summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct26
-rw-r--r--core/config/engine.cpp4
-rw-r--r--core/config/engine.h2
-rw-r--r--core/config/project_settings.cpp37
-rw-r--r--core/config/project_settings.h6
-rw-r--r--core/core_bind.cpp12
-rw-r--r--core/core_bind.h1
-rw-r--r--core/extension/gdextension.cpp54
-rw-r--r--core/extension/gdextension.h7
-rw-r--r--core/extension/gdextension_interface.cpp20
-rw-r--r--core/extension/gdextension_interface.h13
-rw-r--r--core/input/input.cpp3
-rw-r--r--core/io/file_access.cpp74
-rw-r--r--core/io/file_access.h34
-rw-r--r--core/io/file_access_compressed.cpp32
-rw-r--r--core/io/file_access_compressed.h9
-rw-r--r--core/io/file_access_encrypted.cpp41
-rw-r--r--core/io/file_access_encrypted.h9
-rw-r--r--core/io/file_access_memory.h9
-rw-r--r--core/io/file_access_pack.h9
-rw-r--r--core/io/file_access_zip.h9
-rw-r--r--core/io/remote_filesystem_client.cpp2
-rw-r--r--core/io/remote_filesystem_client.h4
-rw-r--r--core/math/delaunay_3d.h13
-rw-r--r--core/string/ustring.cpp19
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--doc/classes/@GlobalScope.xml9
-rw-r--r--doc/classes/AudioStreamRandomizer.xml4
-rw-r--r--doc/classes/BackBufferCopy.xml4
-rw-r--r--doc/classes/BaseMaterial3D.xml6
-rw-r--r--doc/classes/Button.xml2
-rw-r--r--doc/classes/CPUParticles2D.xml2
-rw-r--r--doc/classes/CPUParticles3D.xml2
-rw-r--r--doc/classes/Callable.xml2
-rw-r--r--doc/classes/ColorPickerButton.xml2
-rw-r--r--doc/classes/CompressedCubemap.xml2
-rw-r--r--doc/classes/CompressedCubemapArray.xml2
-rw-r--r--doc/classes/Cubemap.xml2
-rw-r--r--doc/classes/CubemapArray.xml10
-rw-r--r--doc/classes/Curve.xml4
-rw-r--r--doc/classes/CurveTexture.xml4
-rw-r--r--doc/classes/CurveXYZTexture.xml4
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--doc/classes/EditorCommandPalette.xml4
-rw-r--r--doc/classes/EditorExportPlugin.xml2
-rw-r--r--doc/classes/EditorInterface.xml11
-rw-r--r--doc/classes/EditorPlugin.xml13
-rw-r--r--doc/classes/EditorScript.xml3
-rw-r--r--doc/classes/EditorSettings.xml4
-rw-r--r--doc/classes/FileAccess.xml87
-rw-r--r--doc/classes/FogMaterial.xml2
-rw-r--r--doc/classes/FogVolume.xml2
-rw-r--r--doc/classes/Font.xml19
-rw-r--r--doc/classes/FontFile.xml3
-rw-r--r--doc/classes/FontVariation.xml3
-rw-r--r--doc/classes/GPUParticles2D.xml4
-rw-r--r--doc/classes/GPUParticles3D.xml2
-rw-r--r--doc/classes/GPUParticlesAttractor3D.xml4
-rw-r--r--doc/classes/GPUParticlesAttractorBox3D.xml5
-rw-r--r--doc/classes/GPUParticlesAttractorSphere3D.xml5
-rw-r--r--doc/classes/GPUParticlesAttractorVectorField3D.xml5
-rw-r--r--doc/classes/GPUParticlesCollision3D.xml4
-rw-r--r--doc/classes/GPUParticlesCollisionBox3D.xml5
-rw-r--r--doc/classes/GPUParticlesCollisionHeightField3D.xml6
-rw-r--r--doc/classes/GPUParticlesCollisionSDF3D.xml4
-rw-r--r--doc/classes/GPUParticlesCollisionSphere3D.xml5
-rw-r--r--doc/classes/Geometry3D.xml7
-rw-r--r--doc/classes/Gradient.xml8
-rw-r--r--doc/classes/GradientTexture1D.xml6
-rw-r--r--doc/classes/GradientTexture2D.xml4
-rw-r--r--doc/classes/InputEventWithModifiers.xml4
-rw-r--r--doc/classes/Material.xml4
-rw-r--r--doc/classes/MenuButton.xml2
-rw-r--r--doc/classes/NavigationAgent2D.xml2
-rw-r--r--doc/classes/NavigationMeshGenerator.xml2
-rw-r--r--doc/classes/NavigationServer3D.xml9
-rw-r--r--doc/classes/Node.xml8
-rw-r--r--doc/classes/Node3D.xml2
-rw-r--r--doc/classes/NodePath.xml8
-rw-r--r--doc/classes/ORMMaterial3D.xml2
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--doc/classes/OptionButton.xml9
-rw-r--r--doc/classes/PanoramaSkyMaterial.xml4
-rw-r--r--doc/classes/ParticleProcessMaterial.xml6
-rw-r--r--doc/classes/PhysicalSkyMaterial.xml2
-rw-r--r--doc/classes/PlaceholderCubemap.xml10
-rw-r--r--doc/classes/PlaceholderCubemapArray.xml10
-rw-r--r--doc/classes/PopupMenu.xml4
-rw-r--r--doc/classes/ProceduralSkyMaterial.xml8
-rw-r--r--doc/classes/ProjectSettings.xml28
-rw-r--r--doc/classes/RenderSceneBuffersRD.xml2
-rw-r--r--doc/classes/RenderingServer.xml10
-rw-r--r--doc/classes/SceneTree.xml4
-rw-r--r--doc/classes/ShaderGlobalsOverride.xml3
-rw-r--r--doc/classes/ShaderMaterial.xml7
-rw-r--r--doc/classes/Sky.xml4
-rw-r--r--doc/classes/SoftBody3D.xml1
-rw-r--r--doc/classes/StandardMaterial3D.xml2
-rw-r--r--doc/classes/String.xml6
-rw-r--r--doc/classes/StringName.xml6
-rw-r--r--doc/classes/StyleBox.xml2
-rw-r--r--doc/classes/SystemFont.xml3
-rw-r--r--doc/classes/TileMap.xml4
-rw-r--r--doc/classes/TileMapPattern.xml2
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--doc/classes/Tree.xml3
-rw-r--r--doc/classes/Viewport.xml13
-rw-r--r--doc/classes/VisualShader.xml4
-rw-r--r--doc/classes/VisualShaderNode.xml6
-rw-r--r--doc/classes/VisualShaderNodeIf.xml4
-rw-r--r--doc/classes/Window.xml9
-rw-r--r--doc/classes/XMLParser.xml2
-rw-r--r--doc/classes/int.xml2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp20
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--drivers/gles3/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--drivers/gles3/storage/material_storage.cpp10
-rw-r--r--drivers/gles3/storage/texture_storage.h2
-rw-r--r--drivers/unix/file_access_unix.cpp74
-rw-r--r--drivers/unix/file_access_unix.h9
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp58
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h1
-rw-r--r--drivers/vulkan/vulkan_context.cpp40
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp2
-rw-r--r--drivers/windows/file_access_windows.cpp52
-rw-r--r--drivers/windows/file_access_windows.h9
-rw-r--r--editor/connections_dialog.cpp134
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp2
-rw-r--r--editor/directory_create_dialog.cpp40
-rw-r--r--editor/directory_create_dialog.h10
-rw-r--r--editor/editor_inspector.cpp56
-rw-r--r--editor/editor_inspector.h6
-rw-r--r--editor/editor_log.cpp13
-rw-r--r--editor/editor_log.h8
-rw-r--r--editor/editor_node.cpp48
-rw-r--r--editor/editor_property_name_processor.cpp4
-rw-r--r--editor/editor_themes.cpp45
-rw-r--r--editor/export/editor_export.cpp17
-rw-r--r--editor/export/editor_export_platform.h2
-rw-r--r--editor/export/project_export.cpp72
-rw-r--r--editor/export/project_export.h20
-rw-r--r--editor/filesystem_dock.cpp39
-rw-r--r--editor/gui/editor_validation_panel.cpp134
-rw-r--r--editor/gui/editor_validation_panel.h88
-rw-r--r--editor/gui/scene_tree_editor.cpp2
-rw-r--r--editor/icons/Camera2D.svg2
-rw-r--r--editor/icons/Camera3D.svg2
-rw-r--r--editor/icons/CameraAttributes.svg2
-rw-r--r--editor/icons/CameraAttributesPhysical.svg1
-rw-r--r--editor/icons/CameraAttributesPractical.svg1
-rw-r--r--editor/icons/CameraTexture.svg2
-rw-r--r--editor/icons/CompressedTexture2D.svg2
-rw-r--r--editor/icons/CompressedTexture3D.svg1
-rw-r--r--editor/icons/CurveTexture.svg2
-rw-r--r--editor/icons/CurveXYZTexture.svg1
-rw-r--r--editor/icons/GizmoCamera3D.svg1
-rw-r--r--editor/icons/ImageTexture.svg2
-rw-r--r--editor/icons/ImageTexture3D.svg1
-rw-r--r--editor/icons/MaterialPreviewCube.svg2
-rw-r--r--editor/icons/MaterialPreviewCubeOff.svg1
-rw-r--r--editor/icons/MaterialPreviewLight1.svg2
-rw-r--r--editor/icons/MaterialPreviewLight1Off.svg1
-rw-r--r--editor/icons/MaterialPreviewLight2.svg2
-rw-r--r--editor/icons/MaterialPreviewLight2Off.svg1
-rw-r--r--editor/icons/MaterialPreviewSphere.svg2
-rw-r--r--editor/icons/MaterialPreviewSphereOff.svg1
-rw-r--r--editor/icons/NavigationAgent2D.svg2
-rw-r--r--editor/icons/NavigationAgent3D.svg2
-rw-r--r--editor/icons/NavigationObstacle3D.svg2
-rw-r--r--editor/icons/PlaceholderMaterial.svg1
-rw-r--r--editor/icons/PlaceholderMesh.svg1
-rw-r--r--editor/icons/PlaceholderTexture2D.svg1
-rw-r--r--editor/icons/PlaceholderTexture3D.svg1
-rw-r--r--editor/icons/PortableCompressedTexture2D.svg1
-rw-r--r--editor/icons/ProxyTexture.svg1
-rw-r--r--editor/icons/Texture3D.svg1
-rw-r--r--editor/icons/UndoRedo.svg1
-rw-r--r--editor/icons/XRCamera3D.svg2
-rw-r--r--editor/import/resource_importer_layered_texture.cpp11
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/import/resource_importer_scene.cpp23
-rw-r--r--editor/import/resource_importer_scene.h3
-rw-r--r--editor/import/resource_importer_texture.cpp6
-rw-r--r--editor/import/resource_importer_texture.h2
-rw-r--r--editor/import_dock.cpp15
-rw-r--r--editor/import_dock.h1
-rw-r--r--editor/plugin_config_dialog.cpp114
-rw-r--r--editor/plugin_config_dialog.h16
-rw-r--r--editor/plugins/bit_map_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp16
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp1
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp3
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp2
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp4
-rw-r--r--editor/plugins/material_editor_plugin.cpp94
-rw-r--r--editor/plugins/material_editor_plugin.h30
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp47
-rw-r--r--editor/plugins/mesh_editor_plugin.h15
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp273
-rw-r--r--editor/plugins/node_3d_editor_plugin.h11
-rw-r--r--editor/plugins/script_editor_plugin.cpp5
-rw-r--r--editor/plugins/script_editor_plugin.h3
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp13
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp7
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp33
-rw-r--r--editor/plugins/tiles/tile_map_editor.h2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp25
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h4
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp4
-rw-r--r--editor/register_editor_types.cpp1
-rw-r--r--editor/scene_create_dialog.cpp90
-rw-r--r--editor/scene_create_dialog.h14
-rw-r--r--editor/scene_tree_dock.cpp75
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--editor/script_create_dialog.cpp149
-rw-r--r--editor/script_create_dialog.h18
-rw-r--r--editor/shader_create_dialog.cpp104
-rw-r--r--editor/shader_create_dialog.h15
-rw-r--r--main/main.cpp26
-rw-r--r--methods.py3
-rw-r--r--misc/error_suppressions/tsan.txt1
-rw-r--r--misc/extension_api_validation/4.0-stable.expected18
-rwxr-xr-xmisc/scripts/validate_extension_api.sh8
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml3
-rw-r--r--modules/denoise/SCsub9
-rw-r--r--modules/enet/enet_connection.cpp2
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp29
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp145
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h9
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp24
-rw-r--r--modules/gdscript/gdscript_editor.cpp4
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h1
-rw-r--r--modules/gdscript/gdscript_parser.cpp4
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h1
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp18
-rw-r--r--modules/gdscript/language_server/godot_lsp.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/.editorconfig2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd20
-rw-r--r--modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out4
-rw-r--r--modules/glslang/register_types.cpp30
-rw-r--r--modules/gltf/gltf_document.cpp3
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp63
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs6
-rw-r--r--modules/mono/editor/bindings_generator.cpp218
-rw-r--r--modules/mono/editor/bindings_generator.h20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs9
-rw-r--r--modules/mono/godotsharp_dirs.cpp4
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp20
-rw-r--r--modules/navigation/godot_navigation_server.cpp69
-rw-r--r--modules/navigation/godot_navigation_server.h10
-rw-r--r--modules/navigation/nav_mesh_generator_3d.cpp838
-rw-r--r--modules/navigation/nav_mesh_generator_3d.h109
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp720
-rw-r--r--modules/navigation/navigation_mesh_generator.h11
-rw-r--r--modules/navigation/register_types.cpp8
-rw-r--r--modules/noise/doc_classes/FastNoiseLite.xml2
-rw-r--r--modules/noise/doc_classes/Noise.xml2
-rw-r--r--modules/noise/doc_classes/NoiseTexture2D.xml4
-rw-r--r--modules/noise/doc_classes/NoiseTexture3D.xml4
-rw-r--r--modules/noise/icons/NoiseTexture3D.svg1
-rw-r--r--modules/noise/noise_texture_3d.cpp4
-rw-r--r--modules/noise/noise_texture_3d.h2
-rw-r--r--modules/openxr/SCsub5
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp4
-rw-r--r--modules/openxr/openxr_api.cpp4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp10
-rw-r--r--modules/text_server_adv/thorvg_svg_in_ot.cpp15
-rw-r--r--modules/text_server_adv/thorvg_svg_in_ot.h14
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--modules/zip/doc_classes/ZIPReader.xml9
-rw-r--r--modules/zip/zip_reader.cpp16
-rw-r--r--modules/zip/zip_reader.h1
-rw-r--r--platform/android/detect.py4
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml4
-rw-r--r--platform/android/export/export_plugin.cpp19
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/file_access_android.h9
-rw-r--r--platform/android/file_access_filesystem_jandroid.h9
-rw-r--r--platform/android/java/app/AndroidManifest.xml1
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java2
-rw-r--r--platform/ios/detect.py6
-rw-r--r--platform/ios/export/export_plugin.cpp5
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp350
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h30
-rw-r--r--platform/linuxbsd/joypad_linux.cpp24
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp21
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp105
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h7
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h8
-rw-r--r--platform/macos/display_server_macos.h3
-rw-r--r--platform/macos/display_server_macos.mm25
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml2
-rw-r--r--platform/macos/export/export_plugin.cpp9
-rw-r--r--platform/macos/godot_content_view.mm6
-rw-r--r--platform/macos/godot_window_delegate.mm1
-rw-r--r--platform/web/SCsub3
-rw-r--r--platform/web/detect.py11
-rw-r--r--platform/web/export/export_plugin.cpp5
-rw-r--r--platform/web/js/libs/library_godot_audio.js2
-rw-r--r--platform/web/js/libs/library_godot_display.js8
-rw-r--r--platform/windows/detect.py3
-rw-r--r--platform/windows/display_server_windows.cpp9
-rw-r--r--platform/windows/export/export_plugin.cpp4
-rw-r--r--platform/windows/os_windows.cpp2
-rw-r--r--scene/2d/area_2d.cpp2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp6
-rw-r--r--scene/2d/audio_stream_player_2d.h3
-rw-r--r--scene/2d/camera_2d.cpp77
-rw-r--r--scene/2d/camera_2d.h1
-rw-r--r--scene/2d/canvas_modulate.cpp67
-rw-r--r--scene/2d/canvas_modulate.h8
-rw-r--r--scene/2d/tile_map.cpp11
-rw-r--r--scene/3d/area_3d.cpp4
-rw-r--r--scene/3d/area_3d.h5
-rw-r--r--scene/3d/audio_stream_player_3d.cpp3
-rw-r--r--scene/3d/lightmap_gi.cpp2
-rw-r--r--scene/3d/navigation_region_3d.cpp57
-rw-r--r--scene/3d/navigation_region_3d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/tween.cpp7
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/audio/audio_stream_player.h3
-rw-r--r--scene/gui/button.cpp7
-rw-r--r--scene/gui/code_edit.cpp30
-rw-r--r--scene/gui/color_picker.cpp48
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/dialogs.cpp3
-rw-r--r--scene/gui/graph_edit.h8
-rw-r--r--scene/gui/item_list.cpp25
-rw-r--r--scene/gui/menu_bar.cpp2
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp26
-rw-r--r--scene/gui/option_button.h4
-rw-r--r--scene/gui/popup.cpp3
-rw-r--r--scene/gui/popup_menu.compat.inc46
-rw-r--r--scene/gui/popup_menu.cpp28
-rw-r--r--scene/gui/popup_menu.h11
-rw-r--r--scene/gui/rich_text_label.cpp13
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/scroll_bar.cpp8
-rw-r--r--scene/gui/tree.cpp5
-rw-r--r--scene/gui/video_stream_player.cpp2
-rw-r--r--scene/main/node.cpp4
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/scene_tree.cpp3
-rw-r--r--scene/main/viewport.cpp44
-rw-r--r--scene/main/viewport.h12
-rw-r--r--scene/main/window.cpp100
-rw-r--r--scene/main/window.h14
-rw-r--r--scene/resources/default_theme/default_theme.cpp17
-rw-r--r--scene/resources/environment.cpp2
-rw-r--r--scene/resources/font.cpp11
-rw-r--r--scene/resources/font.h1
-rw-r--r--scene/resources/particle_process_material.cpp6
-rw-r--r--scene/resources/primitive_meshes.cpp4
-rw-r--r--scene/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--scu_builders.py23
-rw-r--r--servers/audio/audio_stream.h4
-rw-r--r--servers/audio_server.cpp7
-rw-r--r--servers/navigation_server_3d.cpp4
-rw-r--r--servers/navigation_server_3d.h9
-rw-r--r--servers/navigation_server_3d_dummy.h7
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h2
-rw-r--r--servers/rendering/renderer_rd/effects/bokeh_dof.cpp18
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp22
-rw-r--r--servers/rendering/renderer_rd/effects/luminance.cpp3
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.cpp2
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.cpp32
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.h25
-rw-r--r--servers/rendering/renderer_rd/effects/vrs.cpp3
-rw-r--r--servers/rendering/renderer_rd/environment/fog.cpp2
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp6
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp25
-rw-r--r--servers/rendering/renderer_rd/environment/sky.h3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp19
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp16
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp81
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h5
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp10
-rw-r--r--servers/rendering/renderer_rd/shaders/blit.glsl13
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl7
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl7
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl7
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl54
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/vrs.glsl7
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sky.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl86
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp18
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp174
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h47
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp6
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp93
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h7
-rw-r--r--servers/rendering/renderer_scene_cull.cpp1
-rw-r--r--servers/rendering/renderer_viewport.cpp20
-rw-r--r--servers/rendering/renderer_viewport.h6
-rw-r--r--servers/rendering/rendering_device.cpp6
-rw-r--r--servers/rendering/rendering_device.h1
-rw-r--r--servers/rendering/rendering_server_default.h1
-rw-r--r--servers/rendering/shader_preprocessor.cpp65
-rw-r--r--servers/rendering/shader_preprocessor.h1
-rw-r--r--servers/rendering/storage/texture_storage.h2
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--servers/rendering_server.h1
-rw-r--r--tests/SCsub6
-rw-r--r--tests/core/config/test_project_settings.h60
-rw-r--r--tests/core/io/test_image.h6
-rw-r--r--tests/core/io/test_json.h10
-rw-r--r--tests/core/object/test_class_db.h3
-rw-r--r--tests/core/string/test_string.h11
-rw-r--r--tests/core/string/test_translation_server.h136
-rw-r--r--tests/scene/test_packed_scene.h155
-rw-r--r--tests/scene/test_text_edit.h4
-rw-r--r--tests/scene/test_viewport.h409
-rw-r--r--tests/scene/test_window.h96
-rw-r--r--tests/servers/rendering/test_shader_preprocessor.h333
-rw-r--r--tests/servers/test_navigation_server_3d.h2
-rw-r--r--tests/test_macros.h14
-rw-r--r--tests/test_main.cpp4
-rw-r--r--thirdparty/README.md8
-rw-r--r--thirdparty/libpng/LICENSE4
-rw-r--r--thirdparty/libpng/png.c8
-rw-r--r--thirdparty/libpng/png.h22
-rw-r--r--thirdparty/libpng/pngconf.h2
-rw-r--r--thirdparty/libpng/pngget.c13
-rw-r--r--thirdparty/libpng/pnglibconf.h4
-rw-r--r--thirdparty/libpng/pngpriv.h6
-rw-r--r--thirdparty/libpng/pngset.c60
-rw-r--r--thirdparty/libpng/pngwrite.c10
-rw-r--r--thirdparty/libwebp/AUTHORS2
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv.c1
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv.h2
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c1
-rw-r--r--thirdparty/libwebp/src/dec/tree_dec.c3
-rw-r--r--thirdparty/libwebp/src/dec/vp8_dec.c2
-rw-r--r--thirdparty/libwebp/src/dec/vp8i_dec.h2
-rw-r--r--thirdparty/libwebp/src/dec/webp_dec.c33
-rw-r--r--thirdparty/libwebp/src/demux/demux.c2
-rw-r--r--thirdparty/libwebp/src/dsp/alpha_processing.c1
-rw-r--r--thirdparty/libwebp/src/dsp/cost.c1
-rw-r--r--thirdparty/libwebp/src/dsp/cost_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/cpu.c6
-rw-r--r--thirdparty/libwebp/src/dsp/cpu.h36
-rw-r--r--thirdparty/libwebp/src/dsp/dec.c1
-rw-r--r--thirdparty/libwebp/src/dsp/dec_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/enc.c1
-rw-r--r--thirdparty/libwebp/src/dsp/enc_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/enc_sse2.c242
-rw-r--r--thirdparty/libwebp/src/dsp/filters.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/neon.h4
-rw-r--r--thirdparty/libwebp/src/dsp/quant.h2
-rw-r--r--thirdparty/libwebp/src/dsp/rescaler.c1
-rw-r--r--thirdparty/libwebp/src/dsp/ssim.c1
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling.c1
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/yuv.c1
-rw-r--r--thirdparty/libwebp/src/enc/alpha_enc.c20
-rw-r--r--thirdparty/libwebp/src/enc/analysis_enc.c4
-rw-r--r--thirdparty/libwebp/src/enc/backward_references_enc.c9
-rw-r--r--thirdparty/libwebp/src/enc/frame_enc.c8
-rw-r--r--thirdparty/libwebp/src/enc/picture_csp_enc.c5
-rw-r--r--thirdparty/libwebp/src/enc/picture_rescale_enc.c20
-rw-r--r--thirdparty/libwebp/src/enc/syntax_enc.c6
-rw-r--r--thirdparty/libwebp/src/enc/vp8i_enc.h2
-rw-r--r--thirdparty/libwebp/src/enc/vp8l_enc.c38
-rw-r--r--thirdparty/libwebp/src/enc/webp_enc.c10
-rw-r--r--thirdparty/libwebp/src/mux/muxi.h2
-rw-r--r--thirdparty/libwebp/src/mux/muxread.c7
-rw-r--r--thirdparty/libwebp/src/utils/bit_reader_utils.c3
-rw-r--r--thirdparty/libwebp/src/utils/bit_reader_utils.h3
-rw-r--r--thirdparty/libwebp/src/webp/decode.h5
-rw-r--r--thirdparty/mbedtls/include/mbedtls/aria.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/asn1.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/bignum.h10
-rw-r--r--thirdparty/mbedtls/include/mbedtls/bn_mul.h11
-rw-r--r--thirdparty/mbedtls/include/mbedtls/camellia.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/cipher.h6
-rw-r--r--thirdparty/mbedtls/include/mbedtls/config.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecdsa.h15
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ecp.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/hmac_drbg.h8
-rw-r--r--thirdparty/mbedtls/include/mbedtls/pk.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/platform.h11
-rw-r--r--thirdparty/mbedtls/include/mbedtls/rsa.h12
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl.h11
-rw-r--r--thirdparty/mbedtls/include/mbedtls/version.h8
-rw-r--r--thirdparty/mbedtls/include/mbedtls/x509_crt.h4
-rw-r--r--thirdparty/mbedtls/library/aes.c36
-rw-r--r--thirdparty/mbedtls/library/aesni.c2
-rw-r--r--thirdparty/mbedtls/library/certs.c290
-rw-r--r--thirdparty/mbedtls/library/constant_time.c40
-rw-r--r--thirdparty/mbedtls/library/ctr_drbg.c1
-rw-r--r--thirdparty/mbedtls/library/debug.c14
-rw-r--r--thirdparty/mbedtls/library/ecdh.c2
-rw-r--r--thirdparty/mbedtls/library/ecdsa.c2
-rw-r--r--thirdparty/mbedtls/library/ecp.c10
-rw-r--r--thirdparty/mbedtls/library/ecp_invasive.h4
-rw-r--r--thirdparty/mbedtls/library/entropy.c2
-rw-r--r--thirdparty/mbedtls/library/net_sockets.c7
-rw-r--r--thirdparty/mbedtls/library/pk.c5
-rw-r--r--thirdparty/mbedtls/library/pkparse.c2
-rw-r--r--thirdparty/mbedtls/library/pkwrite.c9
-rw-r--r--thirdparty/mbedtls/library/timing.c22
-rw-r--r--thirdparty/mbedtls/library/x509.c16
-rw-r--r--thirdparty/mbedtls/library/x509_create.c5
-rw-r--r--thirdparty/mbedtls/library/x509_crt.c32
-rw-r--r--thirdparty/mbedtls/patches/windows-arm64-hardclock.diff4
-rw-r--r--thirdparty/miniupnpc/LICENSE2
-rw-r--r--thirdparty/miniupnpc/include/miniupnpc.h4
-rw-r--r--thirdparty/miniupnpc/src/miniupnpcstrings.h2
-rw-r--r--thirdparty/tinyexr/tinyexr.h96
549 files changed, 7695 insertions, 3366 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 582784d78e..f884c690fa 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -302,7 +302,7 @@ License: CC0-1.0
Files: ./thirdparty/miniupnpc/
Comment: MiniUPnP Project
-Copyright: 2005-2022, Thomas Bernard
+Copyright: 2005-2023, Thomas Bernard
License: BSD-3-clause
Files: ./thirdparty/minizip/
diff --git a/SConstruct b/SConstruct
index f82c9c656e..7ea056f839 100644
--- a/SConstruct
+++ b/SConstruct
@@ -193,6 +193,7 @@ opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
+opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "")
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
@@ -220,6 +221,7 @@ opts.Add(
)
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
+opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
# Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@@ -551,7 +553,16 @@ if selected_platform in platform_list:
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
- methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], env_base.dev_build == False))
+ max_includes_per_scu = 8
+ if env_base.dev_build == True:
+ max_includes_per_scu = 1024
+
+ read_scu_limit = int(env["scu_limit"])
+ read_scu_limit = max(0, min(read_scu_limit, 1024))
+ if read_scu_limit != 0:
+ max_includes_per_scu = read_scu_limit
+
+ methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], max_includes_per_scu))
# Must happen after the flags' definition, as configure is when most flags
# are actually handled to change compile options, etc.
@@ -700,6 +711,16 @@ if selected_platform in platform_list:
)
Exit(255)
+ # Disable exception handling. Godot doesn't use exceptions anywhere, and this
+ # saves around 20% of binary size and very significant build time (GH-80513).
+ if env["disable_exceptions"]:
+ if env.msvc:
+ env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
+ else:
+ env.Append(CCFLAGS=["-fno-exceptions"])
+ elif env.msvc:
+ env.Append(CCFLAGS=["/EHsc"])
+
# Configure compiler warnings
if env.msvc: # MSVC
if env["warnings"] == "no":
@@ -729,9 +750,6 @@ if selected_platform in platform_list:
]
)
- # Set exception handling model to avoid warnings caused by Windows system headers.
- env.Append(CCFLAGS=["/EHsc"])
-
if env["werror"]:
env.Append(CCFLAGS=["/WX"])
else: # GCC, Clang
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 7fdea7d1aa..6727c58fd1 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -239,6 +239,10 @@ bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}
+bool Engine::is_generate_spirv_debug_info_enabled() const {
+ return generate_spirv_debug_info;
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
CoreGlobals::print_error_enabled = p_enabled;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 5ea653ba6c..73d40d50ae 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -67,6 +67,7 @@ private:
double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
+ bool generate_spirv_debug_info = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -156,6 +157,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
+ bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
Engine();
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 973162a066..1bfb745662 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -146,30 +146,30 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe
#endif // TOOLS_ENABLED
String ProjectSettings::localize_path(const String &p_path) const {
- if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) {
- return p_path.simplify_path();
+ String path = p_path.simplify_path();
+
+ if (resource_path.is_empty() || (path.is_absolute_path() && !path.begins_with(resource_path))) {
+ return path;
}
// Check if we have a special path (like res://) or a protocol identifier.
- int p = p_path.find("://");
+ int p = path.find("://");
bool found = false;
if (p > 0) {
found = true;
for (int i = 0; i < p; i++) {
- if (!is_ascii_alphanumeric_char(p_path[i])) {
+ if (!is_ascii_alphanumeric_char(path[i])) {
found = false;
break;
}
}
}
if (found) {
- return p_path.simplify_path();
+ return path;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- String path = p_path.replace("\\", "/").simplify_path();
-
if (dir->change_dir(path) == OK) {
String cwd = dir->get_current_dir();
cwd = cwd.replace("\\", "/");
@@ -187,7 +187,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
cwd = cwd.path_join("");
if (!cwd.begins_with(res_path)) {
- return p_path;
+ return path;
}
return cwd.replace_first(res_path, "res://");
@@ -283,6 +283,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
for (int i = 0; i < custom_feature_array.size(); i++) {
custom_features.insert(custom_feature_array[i]);
}
+ _queue_changed();
return true;
}
@@ -324,6 +325,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
}
+ _queue_changed();
return true;
}
@@ -424,6 +426,22 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+void ProjectSettings::_queue_changed() {
+ if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) {
+ return;
+ }
+ is_changed = true;
+ callable_mp(this, &ProjectSettings::_emit_changed).call_deferred();
+}
+
+void ProjectSettings::_emit_changed() {
+ if (!is_changed) {
+ return;
+ }
+ is_changed = false;
+ emit_signal("settings_changed");
+}
+
bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) {
if (PackedData::get_singleton()->is_disabled()) {
return false;
@@ -1225,6 +1243,8 @@ void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0));
ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
+
+ ADD_SIGNAL(MethodInfo("settings_changed"));
}
void ProjectSettings::_add_builtin_input_map() {
@@ -1329,6 +1349,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional");
GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index b89e6694b0..dba4aa6822 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -43,6 +43,9 @@ class TypedArray;
class ProjectSettings : public Object {
GDCLASS(ProjectSettings, Object);
_THREAD_SAFE_CLASS_
+ friend class TestProjectSettingsInternalsAccessor;
+
+ bool is_changed = false;
public:
typedef HashMap<String, Variant> CustomMap;
@@ -114,6 +117,9 @@ protected:
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
+ void _queue_changed();
+ void _emit_changed();
+
static ProjectSettings *singleton;
Error _load_settings_text(const String &p_path);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 4e220d0839..05fe393a2f 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() {
return singleton;
}
+Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) {
+ Vector<Plane> planes_vec;
+ int size = p_planes.size();
+ planes_vec.resize(size);
+ for (int i = 0; i < size; ++i) {
+ planes_vec.set(i, p_planes[i]);
+ }
+ Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size);
+ return ret;
+}
+
TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) {
Variant ret = ::Geometry3D::build_box_planes(p_extents);
return ret;
@@ -1029,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const
}
void Geometry3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points);
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes);
ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z));
ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z));
diff --git a/core/core_bind.h b/core/core_bind.h
index 1cbbcdd251..5f51b64eb7 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -320,6 +320,7 @@ protected:
public:
static Geometry3D *get_singleton();
+ Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes);
TypedArray<Plane> build_box_planes(const Vector3 &p_extents);
TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z);
TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 67b55db3db..1e4cd81034 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -485,6 +485,13 @@ void GDExtension::close_library() {
ERR_FAIL_COND(library == nullptr);
OS::get_singleton()->close_dynamic_library(library);
+#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+ // Delete temporary copy of library if it exists.
+ if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) {
+ DirAccess::remove_absolute(temp_lib_path);
+ }
+#endif
+
library = nullptr;
}
@@ -607,12 +614,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
}
bool compatible = true;
- if (VERSION_MAJOR < compatibility_minimum[0]) {
- compatible = false;
- } else if (VERSION_MINOR < compatibility_minimum[1]) {
- compatible = false;
- } else if (VERSION_PATCH < compatibility_minimum[2]) {
- compatible = false;
+ // Check version lexicographically.
+ if (VERSION_MAJOR != compatibility_minimum[0]) {
+ compatible = VERSION_MAJOR > compatibility_minimum[0];
+ } else if (VERSION_MINOR != compatibility_minimum[1]) {
+ compatible = VERSION_MINOR > compatibility_minimum[1];
+ } else {
+ compatible = VERSION_PATCH >= compatibility_minimum[2];
}
if (!compatible) {
if (r_error) {
@@ -640,6 +648,40 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
Ref<GDExtension> lib;
lib.instantiate();
String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If running on the editor on Windows, we copy the library and open the copy.
+ // This is so the original file isn't locked and can be updated by a compiler.
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (!FileAccess::exists(abs_path)) {
+ if (r_error) {
+ *r_error = ERR_FILE_NOT_FOUND;
+ }
+ ERR_PRINT("GDExtension library not found: " + library_path);
+ return Ref<Resource>();
+ }
+
+ // Copy the file to the same directory as the original with a prefix in the name.
+ // This is so relative path to dependencies are satisfied.
+ String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file());
+
+ Error copy_err = DirAccess::copy_absolute(abs_path, copy_path);
+ if (copy_err) {
+ if (r_error) {
+ *r_error = ERR_CANT_CREATE;
+ }
+ ERR_PRINT("Error copying GDExtension library: " + library_path);
+ return Ref<Resource>();
+ }
+ FileAccess::set_hidden_attribute(copy_path, true);
+
+ // Save the copied path so it can be deleted later.
+ lib->set_temp_library_path(copy_path);
+
+ // Use the copy to open the library.
+ abs_path = copy_path;
+ }
+#endif
err = lib->open_library(abs_path, entry_symbol);
if (r_error) {
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index b935f8706f..5a4dd3d5f5 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -43,6 +43,9 @@ class GDExtension : public Resource {
void *library = nullptr; // pointer if valid,
String library_path;
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ String temp_lib_path;
+#endif
struct Extension {
ObjectGDExtension gdextension;
@@ -76,6 +79,10 @@ public:
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ void set_temp_library_path(const String &p_path) { temp_lib_path = p_path; }
+#endif
+
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 7ef956a470..b4541de8fe 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1048,6 +1048,25 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
+static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) {
+ if (!p_object || !p_language) {
+ return nullptr;
+ }
+
+ const Object *o = (const Object *)p_object;
+ ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance());
+ if (!script_instance_extension) {
+ return nullptr;
+ }
+
+ const ScriptLanguage *language = script_instance_extension->get_language();
+ if (language != p_language) {
+ return nullptr;
+ }
+
+ return script_instance_extension->instance;
+}
+
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
@@ -1216,6 +1235,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
REGISTER_INTERFACE_FUNC(script_instance_create);
+ REGISTER_INTERFACE_FUNC(object_get_script_instance);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 6c05f3988b..cfc21473d6 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -2126,6 +2126,19 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
*/
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+/**
+ * @name object_get_script_instance
+ * @since 4.2
+ *
+ * Get the script instance data attached to this object.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_language A pointer to the language expected for this script instance.
+ *
+ * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create.
+ */
+typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language);
+
/* INTERFACE: ClassDB */
/**
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 4a32abfafa..39f1acf623 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -475,7 +475,8 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S
}
joy_names[p_idx] = js;
- emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected);
+ // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread.
+ call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected);
}
Vector3 Input::get_gravity() const {
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index b669afdc99..6026dbf896 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -583,7 +583,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) {
return mt;
}
-uint32_t FileAccess::get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return 0;
}
@@ -591,11 +591,10 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) {
Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
- uint32_t mt = fa->_get_unix_permissions(p_file);
- return mt;
+ return fa->_get_unix_permissions(p_file);
}
-Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return ERR_UNAVAILABLE;
}
@@ -607,6 +606,52 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi
return err;
}
+bool FileAccess::get_hidden_attribute(const String &p_file) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return false;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ return fa->_get_hidden_attribute(p_file);
+}
+
+Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return ERR_UNAVAILABLE;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ Error err = fa->_set_hidden_attribute(p_file, p_hidden);
+ return err;
+}
+
+bool FileAccess::get_read_only_attribute(const String &p_file) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return false;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ return fa->_get_read_only_attribute(p_file);
+}
+
+Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return ERR_UNAVAILABLE;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ Error err = fa->_set_read_only_attribute(p_file, p_ro);
+ return err;
+}
+
void FileAccess::store_string(const String &p_string) {
if (p_string.length() == 0) {
return;
@@ -865,6 +910,14 @@ void FileAccess::_bind_methods() {
ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists);
ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions);
+
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian");
BIND_ENUM_CONSTANT(READ);
@@ -877,4 +930,17 @@ void FileAccess::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESSION_ZSTD);
BIND_ENUM_CONSTANT(COMPRESSION_GZIP);
BIND_ENUM_CONSTANT(COMPRESSION_BROTLI);
+
+ BIND_BITFIELD_FLAG(UNIX_READ_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_READ_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_READ_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_SET_USER_ID);
+ BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
+ BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
}
diff --git a/core/io/file_access.h b/core/io/file_access.h
index ad1ac665f3..7b9e66bb83 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -60,6 +60,21 @@ public:
WRITE_READ = 7,
};
+ enum UnixPermissionFlags {
+ UNIX_EXECUTE_OTHER = 0x001,
+ UNIX_WRITE_OTHER = 0x002,
+ UNIX_READ_OTHER = 0x004,
+ UNIX_EXECUTE_GROUP = 0x008,
+ UNIX_WRITE_GROUP = 0x010,
+ UNIX_READ_GROUP = 0x020,
+ UNIX_EXECUTE_OWNER = 0x040,
+ UNIX_WRITE_OWNER = 0x080,
+ UNIX_READ_OWNER = 0x100,
+ UNIX_RESTRICTED_DELETE = 0x200,
+ UNIX_SET_GROUP_ID = 0x400,
+ UNIX_SET_USER_ID = 0x800,
+ };
+
enum CompressionMode {
COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
@@ -74,8 +89,13 @@ public:
bool big_endian = false;
bool real_is_double = false;
- virtual uint32_t _get_unix_permissions(const String &p_file) = 0;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0;
+ virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) = 0;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) = 0;
+
+ virtual bool _get_hidden_attribute(const String &p_file) = 0;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) = 0;
+ virtual bool _get_read_only_attribute(const String &p_file) = 0;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) = 0;
protected:
static void _bind_methods();
@@ -185,8 +205,13 @@ public:
static CreateFunc get_create_func(AccessType p_access);
static bool exists(const String &p_name); ///< return true if a file exists
static uint64_t get_modified_time(const String &p_file);
- static uint32_t get_unix_permissions(const String &p_file);
- static Error set_unix_permissions(const String &p_file, uint32_t p_permissions);
+ static BitField<FileAccess::UnixPermissionFlags> get_unix_permissions(const String &p_file);
+ static Error set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions);
+
+ static bool get_hidden_attribute(const String &p_file);
+ static Error set_hidden_attribute(const String &p_file, bool p_hidden);
+ static bool get_read_only_attribute(const String &p_file);
+ static Error set_read_only_attribute(const String &p_file, bool p_ro);
static void set_backup_save(bool p_enable) { backup_save = p_enable; };
static bool is_backup_save_enabled() { return backup_save; };
@@ -212,5 +237,6 @@ public:
VARIANT_ENUM_CAST(FileAccess::CompressionMode);
VARIANT_ENUM_CAST(FileAccess::ModeFlags);
+VARIANT_BITFIELD_CAST(FileAccess::UnixPermissionFlags);
#endif // FILE_ACCESS_H
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 3e5a1217dd..0f00bd292c 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -365,20 +365,48 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) {
}
}
-uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessCompressed::_get_unix_permissions(const String &p_file) {
if (f.is_valid()) {
return f->_get_unix_permissions(p_file);
}
return 0;
}
-Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccessCompressed::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
if (f.is_valid()) {
return f->_set_unix_permissions(p_file, p_permissions);
}
return FAILED;
}
+bool FileAccessCompressed::_get_hidden_attribute(const String &p_file) {
+ if (f.is_valid()) {
+ return f->_get_hidden_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessCompressed::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (f.is_valid()) {
+ return f->_set_hidden_attribute(p_file, p_hidden);
+ }
+ return FAILED;
+}
+
+bool FileAccessCompressed::_get_read_only_attribute(const String &p_file) {
+ if (f.is_valid()) {
+ return f->_get_read_only_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessCompressed::_set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (f.is_valid()) {
+ return f->_set_read_only_attribute(p_file, p_ro);
+ }
+ return FAILED;
+}
+
void FileAccessCompressed::close() {
_close();
}
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index 601b74a9c1..bf57eaa07c 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -94,8 +94,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index c39d19d52b..b689f5b628 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -285,13 +285,46 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) {
return 0;
}
-uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_unix_permissions(p_file);
+ }
return 0;
}
-Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
- ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet.");
- return ERR_UNAVAILABLE;
+Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
+ if (file.is_valid()) {
+ return file->_set_unix_permissions(p_file, p_permissions);
+ }
+ return FAILED;
+}
+
+bool FileAccessEncrypted::_get_hidden_attribute(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_hidden_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessEncrypted::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (file.is_valid()) {
+ return file->_set_hidden_attribute(p_file, p_hidden);
+ }
+ return FAILED;
+}
+
+bool FileAccessEncrypted::_get_read_only_attribute(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_read_only_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessEncrypted::_set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (file.is_valid()) {
+ return file->_set_read_only_attribute(p_file, p_ro);
+ }
+ return FAILED;
}
void FileAccessEncrypted::close() {
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 9702b5a517..489d213b8f 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -85,8 +85,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index 43fe6ab658..ac08e5406f 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -68,8 +68,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override {}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 1538b302c2..97391a5611 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess {
Ref<FileAccess> f;
virtual Error open_internal(const String &p_path, int p_mode_flags) override;
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
public:
virtual bool is_open() const override;
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index f8b640946c..1062a06238 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -106,8 +106,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp
index f22e442a34..1198810441 100644
--- a/core/io/remote_filesystem_client.cpp
+++ b/core/io/remote_filesystem_client.cpp
@@ -270,7 +270,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
String file = temp_file_cache[i].path;
if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) {
- // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway.
+ // File was removed, or server disconnected before transferring it. Since it's no longer valid, remove anyway.
_remove_file(file);
continue;
}
diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h
index 42eba98eb1..fcb5c1cfc3 100644
--- a/core/io/remote_filesystem_client.h
+++ b/core/io/remote_filesystem_client.h
@@ -44,8 +44,8 @@ protected:
String _get_cache_path() { return cache_path; }
struct FileCache {
String path; // Local path (as in "folder/to/file.png")
- uint64_t server_modified_time; // MD5 checksum.
- uint64_t modified_time;
+ uint64_t server_modified_time = 0; // MD5 checksum.
+ uint64_t modified_time = 0;
};
virtual bool _is_configured() { return !cache_path.is_empty(); }
// Can be re-implemented per platform. If so, feel free to ignore get_cache_path()
diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h
index 55923e0133..7df8c37e3c 100644
--- a/core/math/delaunay_3d.h
+++ b/core/math/delaunay_3d.h
@@ -105,8 +105,8 @@ class Delaunay3D {
};
_FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) {
- // the only part in the algorithm where there may be precision errors is this one, so ensure that
- // we do it as maximum precision as possible
+ // The only part in the algorithm where there may be precision errors is this one,
+ // so ensure that we do it with the maximum precision possible.
R128 v0_x = p_points[p_simplex->points[0]].x;
R128 v0_y = p_points[p_simplex->points[0]].y;
@@ -121,7 +121,7 @@ class Delaunay3D {
R128 v3_y = p_points[p_simplex->points[3]].y;
R128 v3_z = p_points[p_simplex->points[3]].z;
- //Create the rows of our "unrolled" 3x3 matrix
+ // Create the rows of our "unrolled" 3x3 matrix.
R128 row1_x = v1_x - v0_x;
R128 row1_y = v1_y - v0_y;
R128 row1_z = v1_z - v0_z;
@@ -138,10 +138,10 @@ class Delaunay3D {
R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z;
R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z;
- //Compute the determinant of said matrix
+ // Compute the determinant of said matrix.
R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z);
- // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula
+ // Compute the volume of the tetrahedron, and precompute a scalar quantity for reuse in the formula.
R128 volume = determinant / R128(6.f);
R128 i12volume = R128(1.f) / (volume * R128(12.f));
@@ -149,8 +149,7 @@ class Delaunay3D {
R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3);
R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3);
- //Once we know the center, the radius is clearly the distance to any vertex
-
+ // Once we know the center, the radius is clearly the distance to any vertex.
R128 rel1_x = center_x - v0_x;
R128 rel1_y = center_y - v0_y;
R128 rel1_z = center_z - v0_z;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 12e6423724..80ca51573c 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1750,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const {
void String::print_unicode_error(const String &p_message, bool p_critical) const {
if (p_critical) {
- print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message));
+ print_error(vformat(U"Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message));
} else {
print_error(vformat("Unicode parsing error: %s", p_message));
}
@@ -3635,6 +3635,23 @@ String String::repeat(int p_count) const {
return new_string;
}
+String String::reverse() const {
+ int len = length();
+ if (len <= 1) {
+ return *this;
+ }
+ String new_string;
+ new_string.resize(len + 1);
+
+ const char32_t *src = ptr();
+ char32_t *dst = new_string.ptrw();
+ for (int i = 0; i < len; i++) {
+ dst[i] = src[len - i - 1];
+ }
+ dst[len] = _null;
+ return new_string;
+}
+
String String::left(int p_len) const {
if (p_len < 0) {
p_len = length() + p_len;
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 295625395d..f45392eee1 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -305,6 +305,7 @@ public:
String replace(const char *p_key, const char *p_with) const;
String replacen(const String &p_key, const String &p_with) const;
String repeat(int p_count) const;
+ String reverse() const;
String insert(int p_at_pos, const String &p_string) const;
String erase(int p_pos, int p_chars = 1) const;
String pad_decimals(int p_digits) const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index dad9183216..ccf9b82022 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1659,6 +1659,7 @@ static void _register_variant_builtin_methods() {
bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray());
bind_string_method(replacen, sarray("what", "forwhat"), varray());
bind_string_method(repeat, sarray("count"), varray());
+ bind_string_method(reverse, sarray(), varray());
bind_string_method(insert, sarray("position", "what"), varray());
bind_string_method(erase, sarray("position", "chars"), varray(1));
bind_string_method(capitalize, sarray(), varray());
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 3b7974f523..e44fdd9776 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -739,8 +739,7 @@
<return type="int" />
<param index="0" name="value" type="int" />
<description>
- Returns the nearest equal or larger power of 2 for the integer [param value].
- In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value &lt;= a[/code] for some non-negative integer [code]n[/code].
+ Returns the smallest integer power of 2 that is greater than or equal to [param value].
[codeblock]
nearest_po2(3) # Returns 4
nearest_po2(4) # Returns 4
@@ -749,7 +748,7 @@
nearest_po2(0) # Returns 0 (this may not be expected)
nearest_po2(-1) # Returns 0 (this may not be expected)
[/codeblock]
- [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for negative values of [param value] (in reality, 1 is the smallest integer power of 2).
+ [b]Warning:[/b] Due to its implementation, this method returns [code]0[/code] rather than [code]1[/code] for values less than or equal to [code]0[/code], with an exception for [param value] being the smallest negative 64-bit integer ([code]-9223372036854775808[/code]) in which case the [param value] is returned unchanged.
</description>
</method>
<method name="pingpong">
@@ -1464,6 +1463,10 @@
<member name="DisplayServer" type="DisplayServer" setter="" getter="">
The [DisplayServer] singleton.
</member>
+ <member name="EditorInterface" type="EditorInterface" setter="" getter="">
+ The [EditorInterface] singleton.
+ [b]Note:[/b] Only available in editor builds.
+ </member>
<member name="Engine" type="Engine" setter="" getter="">
The [Engine] singleton.
</member>
diff --git a/doc/classes/AudioStreamRandomizer.xml b/doc/classes/AudioStreamRandomizer.xml
index 384c316000..12514fe03d 100644
--- a/doc/classes/AudioStreamRandomizer.xml
+++ b/doc/classes/AudioStreamRandomizer.xml
@@ -68,10 +68,10 @@
<member name="playback_mode" type="int" setter="set_playback_mode" getter="get_playback_mode" enum="AudioStreamRandomizer.PlaybackMode" default="0">
Controls how this AudioStreamRandomizer picks which AudioStream to play next.
</member>
- <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.1">
+ <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.0">
The intensity of random pitch variation. A value of 1 means no variation.
</member>
- <member name="random_volume_offset_db" type="float" setter="set_random_volume_offset_db" getter="get_random_volume_offset_db" default="5.0">
+ <member name="random_volume_offset_db" type="float" setter="set_random_volume_offset_db" getter="get_random_volume_offset_db" default="0.0">
The intensity of random volume variation. A value of 0 means no variation.
</member>
<member name="streams_count" type="int" setter="set_streams_count" getter="get_streams_count" default="0">
diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml
index e4e6f1d76d..23d6bc3851 100644
--- a/doc/classes/BackBufferCopy.xml
+++ b/doc/classes/BackBufferCopy.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BackBufferCopy" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]).
+ A node that copies a region of the screen to a buffer for access in shader code.
</brief_description>
<description>
- Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the screen texture in your shader scripts to access the buffer.
+ Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the [member copy_mode]. It can be accessed in shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]).
[b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children.
</description>
<tutorials>
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 949dcc24d0..1cc2976c81 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BaseMaterial3D" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Default 3D rendering material.
+ Abstract base class for defining the 3D rendering properties of meshes.
</brief_description>
<description>
- This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details.
+ This class serves as a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details.
</description>
<tutorials>
- <link title="Standard Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
+ <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
</tutorials>
<methods>
<method name="get_feature" qualifiers="const">
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 6faefcbe0d..25c71deb1f 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -115,7 +115,7 @@
<theme_item name="icon_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Icon modulate [Color] used when the [Button] is being pressed.
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="2">
+ <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index eaacdd590d..051635cb48 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- CPU-based 2D particle emitter.
+ A CPU-based 2D particle emitter.
</brief_description>
<description>
CPU-based 2D particle node used to create a variety of particle systems and effects.
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index f03c463049..bd096dc9c6 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- CPU-based 3D particle emitter.
+ A CPU-based 3D particle emitter.
</brief_description>
<description>
CPU-based 3D particle node used to create a variety of particle systems and effects.
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index c7b2d6df94..b903e98319 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -138,7 +138,7 @@
<method name="get_method" qualifiers="const">
<return type="StringName" />
<description>
- Returns the name of the method represented by this [Callable]. If the callable is a lambda function, returns the function's name.
+ Returns the name of the method represented by this [Callable]. If the callable is a GDScript lambda function, returns the function's name or [code]"&lt;anonymous lambda&gt;"[/code].
</description>
</method>
<method name="get_object" qualifiers="const">
diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index 32ec27868a..f6af188998 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -74,7 +74,7 @@
<theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(0.8, 0.8, 0.8, 1)">
Text [Color] used when the [ColorPickerButton] is being pressed.
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="2">
+ <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between [ColorPickerButton]'s icon and text.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
diff --git a/doc/classes/CompressedCubemap.xml b/doc/classes/CompressedCubemap.xml
index e72d727d7d..6ab0cc5d88 100644
--- a/doc/classes/CompressedCubemap.xml
+++ b/doc/classes/CompressedCubemap.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CompressedCubemap" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 6-sided texture typically used in 3D rendering, optionally compressed.
+ An optionally compressed [Cubemap].
</brief_description>
<description>
A cubemap that is loaded from a [code].ccube[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemap] can use one of 4 compresson methods:
diff --git a/doc/classes/CompressedCubemapArray.xml b/doc/classes/CompressedCubemapArray.xml
index f5829e4e3e..32687229ed 100644
--- a/doc/classes/CompressedCubemapArray.xml
+++ b/doc/classes/CompressedCubemapArray.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CompressedCubemapArray" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Array of 6-sided textures typically used in 3D rendering, optionally compressed.
+ An optionally compressed [CubemapArray].
</brief_description>
<description>
A cubemap array that is loaded from a [code].ccubearray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemapArray] can use one of 4 compresson methods:
diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml
index 0d3b52ddfc..b7da3c4ec6 100644
--- a/doc/classes/Cubemap.xml
+++ b/doc/classes/Cubemap.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Cubemap" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 6-sided texture typically used in 3D rendering.
+ Six square textures representing the faces of a cube. Commonly used as a skybox.
</brief_description>
<description>
A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods.
diff --git a/doc/classes/CubemapArray.xml b/doc/classes/CubemapArray.xml
index ee4ec239f3..7ee497385e 100644
--- a/doc/classes/CubemapArray.xml
+++ b/doc/classes/CubemapArray.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CubemapArray" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A single composite texture resource which consists of multiple [Cubemap]s.
+ An array of [Cubemap]s, stored together and with a single reference.
</brief_description>
<description>
- [CubemapArray]s are made of an array of [Cubemap]s. Accordingly, like [Cubemap]s they are made of multiple textures the amount of which must be divisible by 6 (one image for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray].
- Generally, [CubemapArray]s provide a more efficient way for storing multiple [Cubemap]s compared to storing multiple [Cubemap]s themselves in an array.
- Internally, Godot uses [CubemapArray]s for many effects including the [Sky], if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code].
- To create such a texture file yourself, reimport your image files using the Godot Editor import presets.
+ [CubemapArray]s are made of an array of [Cubemap]s. Like [Cubemap]s, they are made of multiple textures, the amount of which must be divisible by 6 (one for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray].
+ Moreover, [Cubemap]s are allocated in adjacent cache regions on the GPU. This makes [CubemapArray]s the most efficient way to store multiple [Cubemap]s.
+ Internally, Godot uses [CubemapArray]s for many effects, including the [Sky] if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code].
+ To create such a texture file yourself, reimport your image files using the import presets of the File System dock.
[b]Note:[/b] [CubemapArray] is not supported in the OpenGL 3 rendering backend.
</description>
<tutorials>
diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml
index 8f5dc4e945..25b06f1063 100644
--- a/doc/classes/Curve.xml
+++ b/doc/classes/Curve.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Curve" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A mathematic curve.
+ A mathematical curve.
</brief_description>
<description>
- A curve that can be saved and re-used for other objects. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position.
+ This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position.
See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D].
</description>
<tutorials>
diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml
index 4767c18d5a..8cb2384da3 100644
--- a/doc/classes/CurveTexture.xml
+++ b/doc/classes/CurveTexture.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CurveTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A texture that shows a curve.
+ A 1D texture where pixel brightness corresponds to points on a curve.
</brief_description>
<description>
- Renders a given [Curve] provided to it. Simplifies the task of drawing curves and/or saving them as image files.
+ A 1D texture where pixel brightness corresponds to points on a [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files.
If you need to store up to 3 curves within a single texture, use [CurveXYZTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
</description>
<tutorials>
diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml
index e0ab17a35c..8353ed9092 100644
--- a/doc/classes/CurveXYZTexture.xml
+++ b/doc/classes/CurveXYZTexture.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CurveXYZTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A texture that shows 3 different curves (stored on the red, green and blue color channels).
+ A 1D texture where the red, green, and blue color channels correspond to points on 3 curves.
</brief_description>
<description>
- Renders 3 given [Curve]s provided to it, on the red, green and blue channels respectively. Compared to using separate [CurveTexture]s, this further simplifies the task of drawing curves and/or saving them as image files.
+ A 1D texture where the red, green, and blue color channels correspond to points on 3 [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files.
If you only need to store one curve within a single texture, use [CurveTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
</description>
<tutorials>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 465855cc92..c2f81ba109 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -121,7 +121,9 @@
Displays OS native dialog for selecting files or directories in the file system.
Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code].
[b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature.
- [b]Note:[/b] This method is implemented on Windows and macOS.
+ [b]Note:[/b] This method is implemented on Linux, Windows and macOS.
+ [b]Note:[/b] [param current_directory] might be ignored.
+ [b]Note:[/b] On Linux, [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
[b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks.
</description>
diff --git a/doc/classes/EditorCommandPalette.xml b/doc/classes/EditorCommandPalette.xml
index df8bec8002..e6af82b1c3 100644
--- a/doc/classes/EditorCommandPalette.xml
+++ b/doc/classes/EditorCommandPalette.xml
@@ -8,13 +8,13 @@
Command key names use slash delimiters to distinguish sections, for example: [code]"example/command1"[/code] then [code]example[/code] will be the section name.
[codeblocks]
[gdscript]
- var command_palette = get_editor_interface().get_command_palette()
+ var command_palette = EditorInterface.get_command_palette()
# external_command is a function that will be called with the command is executed.
var command_callable = Callable(self, "external_command").bind(arguments)
command_palette.add_command("command", "test/command",command_callable)
[/gdscript]
[csharp]
- EditorCommandPalette commandPalette = GetEditorInterface().GetCommandPalette();
+ EditorCommandPalette commandPalette = EditorInterface.Singleton.GetCommandPalette();
// ExternalCommand is a function that will be called with the command is executed.
Callable commandCallable = new Callable(this, MethodName.ExternalCommand);
commandPalette.AddCommand("command", "test/command", commandCallable)
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 5f9e1e3684..fde6307a13 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -44,7 +44,7 @@
<param index="1" name="path" type="String" />
<description>
Customize a scene. If changes are made to it, return the same or a new scene. Otherwise, return [code]null[/code]. If a new scene is returned, it is up to you to dispose of the old one.
- Implementing this method is required if [method _begin_customize_resources] returns [code]true[/code].
+ Implementing this method is required if [method _begin_customize_scenes] returns [code]true[/code].
</description>
</method>
<method name="_end_customize_resources" qualifiers="virtual">
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index cabd9c0da6..3ae4b7f812 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -5,7 +5,16 @@
</brief_description>
<description>
[EditorInterface] gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, and information about scenes.
- [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorPlugin.get_editor_interface].
+ [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton directly by its name.
+ [codeblocks]
+ [gdscript]
+ var editor_settings = EditorInterface.get_editor_settings()
+ [/gdscript]
+ [csharp]
+ // In C# you can access it via the static Singleton property.
+ EditorSettings settings = EditorInterface.Singleton.GetEditorSettings();
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index ffb4df25d3..8d280b8276 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -245,7 +245,7 @@
# You can use a custom icon:
return preload("res://addons/my_plugin/my_plugin_icon.svg")
# Or use a built-in icon:
- return get_editor_interface().get_base_control().get_theme_icon("Node", "EditorIcons")
+ return EditorInterface.get_base_control().get_theme_icon("Node", "EditorIcons")
[/gdscript]
[csharp]
public override Texture2D _GetPluginIcon()
@@ -253,7 +253,7 @@
// You can use a custom icon:
return ResourceLoader.Load&lt;Texture2D&gt;("res://addons/my_plugin/my_plugin_icon.svg");
// Or use a built-in icon:
- return GetEditorInterface().GetBaseControl().GetThemeIcon("Node", "EditorIcons");
+ return EditorInterface.Singleton.GetBaseControl().GetThemeIcon("Node", "EditorIcons");
}
[/csharp]
[/codeblocks]
@@ -340,7 +340,7 @@
func _enter_tree():
plugin_control = preload("my_plugin_control.tscn").instantiate()
- get_editor_interface().get_editor_main_screen().add_child(plugin_control)
+ EditorInterface.get_editor_main_screen().add_child(plugin_control)
plugin_control.hide()
func _has_main_screen():
@@ -353,7 +353,7 @@
return "My Super Cool Plugin 3000"
func _get_plugin_icon():
- return get_editor_interface().get_base_control().get_theme_icon("Node", "EditorIcons")
+ return EditorInterface.get_base_control().get_theme_icon("Node", "EditorIcons")
[/codeblock]
</description>
</method>
@@ -558,10 +558,11 @@
The callback should have 4 arguments: [Object] [code]undo_redo[/code], [Object] [code]modified_object[/code], [String] [code]property[/code] and [Variant] [code]new_value[/code]. They are, respectively, the [UndoRedo] object used by the inspector, the currently modified object, the name of the modified property and the new value the property is about to take.
</description>
</method>
- <method name="get_editor_interface">
+ <method name="get_editor_interface" is_deprecated="true">
<return type="EditorInterface" />
<description>
- Returns the [EditorInterface] singleton. It provides access to some parts of the editor GUI as well as various inner states and tools.
+ Returns the [EditorInterface] singleton instance.
+ [i]Deprecated.[/i] [EditorInterface] is a global singleton and can be accessed directly by its name.
</description>
</method>
<method name="get_export_as_menu">
diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml
index 01e6b9a52e..8033c18918 100644
--- a/doc/classes/EditorScript.xml
+++ b/doc/classes/EditorScript.xml
@@ -48,10 +48,11 @@
[b]Warning:[/b] The implementation of this method is currently disabled.
</description>
</method>
- <method name="get_editor_interface" qualifiers="const">
+ <method name="get_editor_interface" qualifiers="const" is_deprecated="true">
<return type="EditorInterface" />
<description>
Returns the [EditorInterface] singleton instance.
+ [i]Deprecated.[/i] [EditorInterface] is a global singleton and can be accessed directly by its name.
</description>
</method>
<method name="get_scene" qualifiers="const">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 85efe4362e..5ca89dc03e 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -9,7 +9,7 @@
Accessing the settings can be done using the following methods, such as:
[codeblocks]
[gdscript]
- var settings = get_editor_interface().get_editor_settings()
+ var settings = EditorInterface.get_editor_settings()
# `settings.set("some/property", 10)` also works as this class overrides `_set()` internally.
settings.set_setting("some/property", 10)
# `settings.get("some/property")` also works as this class overrides `_get()` internally.
@@ -17,7 +17,7 @@
var list_of_settings = settings.get_property_list()
[/gdscript]
[csharp]
- EditorSettings settings = GetEditorInterface().GetEditorSettings();
+ EditorSettings settings = EditorInterface.Singleton.GetEditorSettings();
// `settings.set("some/property", value)` also works as this class overrides `_set()` internally.
settings.SetSetting("some/property", Value);
// `settings.get("some/property", value)` also works as this class overrides `_get()` internally.
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 5c1f6775a1..85fbff7932 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -170,6 +170,14 @@
Returns the next 32 bits from the file as a floating-point number.
</description>
</method>
+ <method name="get_hidden_attribute" qualifiers="static">
+ <return type="bool" />
+ <param index="0" name="file" type="String" />
+ <description>
+ Returns [code]true[/code], if file [code]hidden[/code] attribute is set.
+ [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows.
+ </description>
+ </method>
<method name="get_length" qualifiers="const">
<return type="int" />
<description>
@@ -228,6 +236,14 @@
Returns the file cursor's position.
</description>
</method>
+ <method name="get_read_only_attribute" qualifiers="static">
+ <return type="bool" />
+ <param index="0" name="file" type="String" />
+ <description>
+ Returns [code]true[/code], if file [code]read only[/code] attribute is set.
+ [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows.
+ </description>
+ </method>
<method name="get_real" qualifiers="const">
<return type="float" />
<description>
@@ -241,6 +257,14 @@
Returns a SHA-256 [String] representing the file at the given path or an empty [String] on failure.
</description>
</method>
+ <method name="get_unix_permissions" qualifiers="static">
+ <return type="int" enum="FileAccess.UnixPermissionFlags" is_bitfield="true" />
+ <param index="0" name="file" type="String" />
+ <description>
+ Returns file UNIX permissions.
+ [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS.
+ </description>
+ </method>
<method name="get_var" qualifiers="const">
<return type="Variant" />
<param index="0" name="allow_objects" type="bool" default="false" />
@@ -312,6 +336,33 @@
[b]Note:[/b] This is an offset, so you should use negative numbers or the cursor will be at the end of the file.
</description>
</method>
+ <method name="set_hidden_attribute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="file" type="String" />
+ <param index="1" name="hidden" type="bool" />
+ <description>
+ Sets file [code]hidden[/code] attribute.
+ [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows.
+ </description>
+ </method>
+ <method name="set_read_only_attribute" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="file" type="String" />
+ <param index="1" name="ro" type="bool" />
+ <description>
+ Sets file [code]read only[/code] attribute.
+ [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows.
+ </description>
+ </method>
+ <method name="set_unix_permissions" qualifiers="static">
+ <return type="int" enum="Error" />
+ <param index="0" name="file" type="String" />
+ <param index="1" name="permissions" type="int" enum="FileAccess.UnixPermissionFlags" is_bitfield="true" />
+ <description>
+ Sets file UNIX permissions.
+ [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS.
+ </description>
+ </method>
<method name="store_8">
<return type="void" />
<param index="0" name="value" type="int" />
@@ -485,5 +536,41 @@
<constant name="COMPRESSION_BROTLI" value="4" enum="CompressionMode">
Uses the [url=https://github.com/google/brotli]brotli[/url] compression method (only decompression is supported).
</constant>
+ <constant name="UNIX_READ_OWNER" value="256" enum="UnixPermissionFlags" is_bitfield="true">
+ Read for owner bit.
+ </constant>
+ <constant name="UNIX_WRITE_OWNER" value="128" enum="UnixPermissionFlags" is_bitfield="true">
+ Write for owner bit.
+ </constant>
+ <constant name="UNIX_EXECUTE_OWNER" value="64" enum="UnixPermissionFlags" is_bitfield="true">
+ Execute for owner bit.
+ </constant>
+ <constant name="UNIX_READ_GROUP" value="32" enum="UnixPermissionFlags" is_bitfield="true">
+ Read for group bit.
+ </constant>
+ <constant name="UNIX_WRITE_GROUP" value="16" enum="UnixPermissionFlags" is_bitfield="true">
+ Write for group bit.
+ </constant>
+ <constant name="UNIX_EXECUTE_GROUP" value="8" enum="UnixPermissionFlags" is_bitfield="true">
+ Execute for group bit.
+ </constant>
+ <constant name="UNIX_READ_OTHER" value="4" enum="UnixPermissionFlags" is_bitfield="true">
+ Read for other bit.
+ </constant>
+ <constant name="UNIX_WRITE_OTHER" value="2" enum="UnixPermissionFlags" is_bitfield="true">
+ Write for other bit.
+ </constant>
+ <constant name="UNIX_EXECUTE_OTHER" value="1" enum="UnixPermissionFlags" is_bitfield="true">
+ Execute for other bit.
+ </constant>
+ <constant name="UNIX_SET_USER_ID" value="2048" enum="UnixPermissionFlags" is_bitfield="true">
+ Set user id on execution bit.
+ </constant>
+ <constant name="UNIX_SET_GROUP_ID" value="1024" enum="UnixPermissionFlags" is_bitfield="true">
+ Set group id on execution bit.
+ </constant>
+ <constant name="UNIX_RESTRICTED_DELETE" value="512" enum="UnixPermissionFlags" is_bitfield="true">
+ Restricted deletion (sticky) bit.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/FogMaterial.xml b/doc/classes/FogMaterial.xml
index 13366f1813..a6acaaef00 100644
--- a/doc/classes/FogMaterial.xml
+++ b/doc/classes/FogMaterial.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FogMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- [Material] used with a [FogVolume] to draw things with the volumetric fog effect.
+ A material that controls how volumetric fog is rendered, to be assigned to a [FogVolume].
</brief_description>
<description>
A [Material] resource that can be used by [FogVolume]s to draw volumetric effects.
diff --git a/doc/classes/FogVolume.xml b/doc/classes/FogVolume.xml
index 66aeb580c2..538411016e 100644
--- a/doc/classes/FogVolume.xml
+++ b/doc/classes/FogVolume.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FogVolume" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A node used to add local fog with the volumetric fog effect.
+ A region that contributes to the default volumetric fog from the world environment.
</brief_description>
<description>
[FogVolume]s are used to add localized fog into the global volumetric fog effect. [FogVolume]s can also remove volumetric fog from specific areas if using a [FogMaterial] with a negative [member FogMaterial.density].
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index 461e96def4..e87712825d 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -149,12 +149,6 @@
Returns number of faces in the TrueType / OpenType collection.
</description>
</method>
- <method name="get_fallbacks" qualifiers="const">
- <return type="Font[]" />
- <description>
- Returns array of fallback [Font]s.
- </description>
- </method>
<method name="get_font_name" qualifiers="const">
<return type="String" />
<description>
@@ -335,12 +329,11 @@
Sets LRU cache capacity for [code]draw_*[/code] methods.
</description>
</method>
- <method name="set_fallbacks">
- <return type="void" />
- <param index="0" name="fallbacks" type="Font[]" />
- <description>
- Sets array of fallback [Font]s.
- </description>
- </method>
</methods>
+ <members>
+ <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
+ Array of fallback [Font]s to use as a substitute if a glyph is not found in this current [Font].
+ If this array is empty in a [FontVariation], the [member FontVariation.base_font]'s fallbacks are used instead.
+ </member>
+ </members>
</class>
diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml
index ba4761e900..86d1cb552a 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -568,9 +568,6 @@
<member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
Contents of the dynamic font source file.
</member>
- <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
- Array of fallback [Font]s.
- </member>
<member name="fixed_size" type="int" setter="set_fixed_size" getter="get_fixed_size" default="0">
Font size, used only for the bitmap fonts.
</member>
diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml
index eb72b4020c..f02fda31b9 100644
--- a/doc/classes/FontVariation.xml
+++ b/doc/classes/FontVariation.xml
@@ -46,9 +46,6 @@
<member name="base_font" type="Font" setter="set_base_font" getter="get_base_font">
Base font used to create a variation. If not set, default [Theme] font is used.
</member>
- <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
- Array of fallback [Font]s to use as a substitute if a glyph is not found in this [FontVariation]. If not set, [member base_font]'s fallbacks are used instead.
- </member>
<member name="opentype_features" type="Dictionary" setter="set_opentype_features" getter="get_opentype_features" default="{}">
A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url].
</member>
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index 2928215c3e..d5a4c146e0 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 2D particle emitter.
+ A 2D particle emitter.
</brief_description>
<description>
2D particle node used to create a variety of particle systems and effects. [GPUParticles2D] features an emitter that generates some number of particles at a given rate.
Use the [member process_material] property to add a [ParticleProcessMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles.
- 2D particles can optionally collide with [LightOccluder2D] nodes (note: they don't collide with [PhysicsBody2D] nodes).
+ 2D particles can optionally collide with [LightOccluder2D], but they don't collide with [PhysicsBody2D] nodes.
</description>
<tutorials>
<link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link>
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 4c0b2d12ed..31f1f9e66e 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 3D particle emitter.
+ A 3D particle emitter.
</brief_description>
<description>
3D particle node used to create a variety of particle systems and effects. [GPUParticles3D] features an emitter that generates some number of particles at a given rate.
diff --git a/doc/classes/GPUParticlesAttractor3D.xml b/doc/classes/GPUParticlesAttractor3D.xml
index b3ed3b4701..0d219e4bc9 100644
--- a/doc/classes/GPUParticlesAttractor3D.xml
+++ b/doc/classes/GPUParticlesAttractor3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractor3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract class for 3D particle attractors affecting [GPUParticles3D] nodes.
+ Abstract base class for 3D particle attractors.
</brief_description>
<description>
Particle attractors can be used to attract particles towards the attractor's origin, or to push them away from the attractor's origin.
@@ -25,7 +25,7 @@
[b]Note:[/b] If [member directionality] is greater than [code]0.0[/code], the direction in which particles are pushed can be changed by rotating the [GPUParticlesAttractor3D] node.
</member>
<member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0">
- If [member strength] is negative, particles will be pushed in the reverse direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code].
+ Adjusts the strength of the attractor. If [member strength] is negative, particles will be pushed in the opposite direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code].
</member>
</members>
</class>
diff --git a/doc/classes/GPUParticlesAttractorBox3D.xml b/doc/classes/GPUParticlesAttractorBox3D.xml
index d43efc73bf..410d504428 100644
--- a/doc/classes/GPUParticlesAttractorBox3D.xml
+++ b/doc/classes/GPUParticlesAttractorBox3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorBox3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A box-shaped attractor that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A box-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesAttractorSphere3D.xml b/doc/classes/GPUParticlesAttractorSphere3D.xml
index 05c1e60d9a..599d7e4e2a 100644
--- a/doc/classes/GPUParticlesAttractorSphere3D.xml
+++ b/doc/classes/GPUParticlesAttractorSphere3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorSphere3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesAttractorVectorField3D.xml b/doc/classes/GPUParticlesAttractorVectorField3D.xml
index ef9a11e57c..7cb5524a1b 100644
--- a/doc/classes/GPUParticlesAttractorVectorField3D.xml
+++ b/doc/classes/GPUParticlesAttractorVectorField3D.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorVectorField3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes.
+ A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes.
+ A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes.
Unlike [GPUParticlesAttractorBox3D], [GPUParticlesAttractorVectorField3D] uses a [member texture] to affect attraction strength within the box. This can be used to create complex attraction scenarios where particles travel in different directions depending on their location. This can be useful for weather effects such as sandstorms.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesCollision3D.xml b/doc/classes/GPUParticlesCollision3D.xml
index 9471a7caf2..089747b7ee 100644
--- a/doc/classes/GPUParticlesCollision3D.xml
+++ b/doc/classes/GPUParticlesCollision3D.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollision3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract class for 3D particle collision shapes affecting [GPUParticles3D] nodes.
+ Abstract base class for 3D particle collision shapes affecting [GPUParticles3D] nodes.
</brief_description>
<description>
Particle collision shapes can be used to make particles stop or bounce against them.
- Particle collision shapes in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
Particle collision shapes can be temporarily disabled by hiding them.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
diff --git a/doc/classes/GPUParticlesCollisionBox3D.xml b/doc/classes/GPUParticlesCollisionBox3D.xml
index 4bb947a82d..fc225e460f 100644
--- a/doc/classes/GPUParticlesCollisionBox3D.xml
+++ b/doc/classes/GPUParticlesCollisionBox3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionBox3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml
index 58219aa0b3..0b9a94af63 100644
--- a/doc/classes/GPUParticlesCollisionHeightField3D.xml
+++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionHeightField3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
Heightmap shapes allow for efficiently representing collisions for convex and concave objects with a single "floor" (such as terrain). This is less flexible than [GPUParticlesCollisionSDF3D], but it doesn't require a baking step.
- [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, since heightmaps cannot represent overhangs, [GPUParticlesCollisionHeightField3D] is not suited for indoor particle collision.
+ [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, this class is limited since heightmaps cannot represent overhangs (e.g. indoors or caves).
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [code]true[/code] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/GPUParticlesCollisionSDF3D.xml b/doc/classes/GPUParticlesCollisionSDF3D.xml
index b61be49619..11978e0737 100644
--- a/doc/classes/GPUParticlesCollisionSDF3D.xml
+++ b/doc/classes/GPUParticlesCollisionSDF3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionSDF3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes.
+ A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes.
+ A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes.
Signed distance fields (SDF) allow for efficiently representing approximate collision shapes for convex and concave objects of any shape. This is more flexible than [GPUParticlesCollisionHeightField3D], but it requires a baking step.
[b]Baking:[/b] The signed distance field texture can be baked by selecting the [GPUParticlesCollisionSDF3D] node in the editor, then clicking [b]Bake SDF[/b] at the top of the 3D viewport. Any [i]visible[/i] [MeshInstance3D]s within the [member size] will be taken into account for baking, regardless of their [member GeometryInstance3D.gi_mode].
[b]Note:[/b] Baking a [GPUParticlesCollisionSDF3D]'s [member texture] is only possible within the editor, as there is no bake method exposed for use in exported projects. However, it's still possible to load pre-baked [Texture3D]s into its [member texture] property in an exported project.
diff --git a/doc/classes/GPUParticlesCollisionSphere3D.xml b/doc/classes/GPUParticlesCollisionSphere3D.xml
index cade0eaeef..fe2fe3d414 100644
--- a/doc/classes/GPUParticlesCollisionSphere3D.xml
+++ b/doc/classes/GPUParticlesCollisionSphere3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionSphere3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml
index 85b8965faf..a85d17d925 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -45,6 +45,13 @@
Clips the polygon defined by the points in [param points] against the [param plane] and returns the points of the clipped polygon.
</description>
</method>
+ <method name="compute_convex_mesh_points">
+ <return type="PackedVector3Array" />
+ <param index="0" name="planes" type="Plane[]" />
+ <description>
+ Calculates and returns all the vertex points of a convex shape defined by an array of [param planes].
+ </description>
+ </method>
<method name="get_closest_point_to_segment">
<return type="Vector3" />
<param index="0" name="point" type="Vector3" />
diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml
index f2b894d612..c7fa1f40e0 100644
--- a/doc/classes/Gradient.xml
+++ b/doc/classes/Gradient.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Gradient" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A color interpolator resource which can be used to generate colors between user-defined color points.
+ A color transition.
</brief_description>
<description>
- Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the gradient will interpolate from color 1 to color 2 and from color 2 to color 3. The gradient will initially have 2 colors (black and white), one (black) at gradient lower offset 0 and the other (white) at the gradient higher offset 1.
+ This resource describes a color transition by defining a set of colored points and how to interpolate between them.
See also [Curve] which supports more complex easing methods, but does not support colors.
</description>
<tutorials>
@@ -15,7 +15,7 @@
<param index="0" name="offset" type="float" />
<param index="1" name="color" type="Color" />
<description>
- Adds the specified color to the end of the gradient, with the specified offset.
+ Adds the specified color to the gradient, with the specified offset.
</description>
</method>
<method name="get_color">
@@ -42,7 +42,7 @@
<return type="void" />
<param index="0" name="point" type="int" />
<description>
- Removes the color at the index [param point].
+ Removes the color at index [param point].
</description>
</method>
<method name="reverse">
diff --git a/doc/classes/GradientTexture1D.xml b/doc/classes/GradientTexture1D.xml
index 7207de57fd..4e6b76c0a6 100644
--- a/doc/classes/GradientTexture1D.xml
+++ b/doc/classes/GradientTexture1D.xml
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GradientTexture1D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Gradient-filled texture.
+ A 1D texture that uses colors obtained from a [Gradient].
</brief_description>
<description>
- GradientTexture1D uses a [Gradient] to fill the texture data. The gradient will be filled from left to right using colors obtained from the gradient. This means the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width]). See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture].
+ A 1D texture that obtains colors from a [Gradient] to fill the texture data. The texture is filled by sampling the gradient for each pixel. Therefore, the texture does not necessarily represent an exact copy of the gradient, as it may miss some colors if there are not enough pixels. See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture].
</description>
<tutorials>
</tutorials>
<members>
<member name="gradient" type="Gradient" setter="set_gradient" getter="get_gradient">
- The [Gradient] that will be used to fill the texture.
+ The [Gradient] used to fill the texture.
</member>
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
<member name="use_hdr" type="bool" setter="set_use_hdr" getter="is_using_hdr" default="false">
diff --git a/doc/classes/GradientTexture2D.xml b/doc/classes/GradientTexture2D.xml
index 63e511173a..f6588ef791 100644
--- a/doc/classes/GradientTexture2D.xml
+++ b/doc/classes/GradientTexture2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GradientTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Gradient-filled 2D texture.
+ A 2D texture that creates a pattern with colors obtained from a [Gradient].
</brief_description>
<description>
- The texture uses a [Gradient] to fill the texture data in 2D space. The gradient is filled according to the specified [member fill] and [member repeat] types using colors obtained from the gradient. The texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture].
+ A 2D texture that obtains colors from a [Gradient] to fill the texture data. This texture is able to transform a color transition into different patterns such as a linear or a radial gradient. The gradient is sampled individually for each pixel so it does not necessarily represent an exact copy of the gradient(see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml
index 8bb3850dc2..5c018b0c56 100644
--- a/doc/classes/InputEventWithModifiers.xml
+++ b/doc/classes/InputEventWithModifiers.xml
@@ -19,7 +19,7 @@
<method name="is_command_or_control_pressed" qualifiers="const">
<return type="bool" />
<description>
- On macOS, returns [code]true[/code] if [kbd]Meta[/kbd] ([kbd]Command[/kbd]) is pressed.
+ On macOS, returns [code]true[/code] if [kbd]Meta[/kbd] ([kbd]Cmd[/kbd]) is pressed.
On other platforms, returns [code]true[/code] if [kbd]Ctrl[/kbd] is pressed.
</description>
</method>
@@ -29,7 +29,7 @@
State of the [kbd]Alt[/kbd] modifier.
</member>
<member name="command_or_control_autoremap" type="bool" setter="set_command_or_control_autoremap" getter="is_command_or_control_autoremap" default="false">
- Automatically use [kbd]Meta[/kbd] ([kbd]Command[/kbd]) on macOS and [kbd]Ctrl[/kbd] on other platforms. If [code]true[/code], [member ctrl_pressed] and [member meta_pressed] cannot be set.
+ Automatically use [kbd]Meta[/kbd] ([kbd]Cmd[/kbd]) on macOS and [kbd]Ctrl[/kbd] on other platforms. If [code]true[/code], [member ctrl_pressed] and [member meta_pressed] cannot be set.
</member>
<member name="ctrl_pressed" type="bool" setter="set_ctrl_pressed" getter="is_ctrl_pressed" default="false">
State of the [kbd]Ctrl[/kbd] modifier.
diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml
index 505a1465bb..1118461445 100644
--- a/doc/classes/Material.xml
+++ b/doc/classes/Material.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Material" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract base [Resource] for coloring and shading geometry.
+ Abstract base class for applying visual properties to an object, such as color and roughness.
</brief_description>
<description>
- Material is a base [Resource] used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a Material. A few flags and parameters are shared between all material types and are configured here.
+ [Material] is a base resource used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a [Material]. A few flags and parameters are shared between all material types and are configured here.
</description>
<tutorials>
<link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link>
diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml
index 8af233427c..e3a707ba72 100644
--- a/doc/classes/MenuButton.xml
+++ b/doc/classes/MenuButton.xml
@@ -69,7 +69,7 @@
<theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Text [Color] used when the [MenuButton] is being pressed.
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="3">
+ <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between [MenuButton]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index c1570f3149..71f8c50667 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -180,7 +180,7 @@
The distance to search for other agents.
</member>
<member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="20.0">
- The distance threshold before a path point is considered to be reached. This allows agents to not have to hit a path point on the path exactly, but only to reach its general area. If this value is set too high, the NavigationAgent will skip points on the path, which can lead too leaving the navigation mesh. If this value is set too low, the NavigationAgent will be stuck in a repath loop because it will constantly overshoot or undershoot the distance to the next point on each physics frame update.
+ The distance threshold before a path point is considered to be reached. This allows agents to not have to hit a path point on the path exactly, but only to reach its general area. If this value is set too high, the NavigationAgent will skip points on the path, which can lead to leaving the navigation mesh. If this value is set too low, the NavigationAgent will be stuck in a repath loop because it will constantly overshoot or undershoot the distance to the next point on each physics frame update.
</member>
<member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="100.0">
The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml
index dc9b7dc3b2..0997354aff 100644
--- a/doc/classes/NavigationMeshGenerator.xml
+++ b/doc/classes/NavigationMeshGenerator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NavigationMeshGenerator" inherits="Object" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="NavigationMeshGenerator" inherits="Object" is_deprecated="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Helper class for creating and clearing navigation meshes.
</brief_description>
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 86c605b8a3..b56b86f435 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -216,6 +216,15 @@
Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
</description>
</method>
+ <method name="bake_from_source_geometry_data_async">
+ <return type="void" />
+ <param index="0" name="navigation_mesh" type="NavigationMesh" />
+ <param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData3D" />
+ <param index="2" name="callback" type="Callable" default="Callable()" />
+ <description>
+ Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
+ </description>
+ </method>
<method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 49ab3918bb..7510d6bb64 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -1022,12 +1022,12 @@
Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects.
</constant>
<constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
- Notification received from the OS when the mouse enters the game window.
- Implemented on desktop and web platforms.
+ Notification received when the mouse enters the window.
+ Implemented for embedded windows and on desktop and web platforms.
</constant>
<constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003">
- Notification received from the OS when the mouse leaves the game window.
- Implemented on desktop and web platforms.
+ Notification received when the mouse leaves the window.
+ Implemented for embedded windows and on desktop and web platforms.
</constant>
<constant name="NOTIFICATION_WM_WINDOW_FOCUS_IN" value="1004">
Notification received when the node's parent [Window] is focused. This may be a change of focus between two windows of the same engine instance, or from the OS desktop or a third-party application to a window of the game (in which case [constant NOTIFICATION_APPLICATION_FOCUS_IN] is also emitted).
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index b5ead07a10..2606a2b320 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -269,7 +269,7 @@
</methods>
<members>
<member name="basis" type="Basis" setter="set_basis" getter="get_basis">
- Direct access to the 3x3 basis of the [Transform3D] property.
+ Direct access to the 3x3 basis of the [member transform] property.
</member>
<member name="global_position" type="Vector3" setter="set_global_position" getter="get_global_position">
Global position of this node. This is equivalent to [code]global_transform.origin[/code].
diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml
index 77a5cfa6c6..a56e80ec1f 100644
--- a/doc/classes/NodePath.xml
+++ b/doc/classes/NodePath.xml
@@ -101,12 +101,12 @@
Returns all subnames concatenated with a colon character ([code]:[/code]) as separator, i.e. the right side of the first colon in a node path.
[codeblocks]
[gdscript]
- var nodepath = NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path")
- print(nodepath.get_concatenated_subnames()) # texture:load_path
+ var node_path = NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path")
+ print(node_path.get_concatenated_subnames()) # texture:load_path
[/gdscript]
[csharp]
- var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
- GD.Print(nodepath.GetConcatenatedSubnames()); // texture:load_path
+ var nodePath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
+ GD.Print(nodePath.GetConcatenatedSubnames()); // texture:load_path
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/ORMMaterial3D.xml b/doc/classes/ORMMaterial3D.xml
index d134107618..72620573da 100644
--- a/doc/classes/ORMMaterial3D.xml
+++ b/doc/classes/ORMMaterial3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ORMMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Physically based rendering (PBR) material that can be applied to 3D objects, can use an ORM texture.
+ A PBR (Physically Based Rendering) material to be used on 3D objects. Uses an ORM texture.
</brief_description>
<description>
ORMMaterial3D's properties are inherited from [BaseMaterial3D]. Unlike [StandardMaterial3D], ORMMaterial3D uses a single texture for ambient occlusion, roughness and metallic maps, known as an ORM texture.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 03169d390a..d4a6b57c58 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -538,7 +538,7 @@
<return type="bool" />
<description>
Returns [code]true[/code] if application is running in the sandbox.
- [b]Note:[/b] This method is implemented on macOS.
+ [b]Note:[/b] This method is implemented on macOS and Linux.
</description>
</method>
<method name="is_stdout_verbose" qualifiers="const">
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 659f08d49a..d2302fc27f 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -148,6 +148,13 @@
Passing [code]-1[/code] as the index deselects any currently selected item.
</description>
</method>
+ <method name="set_disable_shortcuts">
+ <return type="void" />
+ <param index="0" name="disabled" type="bool" />
+ <description>
+ If [code]true[/code], shortcuts are disabled and cannot be used to trigger the button.
+ </description>
+ </method>
<method name="set_item_disabled">
<return type="void" />
<param index="0" name="idx" type="int" />
@@ -262,7 +269,7 @@
<theme_item name="arrow_margin" data_type="constant" type="int" default="4">
The horizontal space between the arrow icon and the right edge of the button.
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="2">
+ <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between [OptionButton]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item>
<theme_item name="modulate_arrow" data_type="constant" type="int" default="0">
diff --git a/doc/classes/PanoramaSkyMaterial.xml b/doc/classes/PanoramaSkyMaterial.xml
index ab32ca48dd..0d041b9b90 100644
--- a/doc/classes/PanoramaSkyMaterial.xml
+++ b/doc/classes/PanoramaSkyMaterial.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PanoramaSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A [Material] used with [Sky] to draw a background texture.
+ A material that provides a special texture to a [Sky], usually an HDR panorama.
</brief_description>
<description>
- A resource referenced in a [Sky] that is used to draw a background. The Panorama sky material functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a cubemap.
+ A resource referenced in a [Sky] that is used to draw a background. [PanoramaSkyMaterial] functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a [Cubemap].
Using an HDR panorama is strongly recommended for accurate, high-quality reflections. Godot supports the Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) image formats for this purpose.
You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map.
</description>
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml
index 69d3351733..355fec3713 100644
--- a/doc/classes/ParticleProcessMaterial.xml
+++ b/doc/classes/ParticleProcessMaterial.xml
@@ -1,12 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ParticleProcessMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Particle properties for [GPUParticles3D] and [GPUParticles2D] nodes.
+ Holds a particle configuration for [GPUParticles2D] or [GPUParticles3D] nodes.
</brief_description>
<description>
- ParticleProcessMaterial defines particle properties and behavior. It is used in the [code]process_material[/code] of [GPUParticles3D] and [GPUParticles2D] emitter nodes.
- Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] applied to vary values over the lifetime of the particle.
- Particle animation is available only in [GPUParticles2D]. To use it, attach a [CanvasItemMaterial], with [member CanvasItemMaterial.particles_animation] enabled, to the particles node.
+ [ParticleProcessMaterial] defines particle properties and behavior. It is used in the [code]process_material[/code] of the [GPUParticles2D] and [GPUParticles3D] nodes. Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] or a [GradientTexture1D] applied to vary numerical or color values over the lifetime of the particle.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml
index e6a9980e19..13f9d93e66 100644
--- a/doc/classes/PhysicalSkyMaterial.xml
+++ b/doc/classes/PhysicalSkyMaterial.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- [Sky] [Material] used for a physically based sky.
+ A material that defines a sky for a [Sky] resource by a set of physical properties.
</brief_description>
<description>
The [PhysicalSkyMaterial] uses the Preetham analytic daylight model to draw a sky based on physical properties. This results in a substantially more realistic sky than the [ProceduralSkyMaterial], but it is slightly slower and less flexible.
diff --git a/doc/classes/PlaceholderCubemap.xml b/doc/classes/PlaceholderCubemap.xml
index b760bf359b..9cd0c26926 100644
--- a/doc/classes/PlaceholderCubemap.xml
+++ b/doc/classes/PlaceholderCubemap.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PlaceholderCubemap" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Placeholder class for a cubemap texture.
+ A [Cubemap] without image data.
</brief_description>
<description>
- This class is used when loading a project that uses a [Cubemap] subclass in 2 conditions:
- - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly.
- - When this subclass is missing due to using a different engine version or build (e.g. modules disabled).
- [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV).
+ This class replaces a [Cubemap] or a [Cubemap]-derived class in 2 conditions:
+ - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly.
+ - When the [Cubemap]-derived class is missing, for example when using a different engine version.
+ [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PlaceholderCubemapArray.xml b/doc/classes/PlaceholderCubemapArray.xml
index 1074824c0f..9ffbc316a8 100644
--- a/doc/classes/PlaceholderCubemapArray.xml
+++ b/doc/classes/PlaceholderCubemapArray.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PlaceholderCubemapArray" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Placeholder class for a cubemap texture array.
+ A [CubemapArray] without image data.
</brief_description>
<description>
- This class is used when loading a project that uses a [CubemapArray] subclass in 2 conditions:
- - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly.
- - When this subclass is missing due to using a different engine version or build (e.g. modules disabled).
- [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV).
+ This class replaces a [CubemapArray] or a [CubemapArray]-derived class in 2 conditions:
+ - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly.
+ - When the [CubemapArray]-derived class is missing, for example when using a different engine version.
+ [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index daf3163842..b72e65e63a 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -96,9 +96,11 @@
<param index="1" name="shortcut" type="Shortcut" />
<param index="2" name="id" type="int" default="-1" />
<param index="3" name="global" type="bool" default="false" />
+ <param index="4" name="allow_echo" type="bool" default="false" />
<description>
Adds a new item and assigns the specified [Shortcut] and icon [param texture] to it. Sets the label of the checkbox to the [Shortcut]'s name.
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
+ If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events.
</description>
</method>
<method name="add_item">
@@ -161,9 +163,11 @@
<param index="0" name="shortcut" type="Shortcut" />
<param index="1" name="id" type="int" default="-1" />
<param index="2" name="global" type="bool" default="false" />
+ <param index="3" name="allow_echo" type="bool" default="false" />
<description>
Adds a [Shortcut].
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
+ If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events.
</description>
</method>
<method name="add_submenu_item">
diff --git a/doc/classes/ProceduralSkyMaterial.xml b/doc/classes/ProceduralSkyMaterial.xml
index e3a1177cfa..54bffd009f 100644
--- a/doc/classes/ProceduralSkyMaterial.xml
+++ b/doc/classes/ProceduralSkyMaterial.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ProceduralSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A [Material] used with [Sky] to generate a background based on user input parameters.
+ A material that defines a simple sky for a [Sky] resource.
</brief_description>
<description>
- ProceduralSkyMaterial provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are very similar, they are defined by a color at the horizon, another color, and finally an easing curve to interpolate between these two colors. Similarly, the sun is described by a position in the sky, a color, and an easing curve. However, the sun also defines a minimum and maximum angle, these two values define at what distance the easing curve begins and ends from the sun, and thus end up defining the size of the sun in the sky.
- The [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is thus suited for real time updates. When you do not need a quick sky that is not realistic, this is a good option. If you need a more realistic option, try using [PhysicalSkyMaterial] instead.
- The [ProceduralSkyMaterial] supports up to 4 suns. Each sun takes its color, energy, and direction from the corresponding [DirectionalLight3D] in the scene.
+ [ProceduralSkyMaterial] provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are defined by a main color, a color at the horizon, and an easing curve to interpolate between them. Suns are described by a position in the sky, a color, and a max angle from the sun at which the easing curve ends. The max angle therefore defines the size of the sun in the sky.
+ [ProceduralSkyMaterial] supports up to 4 suns, using the color, and energy, direction, and angular distance of the first four [DirectionalLight3D] nodes in the scene. This means that the suns are defined individually by the properties of their corresponding [DirectionalLight3D]s and globally by [member sun_angle_max] and [member sun_curve].
+ [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is therefore suited for real time updates. This makes it a great option for a sky that is simple and computationally cheap, but unrealistic. If you need a more realistic procedural option, use [PhysicalSkyMaterial].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 3c4b8837f8..4407176fd2 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -298,7 +298,7 @@
[b]Note:[/b] Changing this value can help on platforms or with third-party tools where hidden directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder.
</member>
<member name="application/config/version" type="String" setter="" getter="" default="&quot;&quot;">
- The project's human-readable version identifier. This should always be set to a non-empty string, as some exporters rely on this value being defined.
+ The project's human-readable version identifier. This is used by exporters if the version identifier isn't overridden there. If [member application/config/version] is an empty string and the version identifier isn't overridden in an exporter, the exporter will use [code]1.0.0[/code] as a version identifier.
</member>
<member name="application/config/windows_native_icon" type="String" setter="" getter="" default="&quot;&quot;">
Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon].
@@ -809,10 +809,14 @@
<member name="display/window/stretch/mode" type="String" setter="" getter="" default="&quot;disabled&quot;">
Defines how the base size is stretched to fit the resolution of the window or screen.
[b]"disabled"[/b]: No stretching happens. One unit in the scene corresponds to one pixel on the screen. In this mode, [member display/window/stretch/aspect] has no effect. Recommended for non-game applications.
- [b]"canvas_items"[/b]: The base size specified in width and height in the project settings is stretched to cover the whole screen (taking [member display/window/stretch/aspect] into account). This means that everything is rendered directly at the target resolution. 3D is unaffected, while in 2D, there is no longer a 1:1 correspondence between sprite pixels and screen pixels, which may result in scaling artifacts. Recommended for most games that don't use a pixel art aesthetic, although it is possible to use this stretch mode for pixel art games too (especially in 3D).
- [b]"viewport"[/b]: The size of the root [Viewport] is set precisely to the base size specified in the Project Settings' Display section. The scene is rendered to this viewport first. Finally, this viewport is scaled to fit the screen (taking [member display/window/stretch/aspect] into account). Recommended for games that use a pixel art aesthetic.
+ [b]"canvas_items"[/b]: The base size specified in width and height in the project settings is stretched to cover the whole screen (taking [member display/window/stretch/aspect] into account). This means that everything is rendered directly at the target resolution. 3D is unaffected, while in 2D, there is no longer a 1:1 correspondence between sprite pixels and screen pixels, which may result in scaling artifacts. Recommended for most games that don't use a pixel art esthetic, although it is possible to use this stretch mode for pixel art games too (especially in 3D).
+ [b]"viewport"[/b]: The size of the root [Viewport] is set precisely to the base size specified in the Project Settings' Display section. The scene is rendered to this viewport first. Finally, this viewport is scaled to fit the screen (taking [member display/window/stretch/aspect] into account). Recommended for games that use a pixel art esthetic.
</member>
<member name="display/window/stretch/scale" type="float" setter="" getter="" default="1.0">
+ The scale factor multiplier to use for 2D elements. This multiplies the final scale factor determined by [member display/window/stretch/mode]. If using the [b]Disabled[/b] stretch mode, this scale factor is applied as-is. This can be adjusted to make the UI easier to read on certain displays.
+ </member>
+ <member name="display/window/stretch/scale_mode" type="String" setter="" getter="" default="&quot;fractional&quot;">
+ The policy to use to determine the final scale factor for 2D elements. This affects how [member display/window/stretch/scale] is applied, in addition to the automatic scale factor determined by [member display/window/stretch/mode].
</member>
<member name="display/window/subwindows/embed_subwindows" type="bool" setter="" getter="" default="true">
If [code]true[/code] subwindows are embedded in the main window.
@@ -1991,6 +1995,12 @@
<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
If enabled the avoidance calculations use multiple threads.
</member>
+ <member name="navigation/baking/thread_model/baking_use_high_priority_threads" type="bool" setter="" getter="" default="true">
+ If enabled and async navmesh baking uses multiple threads the threads run with high priority.
+ </member>
+ <member name="navigation/baking/thread_model/baking_use_multiple_threads" type="bool" setter="" getter="" default="true">
+ If enabled the async navmesh baking uses multiple threads.
+ </member>
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
</member>
@@ -2623,7 +2633,7 @@
</member>
<member name="rendering/textures/default_filters/texture_mipmap_bias" type="float" setter="" getter="" default="0.0">
Affects the final texture sharpness by reading from a lower or higher mipmap (also called "texture LOD bias"). Negative values make mipmapped textures sharper but grainier when viewed at a distance, while positive values make mipmapped textures blurrier (even when up close).
- Enabling temporal antialiasing ([member rendering/anti_aliasing/quality/use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member rendering/anti_aliasing/quality/screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enbled at the same time, an offset of [code]-0.75[/code] is applied to this value.
+ Enabling temporal antialiasing ([member rendering/anti_aliasing/quality/use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member rendering/anti_aliasing/quality/screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enabled at the same time, an offset of [code]-0.75[/code] is applied to this value.
[b]Note:[/b] If [member rendering/scaling_3d/scale] is lower than [code]1.0[/code] (exclusive), [member rendering/textures/default_filters/texture_mipmap_bias] is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]log2(scaling_3d_scale) + mipmap_bias[/code].
[b]Note:[/b] This property is only read when the project starts. To change the mipmap LOD bias at run-time, set [member Viewport.texture_mipmap_bias] instead.
</member>
@@ -2653,6 +2663,9 @@
<member name="rendering/textures/webp_compression/lossless_compression_factor" type="float" setter="" getter="" default="25">
The default compression factor for lossless WebP. Decompression speed is mostly unaffected by the compression factor. Supported values are 0 to 100.
</member>
+ <member name="rendering/viewport/hdr_2d" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], enables [member Viewport.use_hdr_2d] on the root viewport. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow).
+ </member>
<member name="rendering/viewport/transparent_background" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables [member Viewport.transparent_bg] on the root viewport. This allows per-pixel transparency to be effective after also enabling [member display/window/size/transparent] and [member display/window/per_pixel_transparency/allowed].
</member>
@@ -2707,4 +2720,11 @@
If [code]true[/code], Godot will compile shaders required for XR.
</member>
</members>
+ <signals>
+ <signal name="settings_changed">
+ <description>
+ Emitted when any setting is changed, up to once per process frame.
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml
index 3af5b78fcc..d40d835d26 100644
--- a/doc/classes/RenderSceneBuffersRD.xml
+++ b/doc/classes/RenderSceneBuffersRD.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This object manages all 3D rendering buffers for the rendering device based renderers. An instance of this object is created for every viewport that has 3D rendering enabled.
- All buffers are organised in [b]contexts[/b]. The default context is called [b]render_buffers[/b] and can contain amongst others the color buffer, depth buffer, velocity buffers, VRS density map and MSAA variants of these buffers.
+ All buffers are organized in [b]contexts[/b]. The default context is called [b]render_buffers[/b] and can contain amongst others the color buffer, depth buffer, velocity buffers, VRS density map and MSAA variants of these buffers.
Buffers are only guaranteed to exist during rendering of the viewport.
[b]Note:[/b] this is an internal rendering server object only exposed for GDExtension plugins.
</description>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index a36afc7ff1..d972a2214a 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1450,6 +1450,7 @@
<description>
Returns the name of the video adapter (e.g. "GeForce GTX 1080/PCIe/SSE2").
[b]Note:[/b] When running a headless or server binary, this function returns an empty string.
+ [b]Note:[/b] On the web platform, some browsers such as Firefox may report a different, fixed GPU name such as "GeForce GTX 980" (regardless of the user's actual GPU model). This is done to make fingerprinting more difficult.
</description>
</method>
<method name="get_video_adapter_type" qualifiers="const">
@@ -3756,6 +3757,15 @@
If [code]true[/code], enables debanding on the specified viewport. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding].
</description>
</method>
+ <method name="viewport_set_use_hdr_2d">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d].
+ [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
+ </description>
+ </method>
<method name="viewport_set_use_occlusion_culling">
<return type="void" />
<param index="0" name="viewport" type="RID" />
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index c0d98ef921..fcc5925f8d 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -137,6 +137,7 @@
<param index="0" name="name" type="StringName" />
<description>
Returns [code]true[/code] if the given group exists.
+ A group exists if any [Node] in the tree belongs to it (see [method Node.add_to_group]). Groups without nodes are removed automatically.
</description>
</method>
<method name="notify_group">
@@ -225,7 +226,8 @@
For mobile platforms, see [member quit_on_go_back].
</member>
<member name="current_scene" type="Node" setter="set_current_scene" getter="get_current_scene">
- The current scene.
+ Returns the root node of the currently running scene, regardless of its structure.
+ [b]Warning:[/b] Setting this directly might not work as expected, and will [i]not[/i] add or remove any nodes from the tree, consider using [method change_scene_to_file] or [method change_scene_to_packed] instead.
</member>
<member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint" default="false">
If [code]true[/code], collision shapes will be visible when running the game from the editor for debugging purposes.
diff --git a/doc/classes/ShaderGlobalsOverride.xml b/doc/classes/ShaderGlobalsOverride.xml
index afd6ee527e..c5fe2a3e8a 100644
--- a/doc/classes/ShaderGlobalsOverride.xml
+++ b/doc/classes/ShaderGlobalsOverride.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderGlobalsOverride" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Overrides global shader parameters' values in a specific scene.
+ A node used to override global shader parameters' values in a scene.
</brief_description>
<description>
Similar to how a [WorldEnvironment] node can be used to override the environment while a specific scene is loaded, [ShaderGlobalsOverride] can be used to override global shader parameters temporarily. Once the node is removed, the project-wide values for the global shader parameters are restored. See the [RenderingServer] [code]global_shader_parameter_*[/code] methods for more information.
@@ -9,5 +9,6 @@
[b]Note:[/b] All [ShaderGlobalsOverride] nodes are made part of a [code]"shader_overrides_group"[/code] group when they are added to the scene tree. The currently active [ShaderGlobalsOverride] node also has a [code]"shader_overrides_group_active"[/code] group added to it. You can use this to check which [ShaderGlobalsOverride] node is currently active.
</description>
<tutorials>
+ <link title="Shading language">$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html</link>
</tutorials>
</class>
diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml
index e2d0696a4b..d0752b4b25 100644
--- a/doc/classes/ShaderMaterial.xml
+++ b/doc/classes/ShaderMaterial.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A material that uses a custom [Shader] program.
+ A material defined by a custom [Shader] program and the values of its shader parameters.
</brief_description>
<description>
- A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader.
- [b]Note:[/b] For performance reasons the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] is changed. Only in editor, is also emitted for [member shader] changes.
+ A material that uses a custom [Shader] program to render visual items (canvas items, meshes, skies, fog), or to process particles. Compared to other materials, [ShaderMaterial] gives deeper control over the generated shader code. For more information, see the shaders documentation index below.
+ Multiple [ShaderMaterial]s can use the same shader and configure different values for the shader uniforms.
+ [b]Note:[/b] For performance reasons, the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] changes. Only in editor, it is also emitted for [member shader] changes.
</description>
<tutorials>
<link title="Shaders documentation index">$DOCS_URL/tutorials/shaders/index.html</link>
diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml
index d52fd6ce40..d92319b390 100644
--- a/doc/classes/Sky.xml
+++ b/doc/classes/Sky.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Sky" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Background that uses a [Material] to draw a sky.
+ Defines a 3D environment's background by using a [Material].
</brief_description>
<description>
- The [Sky] class uses a [Material] to draw the background and update the reflection/radiance cubemaps.
+ The [Sky] class uses a [Material] to render a 3D environment's background and the light it emits by updating the reflection/radiance cubemaps.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml
index 1d227241ad..b5202bd4e3 100644
--- a/doc/classes/SoftBody3D.xml
+++ b/doc/classes/SoftBody3D.xml
@@ -107,6 +107,7 @@
<member name="drag_coefficient" type="float" setter="set_drag_coefficient" getter="get_drag_coefficient" default="0.0">
</member>
<member name="linear_stiffness" type="float" setter="set_linear_stiffness" getter="get_linear_stiffness" default="0.5">
+ Higher values will result in a stiffer body, while lower values will increase the body's ability to bend. The value can be between [code]0.0[/code] and [code]1.0[/code] (inclusive).
</member>
<member name="parent_collision_ignore" type="NodePath" setter="set_parent_collision_ignore" getter="get_parent_collision_ignore" default="NodePath(&quot;&quot;)">
[NodePath] to a [CollisionObject3D] this SoftBody3D should avoid clipping.
diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml
index 8814169d1d..55247678cb 100644
--- a/doc/classes/StandardMaterial3D.xml
+++ b/doc/classes/StandardMaterial3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="StandardMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Physically based rendering (PBR) material that can be applied to 3D objects.
+ A PBR (Physically Based Rendering) material to be used on 3D objects.
</brief_description>
<description>
[StandardMaterial3D]'s properties are inherited from [BaseMaterial3D]. [StandardMaterial3D] uses separate textures for ambient occlusion, roughness and metallic maps. To use a single ORM map for all 3 textures, use an [ORMMaterial3D] instead.
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 913f2f2654..9c31576b72 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -720,6 +720,12 @@
Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat].
</description>
</method>
+ <method name="reverse" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the copy of this string in reverse order.
+ </description>
+ </method>
<method name="rfind" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index 0814a30a8d..1c5032be9b 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -627,6 +627,12 @@
Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat].
</description>
</method>
+ <method name="reverse" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the copy of this string in reverse order.
+ </description>
+ </method>
<method name="rfind" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
diff --git a/doc/classes/StyleBox.xml b/doc/classes/StyleBox.xml
index 75fbb03dd6..8ba57766eb 100644
--- a/doc/classes/StyleBox.xml
+++ b/doc/classes/StyleBox.xml
@@ -110,7 +110,7 @@
[method get_margin] should be used to fetch this value as consumer instead of reading these properties directly. This is because it correctly respects negative values and the fallback mentioned above.
</member>
<member name="content_margin_left" type="float" setter="set_content_margin" getter="get_content_margin" default="-1.0">
- The left margin for the contents of this style box. Increasing this value reduces the space available to the contents from the left.
+ The left margin for the contents of this style box. Increasing this value reduces the space available to the contents from the left.
Refer to [member content_margin_bottom] for extra considerations.
</member>
<member name="content_margin_right" type="float" setter="set_content_margin" getter="get_content_margin" default="-1.0">
diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml
index ea3dd0acd2..239bcc257c 100644
--- a/doc/classes/SystemFont.xml
+++ b/doc/classes/SystemFont.xml
@@ -19,9 +19,6 @@
<member name="antialiasing" type="int" setter="set_antialiasing" getter="get_antialiasing" enum="TextServer.FontAntialiasing" default="1">
Font anti-aliasing mode.
</member>
- <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
- Array of fallback [Font]s.
- </member>
<member name="font_italic" type="bool" setter="set_font_italic" getter="get_font_italic" default="false">
If set to [code]true[/code], italic or oblique font is preferred.
</member>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 4ed831c213..3eac11792f 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -242,7 +242,7 @@
<param index="3" name="alternative_tile" type="int" default="-1" />
<description>
Returns a [Vector2i] array with the positions of all cells containing a tile in the given layer. Tiles may be filtered according to their source ([param source_id]), their atlas coordinates ([param atlas_coords]) or alternative id ([param alternative_tile]).
- If a parameter has it's value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default value, this method returns the same result as [method get_used_cells].
+ If a parameter has its value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default value, this method returns the same result as [method get_used_cells].
A cell is considered empty if its source identifier equals -1, its atlas coordinates identifiers is [code]Vector2(-1, -1)[/code] and its alternative identifier is -1.
If [param layer] is negative, the layers are accessed from the last one.
</description>
@@ -316,7 +316,7 @@
<param index="3" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" />
<param index="4" name="alternative_tile" type="int" default="0" />
<description>
- Sets the tile indentifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts:
+ Sets the tile identifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts:
- The source identifier [param source_id] identifies a [TileSetSource] identifier. See [method TileSet.set_source_id],
- The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]). For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]),
- The alternative tile identifier [param alternative_tile] identifies a tile alternative in the atlas (if the source is a [TileSetAtlasSource]), and the scene for a [TileSetScenesCollectionSource].
diff --git a/doc/classes/TileMapPattern.xml b/doc/classes/TileMapPattern.xml
index b1b7720810..1733ecf592 100644
--- a/doc/classes/TileMapPattern.xml
+++ b/doc/classes/TileMapPattern.xml
@@ -71,7 +71,7 @@
<param index="2" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" />
<param index="3" name="alternative_tile" type="int" default="-1" />
<description>
- Sets the tile indentifiers for the cell at coordinates [param coords]. See [method TileMap.set_cell].
+ Sets the tile identifiers for the cell at coordinates [param coords]. See [method TileMap.set_cell].
</description>
</method>
<method name="set_size">
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 1193855d7c..3cba3d7d11 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -332,7 +332,7 @@
<param index="1" name="coords_from" type="Vector2i" />
<param index="2" name="alternative_from" type="int" />
<description>
- According to the configured proxies, maps the provided indentifiers to a new set of identifiers. The source ID, atlas coordinates ID and alternative tile ID are returned as a 3 elements Array.
+ According to the configured proxies, maps the provided identifiers to a new set of identifiers. The source ID, atlas coordinates ID and alternative tile ID are returned as a 3 elements Array.
This function first look for matching alternative-level proxies, then coordinates-level proxies, then source-level proxies.
If no proxy corresponding to provided identifiers are found, returns the same values the ones used as arguments.
</description>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index a1932ba5b6..004e93ac04 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -606,6 +606,9 @@
<theme_item name="font_size" data_type="font_size" type="int">
Font size of the item's text.
</theme_item>
+ <theme_item name="title_button_font_size" data_type="font_size" type="int">
+ Font size of the title button's text.
+ </theme_item>
<theme_item name="arrow" data_type="icon" type="Texture2D">
The arrow icon used when a foldable item is not collapsed.
</theme_item>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 9a5e7ed6f6..101717966e 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -52,6 +52,13 @@
Returns an individual bit on the rendering layer mask.
</description>
</method>
+ <method name="get_embedded_subwindows" qualifiers="const">
+ <return type="Window[]" />
+ <description>
+ Returns a list of the visible embedded [Window]s inside the viewport.
+ [b]Note:[/b] [Window]s inside other viewports will not be listed.
+ </description>
+ </method>
<method name="get_final_transform" qualifiers="const">
<return type="Transform2D" />
<description>
@@ -337,7 +344,7 @@
</member>
<member name="texture_mipmap_bias" type="float" setter="set_texture_mipmap_bias" getter="get_texture_mipmap_bias" default="0.0">
Affects the final texture sharpness by reading from a lower or higher mipmap (also called "texture LOD bias"). Negative values make mipmapped textures sharper but grainier when viewed at a distance, while positive values make mipmapped textures blurrier (even when up close).
- Enabling temporal antialiasing ([member use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enbled at the same time, an offset of [code]-0.75[/code] is applied to this value.
+ Enabling temporal antialiasing ([member use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enabled at the same time, an offset of [code]-0.75[/code] is applied to this value.
[b]Note:[/b] If [member scaling_3d_scale] is lower than [code]1.0[/code] (exclusive), [member texture_mipmap_bias] is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]log2(scaling_3d_scale) + mipmap_bias[/code].
To control this property on the root viewport, set the [member ProjectSettings.rendering/textures/default_filters/texture_mipmap_bias] project setting.
</member>
@@ -348,6 +355,10 @@
If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS]. See also [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding].
In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger.
</member>
+ <member name="use_hdr_2d" type="bool" setter="set_use_hdr_2d" getter="is_using_hdr_2d" default="false">
+ If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients.
+ [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
+ </member>
<member name="use_occlusion_culling" type="bool" setter="set_use_occlusion_culling" getter="is_using_occlusion_culling" default="false">
If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D for this viewport. For the root viewport, [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] must be set to [code]true[/code] instead.
[b]Note:[/b] Enabling occlusion culling has a cost on the CPU. Only enable occlusion culling if you actually plan to use it, and think whether your scene can actually benefit from occlusion culling. Large, open scenes with few or no objects blocking the view will generally not benefit much from occlusion culling. Large open scenes generally benefit more from mesh LOD and visibility ranges ([member GeometryInstance3D.visibility_range_begin] and [member GeometryInstance3D.visibility_range_end]) compared to occlusion culling.
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index bf48d80c66..f424a27a8f 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -4,10 +4,10 @@
A custom shader program with a visual editor.
</brief_description>
<description>
- This class allows you to define a custom shader program that can be used for various materials to render objects.
- The visual shader editor creates the shader.
+ This class provides a graph-like visual editor for creating a [Shader]. Although [VisualShader]s do not require coding, they share the same logic with script shaders. They use [VisualShaderNode]s that can be connected to each other to control the flow of the shader. The visual shader graph is converted to a script shader behind the scenes.
</description>
<tutorials>
+ <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
</tutorials>
<methods>
<method name="add_node">
diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index 5d147bd542..baf9eded56 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Base class for nodes in a visual shader graph.
+ Base class for [VisualShader] nodes. Not related to scene nodes.
</brief_description>
<description>
- Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node has also connection ports that allow to connect it to another nodes and control the flow of the shader.
+ Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node also has connection ports that allow to connect it to another nodes and control the flow of the shader.
</description>
<tutorials>
- <link title="VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
+ <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
</tutorials>
<methods>
<method name="clear_default_input_values">
diff --git a/doc/classes/VisualShaderNodeIf.xml b/doc/classes/VisualShaderNodeIf.xml
index 5fc9a0cdf4..82799bb5fd 100644
--- a/doc/classes/VisualShaderNodeIf.xml
+++ b/doc/classes/VisualShaderNodeIf.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeIf" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Compares two floating-point numbers in order to return a required vector within the visual shader graph.
+ Outputs a 3D vector based on the result of a floating point comparison within the visual shader graph.
</brief_description>
<description>
- First two ports are scalar floating-point numbers to compare, third is tolerance comparison amount and last three ports represents a vectors returned if [code]a == b[/code], [code]a &gt; b[/code] and [code]a &lt; b[/code] respectively.
+ This visual shader node has six input ports. Port 1 and 2 provide the two floating point numbers [code]a[/code] and [code]b[/code] that will be compared. Port 3 is the tolerance, which allows similar floating point number to be considered equal. Ports 4 to 6 are the possible outputs, returned if [code]a == b[/code], [code]a &gt; b[/code], or [code]a &lt; b[/code] respectively.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 82498b9ba4..92cd11d720 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -569,6 +569,9 @@
<member name="content_scale_size" type="Vector2i" setter="set_content_scale_size" getter="get_content_scale_size" default="Vector2i(0, 0)">
Base size of the content (i.e. nodes that are drawn inside the window). If non-zero, [Window]'s content will be scaled when the window is resized to a different size.
</member>
+ <member name="content_scale_stretch" type="int" setter="set_content_scale_stretch" getter="get_content_scale_stretch" enum="Window.ContentScaleStretch" default="0">
+ The policy to use to determine the final scale factor for 2D elements. This affects how [member content_scale_factor] is applied, in addition to the automatic scale factor determined by [member content_scale_size].
+ </member>
<member name="current_screen" type="int" setter="set_current_screen" getter="get_current_screen">
The screen the window is currently on.
</member>
@@ -840,6 +843,12 @@
<constant name="CONTENT_SCALE_ASPECT_EXPAND" value="4" enum="ContentScaleAspect">
The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the top-left corner and add an extra visible area in the stretched space.
</constant>
+ <constant name="CONTENT_SCALE_STRETCH_FRACTIONAL" value="0" enum="ContentScaleStretch">
+ The content will be stretched according to a fractional factor. This fills all the space available in the window, but allows "pixel wobble" to occur due to uneven pixel scaling.
+ </constant>
+ <constant name="CONTENT_SCALE_STRETCH_INTEGER" value="1" enum="ContentScaleStretch">
+ The content will be stretched only according to an integer factor, preserving sharp pixels. This may leave a black background visible on the window's edges depending on the window size.
+ </constant>
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
Automatic layout direction, determined from the parent window layout direction.
</constant>
diff --git a/doc/classes/XMLParser.xml b/doc/classes/XMLParser.xml
index 6812e83b8f..9b960f66fe 100644
--- a/doc/classes/XMLParser.xml
+++ b/doc/classes/XMLParser.xml
@@ -84,7 +84,7 @@
<method name="is_empty" qualifiers="const">
<return type="bool" />
<description>
- Check whether the current element is empty (this only works for completely empty tags, e.g. [code]&lt;element \&gt;[/code]).
+ Check whether the current element is empty (this only works for completely empty tags, e.g. [code]&lt;element /&gt;[/code]).
</description>
</method>
<method name="open">
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index bc0da03e98..914cf75929 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -390,7 +390,7 @@
<operator name="operator ~">
<return type="int" />
<description>
- Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement/]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code].
+ Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code].
[codeblock]
print(~4) # Prints -5
print(~(-7)) # Prints 6
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 86405fa3f0..9f8d9164ce 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -2048,16 +2048,26 @@ void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::C
void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) {
GLES3::Config *config = GLES3::Config::get_singleton();
p_size = nearest_power_of_2_templated(p_size);
- if (p_size == state.shadow_texture_size) {
- return;
- }
if (p_size > config->max_texture_size) {
p_size = config->max_texture_size;
WARN_PRINT("Attempting to set CanvasItem shadow atlas size to " + itos(p_size) + " which is beyond limit of " + itos(config->max_texture_size) + "supported by hardware.");
}
+ if (p_size == state.shadow_texture_size) {
+ return;
+ }
state.shadow_texture_size = p_size;
+
+ if (state.shadow_fb != 0) {
+ glDeleteFramebuffers(1, &state.shadow_fb);
+ GLES3::Utilities::get_singleton()->texture_free_data(state.shadow_texture);
+ glDeleteRenderbuffers(1, &state.shadow_depth_buffer);
+ state.shadow_fb = 0;
+ state.shadow_texture = 0;
+ state.shadow_depth_buffer = 0;
+ }
+ _update_shadow_atlas();
}
bool RasterizerCanvasGLES3::free(RID p_rid) {
@@ -2442,6 +2452,7 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec
return id;
}
+
void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) {
PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon);
ERR_FAIL_COND(!pb_ptr);
@@ -2521,6 +2532,8 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
+ glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
+
polygon_buffers.last_id = 1;
// quad buffer
{
@@ -2700,6 +2713,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1);
data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
+ state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size");
shadow_render.shader.initialize();
shadow_render.shader_version = shadow_render.shader.version_create();
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 2eac5f57e1..c1b3e20e33 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -192,7 +192,7 @@ public:
GLuint index_buffer = 0;
int count = 0;
bool color_disabled = false;
- Color color;
+ Color color = Color(1.0, 1.0, 1.0, 1.0);
};
struct {
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
index d53c0fcb26..21fd4d3d9d 100644
--- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -12,7 +12,7 @@
#define FLAGS_CLIP_RECT_UV uint(1 << 9)
#define FLAGS_TRANSPOSE_RECT uint(1 << 10)
-#define FLAGS_USING_LIGHT_MASK uint(1 << 11)
+// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here.
#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
#define FLAGS_USING_PARTICLES uint(1 << 13)
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index aa6319f0ef..a36004209b 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1957,14 +1957,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture
if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) {
//textire
if (!p_load_textures) {
- value = RID();
continue;
}
String path = value;
- Ref<Resource> resource = ResourceLoader::load(path);
- ERR_CONTINUE(resource.is_null());
- value = resource;
+ if (path.is_empty()) {
+ value = RID();
+ } else {
+ Ref<Resource> resource = ResourceLoader::load(path);
+ value = resource;
+ }
}
if (global_shader_uniforms.variables.has(name)) {
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index bad2b31a31..c25dbef288 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -629,6 +629,8 @@ public:
void render_target_clear_used(RID p_render_target);
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {}
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; }
// new
void render_target_set_as_unused(RID p_render_target) override {
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index a80b7d449e..3b15cf60f7 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -313,19 +313,19 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
}
}
-uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessUnix::_get_unix_permissions(const String &p_file) {
String file = fix_path(p_file);
struct stat status = {};
int err = stat(file.utf8().get_data(), &status);
if (!err) {
- return status.st_mode & 0x7FF; //only permissions
+ return status.st_mode & 0xFFF; //only permissions
} else {
ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + ".");
}
}
-Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccessUnix::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
String file = fix_path(p_file);
int err = chmod(file.utf8().get_data(), p_permissions);
@@ -336,6 +336,74 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_per
return FAILED;
}
+bool FileAccessUnix::_get_hidden_attribute(const String &p_file) {
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+ String file = fix_path(p_file);
+
+ struct stat st = {};
+ int err = stat(file.utf8().get_data(), &st);
+ ERR_FAIL_COND_V_MSG(err, false, "Failed to get attributes for: " + p_file);
+
+ return (st.st_flags & UF_HIDDEN);
+#else
+ return false;
+#endif
+}
+
+Error FileAccessUnix::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+ String file = fix_path(p_file);
+
+ struct stat st = {};
+ int err = stat(file.utf8().get_data(), &st);
+ ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to get attributes for: " + p_file);
+
+ if (p_hidden) {
+ err = chflags(file.utf8().get_data(), st.st_flags | UF_HIDDEN);
+ } else {
+ err = chflags(file.utf8().get_data(), st.st_flags & ~UF_HIDDEN);
+ }
+ ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to set attributes for: " + p_file);
+ return OK;
+#else
+ return ERR_UNAVAILABLE;
+#endif
+}
+
+bool FileAccessUnix::_get_read_only_attribute(const String &p_file) {
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+ String file = fix_path(p_file);
+
+ struct stat st = {};
+ int err = stat(file.utf8().get_data(), &st);
+ ERR_FAIL_COND_V_MSG(err, false, "Failed to get attributes for: " + p_file);
+
+ return st.st_flags & UF_IMMUTABLE;
+#else
+ return false;
+#endif
+}
+
+Error FileAccessUnix::_set_read_only_attribute(const String &p_file, bool p_ro) {
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+ String file = fix_path(p_file);
+
+ struct stat st = {};
+ int err = stat(file.utf8().get_data(), &st);
+ ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to get attributes for: " + p_file);
+
+ if (p_ro) {
+ err = chflags(file.utf8().get_data(), st.st_flags | UF_IMMUTABLE);
+ } else {
+ err = chflags(file.utf8().get_data(), st.st_flags & ~UF_IMMUTABLE);
+ }
+ ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to set attributes for: " + p_file);
+ return OK;
+#else
+ return ERR_UNAVAILABLE;
+#endif
+}
+
void FileAccessUnix::close() {
_close();
}
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 79c4e73636..2bfac27c4f 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -79,8 +79,13 @@ public:
virtual bool file_exists(const String &p_path) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 1ed6839dd7..a55abe6a82 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -5905,6 +5905,64 @@ void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_
us->invalidated_callback_userdata = p_userdata;
}
+Error RenderingDeviceVulkan::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a draw list");
+ ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a compute list");
+
+ // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed
+ // for the source or destination buffers before performing the copy. These masks are effectively ignored.
+ VkPipelineShaderStageCreateFlags src_stage_mask = 0;
+ VkAccessFlags src_access_mask = 0;
+ Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_NO_BARRIER);
+ if (!src_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type.");
+ }
+
+ VkPipelineStageFlags dst_stage_mask = 0;
+ VkAccessFlags dst_access = 0;
+ if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
+ // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set
+ // after the copy command is queued.
+ dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dst_access = VK_ACCESS_TRANSFER_WRITE_BIT;
+ }
+
+ Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stage_mask, dst_access, p_post_barrier);
+ if (!dst_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type.");
+ }
+
+ // Validate the copy's dimensions for both buffers.
+ ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer.");
+ ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer.");
+
+ // Perform the copy.
+ VkBufferCopy region;
+ region.srcOffset = p_src_offset;
+ region.dstOffset = p_dst_offset;
+ region.size = p_size;
+ vkCmdCopyBuffer(frames[frame].draw_command_buffer, src_buffer->buffer, dst_buffer->buffer, 1, &region);
+
+#ifdef FORCE_FULL_BARRIER
+ _full_barrier(true);
+#else
+ if (dst_stage_mask == 0) {
+ dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ }
+
+ // As indicated by the post barrier mask, push a new barrier.
+ if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) {
+ _buffer_memory_barrier(dst_buffer->buffer, p_dst_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true);
+ }
+#endif
+
+ return OK;
+}
+
Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) {
_THREAD_SAFE_METHOD_
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index fd832312ac..edff19a70c 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -1152,6 +1152,7 @@ public:
virtual bool uniform_set_is_valid(RID p_uniform_set);
virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata);
+ virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); // Works for any buffer.
virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0);
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index c167caeb7c..c1632907eb 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -504,6 +504,10 @@ Error VulkanContext::_initialize_device_extensions() {
register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false);
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true);
+ }
+
// TODO consider the following extensions:
// - VK_KHR_spirv_1_4
// - VK_KHR_swapchain_mutable_format
@@ -1700,17 +1704,6 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, Display
Error err = _update_swap_chain(&window);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
- VkSemaphoreCreateInfo semaphoreCreateInfo = {
- /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
- /*pNext*/ nullptr,
- /*flags*/ 0,
- };
-
- for (uint32_t i = 0; i < FRAME_LAG; i++) {
- VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]);
- ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE);
- }
-
windows[p_window_id] = window;
return OK;
}
@@ -1760,9 +1753,6 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi
void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) {
ERR_FAIL_COND(!windows.has(p_window_id));
_clean_up_swap_chain(&windows[p_window_id]);
- for (uint32_t i = 0; i < FRAME_LAG; i++) {
- vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr);
- }
vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr);
windows.erase(p_window_id);
@@ -1792,6 +1782,17 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) {
if (separate_present_queue) {
vkDestroyCommandPool(device, window->present_cmd_pool, nullptr);
}
+
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ // Destroy the semaphores now (we'll re-create it later if we have to).
+ // We must do this because the semaphore cannot be reused if it's in a signaled state
+ // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR)
+ // The only way to reset it would be to present the swapchain... the one we just destroyed.
+ // And the API has no way to "unsignal" the semaphore.
+ vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr);
+ window->image_acquired_semaphores[i] = 0;
+ }
+
return OK;
}
@@ -2175,6 +2176,17 @@ Error VulkanContext::_update_swap_chain(Window *window) {
// Reset current buffer.
window->current_buffer = 0;
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {
+ /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ /*pNext*/ nullptr,
+ /*flags*/ 0,
+ };
+
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]);
+ ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE);
+ }
+
return OK;
}
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index eeeb6ee689..6c84aee2b7 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -706,7 +706,7 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE
}
void AudioDriverWASAPI::thread_func(void *p_udata) {
- CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
AudioDriverWASAPI *ad = static_cast<AudioDriverWASAPI *>(p_udata);
uint32_t avail_frames = 0;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 6e69743d4e..c1e0c3fb60 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -381,14 +381,62 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
}
}
-uint32_t FileAccessWindows::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) {
return 0;
}
-Error FileAccessWindows::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccessWindows::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
return ERR_UNAVAILABLE;
}
+bool FileAccessWindows::_get_hidden_attribute(const String &p_file) {
+ String file = fix_path(p_file);
+
+ DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
+ ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file);
+ return (attrib & FILE_ATTRIBUTE_HIDDEN);
+}
+
+Error FileAccessWindows::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+ String file = fix_path(p_file);
+
+ DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
+ ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
+ BOOL ok;
+ if (p_hidden) {
+ ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_HIDDEN);
+ } else {
+ ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN);
+ }
+ ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
+
+ return OK;
+}
+
+bool FileAccessWindows::_get_read_only_attribute(const String &p_file) {
+ String file = fix_path(p_file);
+
+ DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
+ ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file);
+ return (attrib & FILE_ATTRIBUTE_READONLY);
+}
+
+Error FileAccessWindows::_set_read_only_attribute(const String &p_file, bool p_ro) {
+ String file = fix_path(p_file);
+
+ DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
+ ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
+ BOOL ok;
+ if (p_ro) {
+ ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_READONLY);
+ } else {
+ ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_READONLY);
+ }
+ ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
+
+ return OK;
+}
+
void FileAccessWindows::close() {
_close();
}
diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h
index 13c881e562..73143009fc 100644
--- a/drivers/windows/file_access_windows.h
+++ b/drivers/windows/file_access_windows.h
@@ -80,8 +80,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 3965dcd198..d4aebfc7a6 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -31,6 +31,7 @@
#include "connections_dialog.h"
#include "core/config/project_settings.h"
+#include "core/templates/hash_set.h"
#include "editor/doc_tools.h"
#include "editor/editor_help.h"
#include "editor/editor_inspector.h"
@@ -1239,60 +1240,102 @@ void ConnectionsDock::update_tree() {
}
TreeItem *root = tree->create_item();
+ DocTools *doc_data = EditorHelp::get_doc_data();
+ EditorData &editor_data = EditorNode::get_editor_data();
+ StringName native_base = selected_node->get_class();
+ Ref<Script> script_base = selected_node->get_script();
+
+ while (native_base != StringName()) {
+ String class_name;
+ String doc_class_name;
+ Ref<Texture2D> class_icon;
+ List<MethodInfo> class_signals;
+ const DocData::ClassDoc *class_doc = nullptr;
+
+ if (script_base.is_valid()) {
+ // Try script global name.
+ class_name = script_base->get_global_name();
+ if (!class_name.is_empty()) {
+ HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(class_name);
+ if (F) {
+ class_doc = &F->value;
+ doc_class_name = class_name;
+ }
+ }
- List<MethodInfo> node_signals;
-
- selected_node->get_signal_list(&node_signals);
+ // Try script path.
+ if (class_name.is_empty()) {
+ class_name = script_base->get_path().get_file();
+ }
+ if (!class_doc) {
+ doc_class_name = script_base->get_path().trim_prefix("res://").quote();
+ HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(doc_class_name);
+ if (F) {
+ class_doc = &F->value;
+ }
+ }
- bool did_script = false;
- StringName base = selected_node->get_class();
+ class_icon = editor_data.get_script_icon(script_base);
+ if (class_icon.is_null() && has_theme_icon(native_base, SNAME("EditorIcons"))) {
+ class_icon = get_theme_icon(native_base, SNAME("EditorIcons"));
+ }
- while (base) {
- List<MethodInfo> node_signals2;
- Ref<Texture2D> icon;
- String name;
+ script_base->get_script_signal_list(&class_signals);
- if (!did_script) {
- // Get script signals (including signals from any base scripts).
- Ref<Script> scr = selected_node->get_script();
- if (scr.is_valid()) {
- scr->get_script_signal_list(&node_signals2);
- if (scr->get_path().is_resource_file()) {
- name = scr->get_path().get_file();
- } else {
- name = scr->get_class();
+ // TODO: Core: Add optional parameter to ignore base classes (no_inheritance like in ClassDB).
+ Ref<Script> base = script_base->get_base_script();
+ if (base.is_valid()) {
+ List<MethodInfo> base_signals;
+ base->get_script_signal_list(&base_signals);
+ HashSet<String> base_signal_names;
+ for (List<MethodInfo>::Element *F = base_signals.front(); F; F = F->next()) {
+ base_signal_names.insert(F->get().name);
}
-
- if (has_theme_icon(scr->get_class(), SNAME("EditorIcons"))) {
- icon = get_theme_icon(scr->get_class(), SNAME("EditorIcons"));
+ for (List<MethodInfo>::Element *F = class_signals.front(); F; F = F->next()) {
+ if (base_signal_names.has(F->get().name)) {
+ class_signals.erase(F);
+ }
}
}
+
+ script_base = base;
} else {
- ClassDB::get_signal_list(base, &node_signals2, true);
- if (has_theme_icon(base, SNAME("EditorIcons"))) {
- icon = get_theme_icon(base, SNAME("EditorIcons"));
+ class_name = native_base;
+
+ if (has_theme_icon(native_base, SNAME("EditorIcons"))) {
+ class_icon = get_theme_icon(native_base, SNAME("EditorIcons"));
}
- name = base;
+
+ ClassDB::get_signal_list(native_base, &class_signals, true);
+
+ HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(class_name);
+ if (F) {
+ class_doc = &F->value;
+ doc_class_name = class_name;
+ }
+
+ native_base = ClassDB::get_parent_class(native_base);
}
- if (icon.is_null()) {
- icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
+ if (class_icon.is_null()) {
+ class_icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
}
TreeItem *section_item = nullptr;
// Create subsections.
- if (node_signals2.size()) {
+ if (!class_signals.is_empty()) {
+ class_signals.sort();
+
section_item = tree->create_item(root);
- section_item->set_text(0, name);
- section_item->set_icon(0, icon);
+ section_item->set_text(0, class_name);
+ section_item->set_icon(0, class_icon);
section_item->set_selectable(0, false);
section_item->set_editable(0, false);
section_item->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
- node_signals2.sort();
}
- for (MethodInfo &mi : node_signals2) {
+ for (MethodInfo &mi : class_signals) {
const StringName signal_name = mi.name;
if (!search_box->get_text().is_subsequence_ofn(signal_name)) {
continue;
@@ -1320,9 +1363,9 @@ void ConnectionsDock::update_tree() {
String descr;
bool found = false;
- HashMap<StringName, HashMap<StringName, String>>::Iterator G = descr_cache.find(base);
+ HashMap<StringName, HashMap<StringName, String>>::ConstIterator G = descr_cache.find(doc_class_name);
if (G) {
- HashMap<StringName, String>::Iterator F = G->value.find(signal_name);
+ HashMap<StringName, String>::ConstIterator F = G->value.find(signal_name);
if (F) {
found = true;
descr = F->value;
@@ -1330,22 +1373,15 @@ void ConnectionsDock::update_tree() {
}
if (!found) {
- DocTools *dd = EditorHelp::get_doc_data();
- HashMap<String, DocData::ClassDoc>::Iterator F = dd->class_list.find(base);
- while (F && descr.is_empty()) {
- for (int i = 0; i < F->value.signals.size(); i++) {
- if (F->value.signals[i].name == signal_name.operator String()) {
- descr = DTR(F->value.signals[i].description);
+ if (class_doc) {
+ for (int i = 0; i < class_doc->signals.size(); i++) {
+ if (class_doc->signals[i].name == signal_name.operator String()) {
+ descr = DTR(class_doc->signals[i].description);
break;
}
}
- if (!F->value.inherits.is_empty()) {
- F = dd->class_list.find(F->value.inherits);
- } else {
- break;
- }
}
- descr_cache[base][signal_name] = descr;
+ descr_cache[doc_class_name][signal_name] = descr;
}
// "::" separators used in make_custom_tooltip for formatting.
@@ -1400,12 +1436,6 @@ void ConnectionsDock::update_tree() {
}
}
}
-
- if (!did_script) {
- did_script = true;
- } else {
- base = ClassDB::get_parent_class(base);
- }
}
connect_button->set_text(TTR("Connect..."));
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index 1e9b7c2c60..b2fd84d2ed 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -232,7 +232,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
PropertyHint h = PROPERTY_HINT_NONE;
String hs;
- if (var.var_type == Variant::OBJECT) {
+ if (var.var_type == Variant::OBJECT && v) {
v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id();
h = PROPERTY_HINT_OBJECT_ID;
hs = "Object";
diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp
index df860bab2c..fed7cb82c9 100644
--- a/editor/directory_create_dialog.cpp
+++ b/editor/directory_create_dialog.cpp
@@ -33,10 +33,10 @@
#include "core/io/dir_access.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
-#include "scene/gui/panel_container.h"
static String sanitize_input(const String &p_path) {
String path = p_path.strip_edges();
@@ -73,24 +73,17 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const {
return String();
}
-void DirectoryCreateDialog::_on_dir_path_changed(const String &p_text) {
- const String path = sanitize_input(p_text);
+void DirectoryCreateDialog::_on_dir_path_changed() {
+ const String path = sanitize_input(dir_path->get_text());
const String error = _validate_path(path);
if (error.is_empty()) {
- status_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
-
if (path.contains("/")) {
- status_label->set_text(TTR("Using slashes in folder names will create subfolders recursively."));
- } else {
- status_label->set_text(TTR("Folder name is valid."));
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK);
}
} else {
- status_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- status_label->set_text(error);
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, error, EditorValidationPanel::MSG_ERROR);
}
-
- get_ok_button()->set_disabled(!error.is_empty());
}
void DirectoryCreateDialog::ok_pressed() {
@@ -127,21 +120,13 @@ void DirectoryCreateDialog::config(const String &p_base_dir) {
label->set_text(vformat(TTR("Create new folder in %s:"), base_dir));
dir_path->set_text("new folder");
dir_path->select_all();
- _on_dir_path_changed(dir_path->get_text());
+ validation_panel->update();
}
void DirectoryCreateDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("dir_created"));
}
-void DirectoryCreateDialog::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_THEME_CHANGED: {
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- } break;
- }
-}
-
DirectoryCreateDialog::DirectoryCreateDialog() {
set_title(TTR("Create Folder"));
set_min_size(Size2i(480, 0) * EDSCALE);
@@ -154,7 +139,6 @@ DirectoryCreateDialog::DirectoryCreateDialog() {
vb->add_child(label);
dir_path = memnew(LineEdit);
- dir_path->connect("text_changed", callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed));
vb->add_child(dir_path);
register_text_enter(dir_path);
@@ -162,11 +146,11 @@ DirectoryCreateDialog::DirectoryCreateDialog() {
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
vb->add_child(spacing);
- status_panel = memnew(PanelContainer);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- vb->add_child(status_panel);
+ validation_panel = memnew(EditorValidationPanel);
+ vb->add_child(validation_panel);
+ validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Folder name is valid."));
+ validation_panel->set_update_callback(callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed));
+ validation_panel->set_accept_button(get_ok_button());
- status_label = memnew(Label);
- status_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(status_label);
+ dir_path->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}
diff --git a/editor/directory_create_dialog.h b/editor/directory_create_dialog.h
index e2601181da..82e2e98ae5 100644
--- a/editor/directory_create_dialog.h
+++ b/editor/directory_create_dialog.h
@@ -33,9 +33,9 @@
#include "scene/gui/dialogs.h"
+class EditorValidationPanel;
class Label;
class LineEdit;
-class PanelContainer;
class DirectoryCreateDialog : public ConfirmationDialog {
GDCLASS(DirectoryCreateDialog, ConfirmationDialog);
@@ -44,17 +44,13 @@ class DirectoryCreateDialog : public ConfirmationDialog {
Label *label = nullptr;
LineEdit *dir_path = nullptr;
-
- PanelContainer *status_panel = nullptr;
- Label *status_label = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
String _validate_path(const String &p_path) const;
-
- void _on_dir_path_changed(const String &p_text);
+ void _on_dir_path_changed();
protected:
static void _bind_methods();
- void _notification(int p_what);
virtual void ok_pressed() override;
virtual void _post_popup() override;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index d95b1de365..7ac812101a 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_validation_panel.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/script_editor_plugin.h"
#include "multi_node_edit.h"
@@ -3927,12 +3928,6 @@ void EditorInspector::_notification(int p_what) {
}
} break;
- case NOTIFICATION_THEME_CHANGED: {
- if (add_meta_error_panel) {
- add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- }
- } break;
-
case NOTIFICATION_PREDELETE: {
edit(nullptr); //just in case
} break;
@@ -4083,27 +4078,17 @@ void EditorInspector::_add_meta_confirm() {
undo_redo->commit_action();
}
-void EditorInspector::_check_meta_name(const String &p_name) {
- String error;
-
- if (p_name == "") {
- error = TTR("Metadata name can't be empty.");
- } else if (!p_name.is_valid_identifier()) {
- error = TTR("Metadata name must be a valid identifier.");
- } else if (object->has_meta(p_name)) {
- error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name);
- } else if (p_name[0] == '_') {
- error = TTR("Names starting with _ are reserved for editor-only metadata.");
- }
+void EditorInspector::_check_meta_name() {
+ const String meta_name = add_meta_name->get_text();
- if (error != "") {
- add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- add_meta_error->set_text(error);
- add_meta_dialog->get_ok_button()->set_disabled(true);
- } else {
- add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- add_meta_error->set_text(TTR("Metadata name is valid."));
- add_meta_dialog->get_ok_button()->set_disabled(false);
+ if (meta_name.is_empty()) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR);
+ } else if (!meta_name.is_valid_identifier()) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR);
+ } else if (object->has_meta(meta_name)) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR);
+ } else if (meta_name[0] == '_') {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Names starting with _ are reserved for editor-only metadata."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -4143,16 +4128,13 @@ void EditorInspector::_show_add_meta_dialog() {
add_meta_dialog->register_text_enter(add_meta_name);
add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm));
- add_meta_error_panel = memnew(PanelContainer);
- vbc->add_child(add_meta_error_panel);
- if (is_inside_tree()) {
- add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- }
-
- add_meta_error = memnew(Label);
- add_meta_error_panel->add_child(add_meta_error);
+ validation_panel = memnew(EditorValidationPanel);
+ vbc->add_child(validation_panel);
+ validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name is valid."));
+ validation_panel->set_update_callback(callable_mp(this, &EditorInspector::_check_meta_name));
+ validation_panel->set_accept_button(add_meta_dialog->get_ok_button());
- add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name));
+ add_meta_name->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}
Node *node = Object::cast_to<Node>(object);
@@ -4164,9 +4146,9 @@ void EditorInspector::_show_add_meta_dialog() {
}
add_meta_dialog->popup_centered();
- add_meta_name->set_text("");
- _check_meta_name("");
add_meta_name->grab_focus();
+ add_meta_name->set_text("");
+ validation_panel->update();
}
void EditorInspector::_bind_methods() {
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 9a4c4f7f99..ed0d0ec373 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -39,6 +39,7 @@ class AcceptDialog;
class Button;
class ConfirmationDialog;
class EditorInspector;
+class EditorValidationPanel;
class LineEdit;
class OptionButton;
class PanelContainer;
@@ -543,12 +544,11 @@ class EditorInspector : public ScrollContainer {
ConfirmationDialog *add_meta_dialog = nullptr;
LineEdit *add_meta_name = nullptr;
OptionButton *add_meta_type = nullptr;
- PanelContainer *add_meta_error_panel = nullptr;
- Label *add_meta_error = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
void _add_meta_confirm();
void _show_add_meta_dialog();
- void _check_meta_name(const String &p_name);
+ void _check_meta_name();
protected:
static void _bind_methods();
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 1bc9f00f08..4c2f6b3176 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -212,7 +212,7 @@ void EditorLog::clear() {
_clear_request();
}
-void EditorLog::_process_message(const String &p_msg, MessageType p_type) {
+void EditorLog::_process_message(const String &p_msg, MessageType p_type, bool p_clear) {
if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg && messages[messages.size() - 1].type == p_type) {
// If previous message is the same as the new one, increase previous count rather than adding another
// instance to the messages list.
@@ -222,7 +222,7 @@ void EditorLog::_process_message(const String &p_msg, MessageType p_type) {
_add_log_line(previous, collapse);
} else {
// Different message to the previous one received.
- LogMessage message(p_msg, p_type);
+ LogMessage message(p_msg, p_type, p_clear);
_add_log_line(message);
messages.push_back(message);
}
@@ -237,9 +237,10 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
// search functionality (see the comments on the PR above for more details). This behavior
// also matches that of other IDE's.
Vector<String> lines = p_msg.split("\n", true);
+ int line_count = lines.size();
- for (int i = 0; i < lines.size(); i++) {
- _process_message(lines[i], p_type);
+ for (int i = 0; i < line_count; i++) {
+ _process_message(lines[i], p_type, i == line_count - 1);
}
}
@@ -338,7 +339,9 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
} else {
log->add_text(p_message.text);
}
- log->pop_all(); // Pop all unclosed tags.
+ if (p_message.clear || p_message.type != MSG_TYPE_STD_RICH) {
+ log->pop_all(); // Pop all unclosed tags.
+ }
log->add_newline();
if (p_replace_previous) {
diff --git a/editor/editor_log.h b/editor/editor_log.h
index b875066afa..07f3a25c3e 100644
--- a/editor/editor_log.h
+++ b/editor/editor_log.h
@@ -60,12 +60,14 @@ private:
String text;
MessageType type;
int count = 1;
+ bool clear = true;
LogMessage() {}
- LogMessage(const String p_text, MessageType p_type) :
+ LogMessage(const String p_text, MessageType p_type, bool p_clear) :
text(p_text),
- type(p_type) {
+ type(p_type),
+ clear(p_clear) {
}
};
@@ -166,7 +168,7 @@ private:
void _set_search_visible(bool p_visible);
void _search_changed(const String &p_text);
- void _process_message(const String &p_msg, MessageType p_type);
+ void _process_message(const String &p_msg, MessageType p_type, bool p_clear);
void _reset_message_counts();
void _set_collapse(bool p_collapse);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index e0a014ac1a..c6f5a6082b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -496,6 +496,9 @@ void EditorNode::_update_from_settings() {
Viewport::MSAA msaa = Viewport::MSAA(int(GLOBAL_GET("rendering/anti_aliasing/quality/msaa_2d")));
scene_root->set_msaa_2d(msaa);
+ bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
+ scene_root->set_use_hdr_2d(use_hdr_2d);
+
float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
scene_root->set_mesh_lod_threshold(mesh_lod_threshold);
@@ -2354,7 +2357,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
SceneTreeDock::get_singleton()->set_selected(current_node);
InspectorDock::get_singleton()->update(current_node);
if (!inspector_only && !skip_main_plugin) {
- skip_main_plugin = stay_in_script_editor_on_node_selected && ScriptEditor::get_singleton()->is_visible_in_tree();
+ skip_main_plugin = stay_in_script_editor_on_node_selected && !ScriptEditor::get_singleton()->is_editor_floating() && ScriptEditor::get_singleton()->is_visible_in_tree();
}
} else {
NodeDock::get_singleton()->set_node(nullptr);
@@ -5006,12 +5009,17 @@ void EditorNode::_load_editor_layout() {
config.instantiate();
Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
if (err != OK) { // No config.
- // If config is not found, expand the res:// folder by default.
+ // If config is not found, expand the res:// folder and favorites by default.
TreeItem *root = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("res://", 0);
if (root) {
root->set_collapsed(false);
}
+ TreeItem *favorites = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("Favorites", 0);
+ if (favorites) {
+ favorites->set_collapsed(false);
+ }
+
if (overridden_default_layout >= 0) {
_layout_menu_option(overridden_default_layout);
}
@@ -5673,12 +5681,13 @@ void EditorNode::_scene_tab_exit() {
}
void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
+ int tab_id = scene_tabs->get_hovered_tab();
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) {
- _scene_tab_closed(scene_tabs->get_hovered_tab());
+ _scene_tab_closed(tab_id);
}
} else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
int tab_buttons = 0;
@@ -5697,12 +5706,12 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->reset_size();
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE);
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE);
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE);
}
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES);
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE);
@@ -5716,7 +5725,13 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true);
}
scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS);
+ if (editor_data.get_edited_scene_count() <= 1) {
+ scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_OTHERS), true);
+ }
scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), FILE_CLOSE_RIGHT);
+ if (editor_data.get_edited_scene_count() == tab_id + 1) {
+ scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_RIGHT), true);
+ }
scene_tabs_context_menu->add_item(TTR("Close All Tabs"), FILE_CLOSE_ALL);
}
scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position());
@@ -6920,8 +6935,7 @@ EditorNode::EditorNode() {
{
// Register importers at the beginning, so dialogs are created with the right extensions.
- Ref<ResourceImporterTexture> import_texture;
- import_texture.instantiate();
+ Ref<ResourceImporterTexture> import_texture = memnew(ResourceImporterTexture(true));
ResourceFormatImporter::get_singleton()->add_importer(import_texture);
Ref<ResourceImporterLayeredTexture> import_cubemap;
@@ -6939,8 +6953,7 @@ EditorNode::EditorNode() {
import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);
- Ref<ResourceImporterLayeredTexture> import_3d;
- import_3d.instantiate();
+ Ref<ResourceImporterLayeredTexture> import_3d = memnew(ResourceImporterLayeredTexture(true));
import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D);
ResourceFormatImporter::get_singleton()->add_importer(import_3d);
@@ -6980,12 +6993,10 @@ EditorNode::EditorNode() {
import_shader_file.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(import_shader_file);
- Ref<ResourceImporterScene> import_scene;
- import_scene.instantiate();
+ Ref<ResourceImporterScene> import_scene = memnew(ResourceImporterScene(false, true));
ResourceFormatImporter::get_singleton()->add_importer(import_scene);
- Ref<ResourceImporterScene> import_animation;
- import_animation = Ref<ResourceImporterScene>(memnew(ResourceImporterScene(true)));
+ Ref<ResourceImporterScene> import_animation = memnew(ResourceImporterScene(true, true));
ResourceFormatImporter::get_singleton()->add_importer(import_animation);
{
@@ -7468,8 +7479,8 @@ EditorNode::EditorNode() {
export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option));
file_menu->add_separator();
- file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true);
- file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true);
+ file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true, true);
+ file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true, true);
file_menu->add_separator();
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE);
@@ -7848,7 +7859,6 @@ EditorNode::EditorNode() {
log = memnew(EditorLog);
Button *output_button = add_bottom_panel_item(TTR("Output"), log);
- output_button->set_theme_type_variation("BottomPanelButton");
log->set_tool_button(output_button);
center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized));
@@ -8000,8 +8010,8 @@ EditorNode::EditorNode() {
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
vcs_actions_menu->set_name("Version Control");
vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option));
- vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA);
- vcs_actions_menu->add_item(TTR("Version Control Settings"), RUN_VCS_SETTINGS);
+ vcs_actions_menu->add_item(TTR("Create Version Control Metadata..."), RUN_VCS_METADATA);
+ vcs_actions_menu->add_item(TTR("Version Control Settings..."), RUN_VCS_SETTINGS);
project_menu->add_child(vcs_actions_menu);
project_menu->set_item_submenu(project_menu->get_item_index(VCS_MENU), "Version Control");
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index fd14136fc7..a6cd07dd8b 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -269,9 +269,9 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["uri"] = "URI";
capitalize_string_remaps["url"] = "URL";
capitalize_string_remaps["urls"] = "URLs";
- capitalize_string_remaps["us"] = String::utf8("(µs)"); // Unit.
+ capitalize_string_remaps["us"] = U"(µs)"; // Unit.
capitalize_string_remaps["usb"] = "USB";
- capitalize_string_remaps["usec"] = String::utf8("(µsec)"); // Unit.
+ capitalize_string_remaps["usec"] = U"(µsec)"; // Unit.
capitalize_string_remaps["uuid"] = "UUID";
capitalize_string_remaps["uv"] = "UV";
capitalize_string_remaps["uv1"] = "UV1";
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 3302b1dc41..f904ed875d 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -176,12 +176,6 @@ void EditorColorMap::create() {
add_conversion_exception("Sky");
add_conversion_exception("EditorControlAnchor");
add_conversion_exception("DefaultProjectIcon");
- add_conversion_exception("GuiChecked");
- add_conversion_exception("GuiRadioChecked");
- add_conversion_exception("GuiIndeterminate");
- add_conversion_exception("GuiCloseCustomizable");
- add_conversion_exception("GuiGraphNodePort");
- add_conversion_exception("GuiResizer");
add_conversion_exception("ZoomMore");
add_conversion_exception("ZoomLess");
add_conversion_exception("ZoomReset");
@@ -191,6 +185,18 @@ void EditorColorMap::create() {
add_conversion_exception("StatusSuccess");
add_conversion_exception("StatusWarning");
add_conversion_exception("OverbrightIndicator");
+ add_conversion_exception("MaterialPreviewCube");
+ add_conversion_exception("MaterialPreviewSphere");
+ add_conversion_exception("MaterialPreviewLight1");
+ add_conversion_exception("MaterialPreviewLight2");
+
+ // GUI
+ add_conversion_exception("GuiChecked");
+ add_conversion_exception("GuiRadioChecked");
+ add_conversion_exception("GuiIndeterminate");
+ add_conversion_exception("GuiCloseCustomizable");
+ add_conversion_exception("GuiGraphNodePort");
+ add_conversion_exception("GuiResizer");
add_conversion_exception("GuiMiniCheckerboard");
/// Code Editor.
@@ -908,7 +914,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("icon_pressed_color", "Button", icon_pressed_color);
theme->set_color("icon_disabled_color", "Button", icon_disabled_color);
- theme->set_constant("h_separation", "Button", 2 * EDSCALE);
+ theme->set_constant("h_separation", "Button", 4 * EDSCALE);
theme->set_constant("outline_size", "Button", 0);
const float ACTION_BUTTON_EXTRA_MARGIN = 32 * EDSCALE;
@@ -944,6 +950,25 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
editor_log_button_pressed->set_border_color(accent_color);
theme->set_stylebox("pressed", "EditorLogFilterButton", editor_log_button_pressed);
+ // Buttons in material previews
+ const Color dim_light_color = icon_normal_color.darkened(0.24);
+ const Color dim_light_highlighted_color = icon_normal_color.darkened(0.18);
+ Ref<StyleBox> sb_empty_borderless = make_empty_stylebox();
+
+ theme->set_type_variation("PreviewLightButton", "Button");
+ // When pressed, don't use the accent color tint. When unpressed, dim the icon.
+ theme->set_color("icon_normal_color", "PreviewLightButton", dim_light_color);
+ theme->set_color("icon_focus_color", "PreviewLightButton", dim_light_color);
+ theme->set_color("icon_pressed_color", "PreviewLightButton", icon_normal_color);
+ theme->set_color("icon_hover_pressed_color", "PreviewLightButton", icon_normal_color);
+ // Unpressed icon is dim, so use a dim highlight.
+ theme->set_color("icon_hover_color", "PreviewLightButton", dim_light_highlighted_color);
+
+ theme->set_stylebox("normal", "PreviewLightButton", sb_empty_borderless);
+ theme->set_stylebox("hover", "PreviewLightButton", sb_empty_borderless);
+ theme->set_stylebox("focus", "PreviewLightButton", sb_empty_borderless);
+ theme->set_stylebox("pressed", "PreviewLightButton", sb_empty_borderless);
+
// ProjectTag
{
theme->set_type_variation("ProjectTag", "Button");
@@ -1255,6 +1280,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
}
theme->set_stylebox("panel", "Tree", style_tree_bg);
+ theme->set_stylebox("panel", "EditorValidationPanel", style_tree_bg);
// Tree
theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons")));
@@ -1453,9 +1479,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("panel", "TabContainer", style_content_panel);
// Bottom panel.
- theme->set_type_variation("BottomPanelButton", "Button");
- // Add separation for the warning/error icon.
- theme->set_constant("h_separation", "BottomPanelButton", 6 * EDSCALE);
Ref<StyleBoxFlat> style_bottom_panel = style_content_panel->duplicate();
style_bottom_panel->set_corner_radius_all(corner_radius * EDSCALE);
theme->set_stylebox("BottomPanel", "EditorStyles", style_bottom_panel);
@@ -1944,7 +1967,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("files_disabled", "FileDialog", font_disabled_color);
// ColorPicker
- theme->set_constant("margin", "ColorPicker", popup_margin_size);
+ theme->set_constant("margin", "ColorPicker", default_margin_size);
theme->set_constant("sv_width", "ColorPicker", 256 * EDSCALE);
theme->set_constant("sv_height", "ColorPicker", 256 * EDSCALE);
theme->set_constant("h_width", "ColorPicker", 30 * EDSCALE);
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 6c4fb480d7..670fd0a06d 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -32,7 +32,6 @@
#include "core/config/project_settings.h"
#include "core/io/config_file.h"
-#include "editor/import/resource_importer_texture_settings.h"
EditorExport *EditorExport::singleton = nullptr;
@@ -138,22 +137,6 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
}
}
-String EditorExportPlatform::test_etc2() const {
- if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
- return TTR("Target platform requires 'ETC2/ASTC' texture compression. Enable 'Import ETC2 ASTC' in Project Settings.") + "\n";
- }
-
- return String();
-}
-
-String EditorExportPlatform::test_bc() const {
- if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
- return TTR("Target platform requires 'S3TC/BPTC' texture compression. Enable 'Import S3TC BPTC' in Project Settings.") + "\n";
- }
-
- return String();
-}
-
int EditorExport::get_export_preset_count() const {
return export_presets.size();
}
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 763836e3ec..5f5702026c 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -237,8 +237,6 @@ public:
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
- String test_etc2() const;
- String test_bc() const;
bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 7c7762e0fd..61f875713f 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
#include "scene/gui/item_list.h"
@@ -51,6 +52,44 @@
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
+void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() {
+ ProjectSettings::get_singleton()->set_setting(setting_identifier, true);
+ ProjectSettings::get_singleton()->save();
+ EditorFileSystem::get_singleton()->scan_changes();
+ emit_signal("texture_format_enabled");
+}
+
+void ProjectExportTextureFormatError::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("texture_format_enabled"));
+}
+
+void ProjectExportTextureFormatError::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ texture_format_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ } break;
+ }
+}
+
+void ProjectExportTextureFormatError::show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier) {
+ texture_format_error_label->set_text(vformat(TTR("Target platform requires '%s' texture compression. Enable 'Import %s' to fix."), p_friendly_name, p_friendly_name.replace("/", " ")));
+ setting_identifier = p_setting_identifier;
+ show();
+}
+
+ProjectExportTextureFormatError::ProjectExportTextureFormatError() {
+ // Set up the label.
+ texture_format_error_label = memnew(Label);
+ add_child(texture_format_error_label);
+ // Set up the fix button.
+ fix_texture_format_button = memnew(LinkButton);
+ fix_texture_format_button->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ fix_texture_format_button->set_text(TTR("Fix Import"));
+ add_child(fix_texture_format_button);
+ fix_texture_format_button->connect("pressed", callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed));
+}
+
void ProjectExportDialog::_theme_changed() {
duplicate_preset->set_icon(presets->get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")));
delete_preset->set_icon(presets->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
@@ -300,6 +339,14 @@ void ProjectExportDialog::_edit_preset(int p_index) {
_update_export_all();
child_controls_changed();
+ if ((feature_set.has("s3tc") || feature_set.has("bptc")) && !ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
+ export_texture_format_error->show_for_texture_format("S3TC/BPTC", "rendering/textures/vram_compression/import_s3tc_bptc");
+ } else if ((feature_set.has("etc2") || feature_set.has("astc")) && !ResourceImporterTextureSettings::should_import_etc2_astc()) {
+ export_texture_format_error->show_for_texture_format("ETC2/ASTC", "rendering/textures/vram_compression/import_etc2_astc");
+ } else {
+ export_texture_format_error->hide();
+ }
+
String enc_in_filters_str = current->get_enc_in_filter();
String enc_ex_filters_str = current->get_enc_ex_filter();
if (!updating_enc_filters) {
@@ -343,29 +390,29 @@ void ProjectExportDialog::_update_feature_list() {
Ref<EditorExportPreset> current = get_current_preset();
ERR_FAIL_COND(current.is_null());
- RBSet<String> fset;
- List<String> features;
+ List<String> features_list;
- current->get_platform()->get_platform_features(&features);
- current->get_platform()->get_preset_features(current, &features);
+ current->get_platform()->get_platform_features(&features_list);
+ current->get_platform()->get_preset_features(current, &features_list);
String custom = current->get_custom_features();
Vector<String> custom_list = custom.split(",");
for (int i = 0; i < custom_list.size(); i++) {
String f = custom_list[i].strip_edges();
if (!f.is_empty()) {
- features.push_back(f);
+ features_list.push_back(f);
}
}
- for (const String &E : features) {
- fset.insert(E);
+ feature_set.clear();
+ for (const String &E : features_list) {
+ feature_set.insert(E);
}
custom_feature_display->clear();
String text;
bool first = true;
- for (const String &E : fset) {
+ for (const String &E : feature_set) {
if (!first) {
text += ", ";
} else {
@@ -1356,6 +1403,13 @@ ProjectExportDialog::ProjectExportDialog() {
add_child(export_pck_zip);
export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected));
+ // Export warnings and errors bottom section.
+
+ export_texture_format_error = memnew(ProjectExportTextureFormatError);
+ main_vb->add_child(export_texture_format_error);
+ export_texture_format_error->hide();
+ export_texture_format_error->connect("texture_format_enabled", callable_mp(this, &ProjectExportDialog::_update_current_preset));
+
export_error = memnew(Label);
main_vb->add_child(export_error);
export_error->hide();
@@ -1390,6 +1444,8 @@ ProjectExportDialog::ProjectExportDialog() {
export_templates_error->add_child(download_templates);
download_templates->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager));
+ // Export project file dialog.
+
export_project = memnew(EditorFileDialog);
export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(export_project);
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index e53e393432..4f76167e22 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -41,6 +41,7 @@ class EditorFileSystemDirectory;
class EditorInspector;
class EditorPropertyPath;
class ItemList;
+class LinkButton;
class MenuButton;
class OptionButton;
class PopupMenu;
@@ -49,6 +50,23 @@ class TabContainer;
class Tree;
class TreeItem;
+class ProjectExportTextureFormatError : public HBoxContainer {
+ GDCLASS(ProjectExportTextureFormatError, HBoxContainer);
+
+ Label *texture_format_error_label = nullptr;
+ LinkButton *fix_texture_format_button = nullptr;
+ String setting_identifier;
+ void _on_fix_texture_format_pressed();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier);
+ ProjectExportTextureFormatError();
+};
+
class ProjectExportDialog : public ConfirmationDialog {
GDCLASS(ProjectExportDialog, ConfirmationDialog);
@@ -86,12 +104,14 @@ private:
Button *export_all_button = nullptr;
AcceptDialog *export_all_dialog = nullptr;
+ RBSet<String> feature_set;
LineEdit *custom_features = nullptr;
RichTextLabel *custom_feature_display = nullptr;
LineEdit *script_key = nullptr;
Label *script_key_error = nullptr;
+ ProjectExportTextureFormatError *export_texture_format_error = nullptr;
Label *export_error = nullptr;
Label *export_warning = nullptr;
HBoxContainer *export_templates_error = nullptr;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 1ff68b7d36..751f1c575d 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2200,13 +2200,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_REIMPORT: {
- // Reimport all selected files.
- Vector<String> reimport;
- for (int i = 0; i < p_selected.size(); i++) {
- reimport.push_back(p_selected[i]);
- }
-
- ERR_FAIL_COND_MSG(reimport.size() == 0, "You need to select files to reimport them.");
+ ImportDock::get_singleton()->reimport_resources(p_selected);
} break;
case FILE_NEW_FOLDER: {
@@ -2830,6 +2824,37 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
if (!all_not_favorites) {
p_popup->add_icon_item(get_theme_icon(SNAME("NonFavorite"), SNAME("EditorIcons")), TTR("Remove from Favorites"), FILE_REMOVE_FAVORITE);
}
+
+ {
+ List<String> resource_extensions;
+ ResourceFormatImporter::get_singleton()->get_recognized_extensions_for_type("Resource", &resource_extensions);
+ HashSet<String> extension_list;
+ for (const String &extension : resource_extensions) {
+ extension_list.insert(extension);
+ }
+
+ bool resource_valid = true;
+ String main_extension;
+
+ for (int i = 0; i != p_paths.size(); ++i) {
+ String extension = p_paths[i].get_extension();
+ if (extension_list.has(extension)) {
+ if (main_extension.is_empty()) {
+ main_extension = extension;
+ } else if (extension != main_extension) {
+ resource_valid = false;
+ break;
+ }
+ } else {
+ resource_valid = false;
+ break;
+ }
+ }
+
+ if (resource_valid) {
+ p_popup->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Reimport"), FILE_REIMPORT);
+ }
+ }
}
if (p_paths.size() == 1) {
diff --git a/editor/gui/editor_validation_panel.cpp b/editor/gui/editor_validation_panel.cpp
new file mode 100644
index 0000000000..af15010b78
--- /dev/null
+++ b/editor/gui/editor_validation_panel.cpp
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/* editor_validation_panel.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "editor_validation_panel.h"
+
+#include "editor/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+
+void EditorValidationPanel::_update() {
+ for (const KeyValue<int, String> &E : valid_messages) {
+ set_message(E.key, E.value, MSG_OK);
+ }
+
+ valid = true;
+ update_callback.callv(Array());
+
+ if (accept_button) {
+ accept_button->set_disabled(!valid);
+ }
+ pending_update = false;
+}
+
+void EditorValidationPanel::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ theme_cache.valid_color = get_theme_color(SNAME("success_color"), SNAME("Editor"));
+ theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor"));
+ theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor"));
+ } break;
+ }
+}
+
+void EditorValidationPanel::add_line(int p_id, const String &p_valid_message) {
+ ERR_FAIL_COND(valid_messages.has(p_id));
+
+ Label *label = memnew(Label);
+ message_container->add_child(label);
+ label->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
+ label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
+
+ valid_messages[p_id] = p_valid_message;
+ labels[p_id] = label;
+}
+
+void EditorValidationPanel::set_accept_button(Button *p_button) {
+ accept_button = p_button;
+}
+
+void EditorValidationPanel::set_update_callback(const Callable &p_callback) {
+ update_callback = p_callback;
+}
+
+void EditorValidationPanel::update() {
+ ERR_FAIL_COND(update_callback.is_null());
+
+ if (pending_update) {
+ return;
+ }
+ pending_update = true;
+ callable_mp(this, &EditorValidationPanel::_update).call_deferred();
+}
+
+void EditorValidationPanel::set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix) {
+ ERR_FAIL_COND(!valid_messages.has(p_id));
+
+ Label *label = labels[p_id];
+ if (p_text.is_empty()) {
+ label->hide();
+ return;
+ }
+ label->show();
+
+ if (p_auto_prefix) {
+ label->set_text(String(U"• ") + p_text);
+ } else {
+ label->set_text(p_text);
+ }
+
+ switch (p_type) {
+ case MSG_OK:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.valid_color);
+ break;
+ case MSG_WARNING:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.warning_color);
+ break;
+ case MSG_ERROR:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.error_color);
+ valid = false;
+ break;
+ case MSG_INFO:
+ label->remove_theme_color_override(SNAME("font_color"));
+ break;
+ }
+}
+
+bool EditorValidationPanel::is_valid() const {
+ return valid;
+}
+
+EditorValidationPanel::EditorValidationPanel() {
+ set_v_size_flags(SIZE_EXPAND_FILL);
+
+ message_container = memnew(VBoxContainer);
+ add_child(message_container);
+}
diff --git a/editor/gui/editor_validation_panel.h b/editor/gui/editor_validation_panel.h
new file mode 100644
index 0000000000..c371795e15
--- /dev/null
+++ b/editor/gui/editor_validation_panel.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* editor_validation_panel.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 EDITOR_VALIDATION_PANEL_H
+#define EDITOR_VALIDATION_PANEL_H
+
+#include "scene/gui/panel_container.h"
+
+class Button;
+class Label;
+class VBoxContainer;
+
+class EditorValidationPanel : public PanelContainer {
+ GDCLASS(EditorValidationPanel, PanelContainer);
+
+public:
+ enum MessageType {
+ MSG_OK,
+ MSG_WARNING,
+ MSG_ERROR,
+ MSG_INFO,
+ };
+
+ static const int MSG_ID_DEFAULT = 0; // Avoids hard-coding ID in dialogs with single-line validation.
+
+private:
+ VBoxContainer *message_container = nullptr;
+
+ HashMap<int, String> valid_messages;
+ HashMap<int, Label *> labels;
+
+ bool valid = false;
+ bool pending_update = false;
+
+ struct ThemeCache {
+ Color valid_color;
+ Color warning_color;
+ Color error_color;
+ } theme_cache;
+
+ void _update();
+
+ Callable update_callback;
+ Button *accept_button = nullptr;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void add_line(int p_id, const String &p_valid_message = "");
+ void set_accept_button(Button *p_button);
+ void set_update_callback(const Callable &p_callback);
+
+ void update();
+ void set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix = true);
+ bool is_valid() const;
+
+ EditorValidationPanel();
+};
+
+#endif // EDITOR_VALIDATION_PANEL_H
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 7ca24d6168..d07e5dfa1a 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -294,7 +294,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
}
// Improve looks on tooltip, extra spacing on non-bullet point newlines.
- const String bullet_point = String::utf8("• ");
+ const String bullet_point = U"• ";
int next_newline = 0;
while (next_newline != -1) {
next_newline = conf_warning.find("\n", next_newline + 2);
diff --git a/editor/icons/Camera2D.svg b/editor/icons/Camera2D.svg
index 81e5cc2c8e..24198a8f06 100644
--- a/editor/icons/Camera2D.svg
+++ b/editor/icons/Camera2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#8da5f3"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg>
diff --git a/editor/icons/Camera3D.svg b/editor/icons/Camera3D.svg
index bf61aa48fc..69d435ff17 100644
--- a/editor/icons/Camera3D.svg
+++ b/editor/icons/Camera3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f" fill-opacity=".996078"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg>
diff --git a/editor/icons/CameraAttributes.svg b/editor/icons/CameraAttributes.svg
index 459c64e11c..72d88ace7a 100644
--- a/editor/icons/CameraAttributes.svg
+++ b/editor/icons/CameraAttributes.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#c38ef1"/></svg>
diff --git a/editor/icons/CameraAttributesPhysical.svg b/editor/icons/CameraAttributesPhysical.svg
new file mode 100644
index 0000000000..f75cd0dc44
--- /dev/null
+++ b/editor/icons/CameraAttributesPhysical.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#ffca5f"/></svg>
diff --git a/editor/icons/CameraAttributesPractical.svg b/editor/icons/CameraAttributesPractical.svg
new file mode 100644
index 0000000000..0aed99056d
--- /dev/null
+++ b/editor/icons/CameraAttributesPractical.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#5fb2ff"/></svg>
diff --git a/editor/icons/CameraTexture.svg b/editor/icons/CameraTexture.svg
index 145a817b7e..5a050b900e 100644
--- a/editor/icons/CameraTexture.svg
+++ b/editor/icons/CameraTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm5.818 1A1.65 1.65 0 0 0 7.19 5.514a1.65 1.65 0 1 0-1.641 2.752v1.19a.55.55 0 0 0 .545.544h3.27a.55.55 0 0 0 .544-.545V8.91L11.545 10V6.728L9.908 7.82v-.965a1.65 1.65 0 0 0-1.09-2.851z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m2 1c-.5522847 0-1 .4477153-1 1v12c0 .552285.4477153 1 1 1h12c.552285 0 1-.447715 1-1v-12c0-.5522847-.447715-1-1-1zm1 2h10v8h-10z"/><g stroke-width="1.97307" transform="matrix(.50682329 0 0 .50682329 3.994604 2.971119)"><path d="m5.9298703 2.0300582-1.9730742 3.934252-2.95288.01033s-1.00000003.000001-1.00000003 1v5.9004198s-.00049262 1.000376.99950633 1.000351l13.7985506-.000351s1-.000001 1-1v-5.9004195s-.000008-.9979028-1-1l-2.95288-.01033-1.9730741-3.934252zm2.0701297 4.8922972c1.6568542 0 3 1.3431458 3 3 0 1.6568536-1.3431458 2.9999996-3 2.9999996s-3-1.343146-3-2.9999996c0-1.6568542 1.3431458-3 3-3z"/><path d="m.0106475 2.0300582 3.9461486-.0000002v1.9730745h-3.9461486z"/></g></g></svg>
diff --git a/editor/icons/CompressedTexture2D.svg b/editor/icons/CompressedTexture2D.svg
index 54ff10b3c1..aa65696396 100644
--- a/editor/icons/CompressedTexture2D.svg
+++ b/editor/icons/CompressedTexture2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h6v-2h2v-2h-2-5v-8h5v-2zm6 2v2h2v-2zm2 0h2v-2h-2zm2 0v2h2v-2zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm-2-4v-2h-2v-1h-1v1h-1v1h-1v1h-1v1h2 2v-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h6v-2h2v-2H3V3h5V1zm6 2v2h2V3zm2 0h2V1h-2zm2 0v2h2V3zm0 2h-2v2h2zm0 2v2h2V7zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm-2-4V7H8V6H7v1H6v1H5v1H4v1h4V9z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CompressedTexture3D.svg b/editor/icons/CompressedTexture3D.svg
new file mode 100644
index 0000000000..0f027ad71b
--- /dev/null
+++ b/editor/icons/CompressedTexture3D.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h6v-2h2v-2H8v1H2.25V5H10V3H8v.75H3.25L5 2h3V1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5Zm6.675-8L8 6.5V7h2v2H8v1.75l-.325.25-1.5-.5-1.75.75-1.75-.75L4.25 8l.875 1.25ZM10 3h2V1h-2zm2 0v2h2V3zm0 2h-2v2h2zm0 2v2h2V7zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2z" fill="#e0e0e0"/><path d="M7.675 6v5L6 10.5l-.875-1.25zM4.25 8v3.25L2.5 10.5" fill="#000" fill-opacity=".4"/></svg>
diff --git a/editor/icons/CurveTexture.svg b/editor/icons/CurveTexture.svg
index 50232fdc4a..a5524f145c 100644
--- a/editor/icons/CurveTexture.svg
+++ b/editor/icons/CurveTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11c.331 0 .666-.008 1-.014V3h10v1.135a3 3 0 0 1 2-.006V2a1 1 0 0 0-1-1H2zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.967.988a1 1 0 0 0-.928.739c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-.994-1.287z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CurveXYZTexture.svg b/editor/icons/CurveXYZTexture.svg
new file mode 100644
index 0000000000..e376dd434b
--- /dev/null
+++ b/editor/icons/CurveXYZTexture.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v10a2 2 0 0 1 .75-.1Q3 11.9 3 10.62V3h10v6.5a2 2 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v.5a2 2 0 0 1 2.7.5h1.2a2 2 0 0 1 3.2 0h.9V8h-1V6h-1V5z" fill="#e0e0e0"/><g stroke-width="1.6" stroke-linecap="round" fill="none"><path d="M2 14.2q2.5 0 3-3" stroke="#ff5f5f"/><path d="M6.5 14.2q2.5 0 3-3" stroke="#5fff97"/><path d="M11 14.2q2.5 0 3-3" stroke="#5fb2ff"/></g></svg>
diff --git a/editor/icons/GizmoCamera3D.svg b/editor/icons/GizmoCamera3D.svg
new file mode 100644
index 0000000000..c22fa18ee4
--- /dev/null
+++ b/editor/icons/GizmoCamera3D.svg
@@ -0,0 +1 @@
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52.789062 12.285156-.068359.001953s-2.40509.20501-4.607422 1.613282c-2.202331 1.408271-4.620558 4.01711-6.382812 8.287109l-6.40625 15.527344h-.966797v-3.986328s.05404-2.69271-1.351563-5.503907c-1.405598-2.811197-5.028556-5.923828-10.076171-5.923828-5.047616 0-8.672527 3.112631-10.078126 5.923828s-1.351562 5.503907-1.351562 5.503907v4.796875c-2.8348528 1.102579-4.8904632 3.167645-5.8632812 5.113281-1.4055984 2.811197-1.3515624 5.503906-1.3515626 5.503906v59.427732c0 5.04762 3.1145841 8.67253 5.9257818 10.07813 2.811195 1.4056 5.503906 1.35156 5.503906 1.35156h96.820316c5.04761 0 8.67252-3.11458 10.07812-5.92578s1.35156-5.50391 1.35156-5.50391v-59.427732c0-5.047615-3.11458-8.672526-5.92578-10.078125-2.81119-1.405599-5.5039-1.349609-5.5039-1.349609h-19.859379l-6.40625-15.527344c-1.693114-4.102471-4.120301-6.670605-6.261719-8.09375-2.141416-1.423145-4.378906-1.779297-4.378906-1.779297l-.242187-.029297zm.201172 8h21.722657c.009-.0059.05394-.06976.867187.470703.864378.57445 2.152133 1.720094 3.292969 4.484375l8.451172 20.47461h25.210941s1.02269.05432 1.92578.505859c.90308.451543 1.5039.540926 1.5039 2.921875v59.427732s-.0563 1.0227-.50781 1.92578c-.45154.90309-.54093 1.50391-2.92187 1.50391h-96.820316s-1.022696-.0543-1.925782-.50586c-.903086-.45154-1.503906-.54288-1.503906-2.92383v-59.427732s.05627-1.022696.507813-1.925781c.451542-.903085.540927-1.501953 2.921875-1.501953h24.960937l8.451172-20.47461c1.241133-3.007304 2.535771-4.112924 3.296875-4.599609.756188-.483541.568815-.357095.566406-.355469zm12.009766 24c-18.572491 0-33.714844 15.142353-33.714844 33.714844s15.142352 33.71484 33.714844 33.71484 33.714844-15.142349 33.714844-33.71484-15.142353-33.714844-33.714844-33.714844zm0 8c14.24897 0 25.714844 11.465874 25.714844 25.714844s-11.465875 25.71484-25.714844 25.71484-25.714844-11.46587-25.714844-25.71484 11.465874-25.714844 25.714844-25.714844z" fill-opacity=".3"/><g fill="#f7f5cf" stroke-width="8.00001"><path d="m52.857144 16.285714s-4.425182.151262-7.428572 7.428572l-7.428571 17.999668h-22.285715c-7.4285711 0-7.4285711 7.428572-7.4285711 7.428572v59.428574c0 7.42857 7.4285711 7.42857 7.4285711 7.42857h96.821434c7.42857 0 7.42857-7.42857 7.42857-7.42857v-59.428574c0-7.428572-7.42857-7.428572-7.42857-7.428572h-22.535719l-7.428571-17.999668c-2.833953-6.866759-7.428572-7.428572-7.428572-7.428572zm12.142856 31.999999c16.410747 0 29.714286 13.303539 29.714286 29.714286s-13.303539 29.714291-29.714286 29.714291-29.714286-13.303544-29.714286-29.714291 13.303539-29.714286 29.714286-29.714286z"/><path d="m15.500291 33.728577h14.857143s0-7.428572-7.428571-7.428572c-7.428572 0-7.428572 7.428572-7.428572 7.428572z"/></g></svg>
diff --git a/editor/icons/ImageTexture.svg b/editor/icons/ImageTexture.svg
index 25d4b53d00..17fc57d31d 100644
--- a/editor/icons/ImageTexture.svg
+++ b/editor/icons/ImageTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-12a1 1 0 0 0 -1-1zm1 2h10v8h-10zm6 2v1h-1v1h-2v1h-1v1h-1v1h2 2 2 2v-2h-1v-2h-1v-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm6 2v1H8v1H6v1H5v1H4v1h8V8h-1V6h-1V5z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ImageTexture3D.svg b/editor/icons/ImageTexture3D.svg
new file mode 100644
index 0000000000..7cb4d46f8d
--- /dev/null
+++ b/editor/icons/ImageTexture3D.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6V4.75L14 3v6.25zm-1-7.25h-8L5 2h8zM7.675 6l3 4-3 1-1.5-.5-1.75.75-1.75-.75L4.25 8l.875 1.25z" fill="#e0e0e0"/><path d="M7.675 6v5L6 10.5l-.875-1.25zM4.25 8v3.25L2.5 10.5" fill="#000" fill-opacity=".4"/></svg>
diff --git a/editor/icons/MaterialPreviewCube.svg b/editor/icons/MaterialPreviewCube.svg
index e7e56d02aa..68d4595bc2 100644
--- a/editor/icons/MaterialPreviewCube.svg
+++ b/editor/icons/MaterialPreviewCube.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/><path d="m8 15 7-3V4L8 7z" fill="#d6d6d6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1 7 3v8l-7 3-7-3V4z" fill="none" stroke="#000" stroke-opacity=".8" stroke-width="2" stroke-linejoin="round"/><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#f9f9f9"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MaterialPreviewCubeOff.svg b/editor/icons/MaterialPreviewCubeOff.svg
deleted file mode 100644
index ccf8a1e8f6..0000000000
--- a/editor/icons/MaterialPreviewCubeOff.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/><path d="m8 15 7-3V4L8 7z" fill="#d6d6d6"/><path d="M8 1 1 4v8l7 3 7-3V4z" fill-opacity=".235"/></svg>
diff --git a/editor/icons/MaterialPreviewLight1.svg b/editor/icons/MaterialPreviewLight1.svg
index 5e0f7539c4..ef21a4ecd7 100644
--- a/editor/icons/MaterialPreviewLight1.svg
+++ b/editor/icons/MaterialPreviewLight1.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v6H8V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-width="2" stroke-opacity=".8" stroke-linejoin="round"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z"/><circle cx="8" cy="8" r="3.5" fill="#000"/></g><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2zm1-7.875a.75.75 0 0 1 1.145.625V11h-1.5V7.152l-1 .667-.75-1.2z" fill="#f9f9f9"/></svg>
diff --git a/editor/icons/MaterialPreviewLight1Off.svg b/editor/icons/MaterialPreviewLight1Off.svg
deleted file mode 100644
index 47f9ee83c4..0000000000
--- a/editor/icons/MaterialPreviewLight1Off.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v6H8V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0" fill-opacity=".69"/></svg>
diff --git a/editor/icons/MaterialPreviewLight2.svg b/editor/icons/MaterialPreviewLight2.svg
index 7370caf17f..7714864840 100644
--- a/editor/icons/MaterialPreviewLight2.svg
+++ b/editor/icons/MaterialPreviewLight2.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v3H7v1h2v1H6V7h2V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-width="2" stroke-opacity=".8" stroke-linejoin="round"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z"/><circle cx="8" cy="8" r="3.5" fill="#000"/></g><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2zM5.9 6.45a1.98 1.98 0 1 1 3.4 1.8L8.05 9.5h2V11H6.8a.9.9 0 0 1-.75-1.5L8.3 7.25a.62.62 0 1 0-1.1-.45z" fill="#f9f9f9"/></svg>
diff --git a/editor/icons/MaterialPreviewLight2Off.svg b/editor/icons/MaterialPreviewLight2Off.svg
deleted file mode 100644
index 2cbfa4f176..0000000000
--- a/editor/icons/MaterialPreviewLight2Off.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v3H7v1h2v1H6V7h2V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0" fill-opacity=".69"/></svg>
diff --git a/editor/icons/MaterialPreviewSphere.svg b/editor/icons/MaterialPreviewSphere.svg
index 949d1ef3a7..e3c58fefe3 100644
--- a/editor/icons/MaterialPreviewSphere.svg
+++ b/editor/icons/MaterialPreviewSphere.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="8" fill="#000" fill-opacity=".8"/><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 7a2 2 0 0 1 0-4 2 2 0 0 1 0 4" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MaterialPreviewSphereOff.svg b/editor/icons/MaterialPreviewSphereOff.svg
deleted file mode 100644
index 5ebd36f85f..0000000000
--- a/editor/icons/MaterialPreviewSphereOff.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill="#e0e0e0"/><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill-opacity=".23529"/></svg>
diff --git a/editor/icons/NavigationAgent2D.svg b/editor/icons/NavigationAgent2D.svg
index 09b9b35262..ad11096afc 100644
--- a/editor/icons/NavigationAgent2D.svg
+++ b/editor/icons/NavigationAgent2D.svg
@@ -1 +1 @@
-<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5.007-6.03 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/NavigationAgent3D.svg b/editor/icons/NavigationAgent3D.svg
index cc34201f9b..6f887611a9 100644
--- a/editor/icons/NavigationAgent3D.svg
+++ b/editor/icons/NavigationAgent3D.svg
@@ -1 +1 @@
-<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1.086c-.845.156-1.476.514-1.94.988L4.05 4.059Q4 8 7 9.879V15l1-1z" fill="#e0e0e0" fill-opacity=".5"/><path d="M7 3C4 3 3 5 3 7s2 5 4 8c.338-.507.672-1.012 1-1.514v-4.76a2 2 0 1 1 0-3.453V3.084A5.663 5.663 0 0 0 7 3z" fill="#e0e0e0"/><g fill="#fc7f7f"><path d="M9 1c-.363 0-.695.03-1 .086V14l1-1c2-3 4-6 4-8s-1-4-4-4z" fill-opacity=".5"/><path d="M8 3.084v2.19a2 2 0 0 1 0 3.453v4.76c1.615-2.47 3-4.825 3-6.487 0-1.759-.774-3.517-3-3.916z"/></g></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/NavigationObstacle3D.svg b/editor/icons/NavigationObstacle3D.svg
index 2f6f198aab..52080bf3fc 100644
--- a/editor/icons/NavigationObstacle3D.svg
+++ b/editor/icons/NavigationObstacle3D.svg
@@ -1 +1 @@
-<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="M4.607 8.379C2.81 9.307 1 10.45 1 11c0 1 6 4 7 4v-2c-2.5 0-5-1-4-3z" fill-opacity=".5"/><path d="M8 .875c-.375 0-.75.375-1 1.125l-3 8c-1 2 1.5 3 4 3V9.25c-.875 0-1.75-.25-2.5-.75l1-3.5c.5.25 1 .375 1.5.375z"/></g><g fill="#fc7f7f"><path d="M11.393 8.379 12 10c1.002 2.005-1.512 3.005-4.018 3v1.998L8 15c1 0 7-3 7-4 0-.549-1.81-1.693-3.607-2.621z" fill-opacity=".5"/><path d="m8 .875-.018.002v4.498A3.323 3.323 0 0 0 9.5 5l1 3.5a4.508 4.508 0 0 1-2.518.75V13c2.506.005 5.02-.995 4.018-3L9 2C8.75 1.25 8.375.875 8 .875z"/></g></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 .875C7.375.875 6.75 1.25 6.5 2l-3 10H8V9H5.5l1-4H8zM2 13c-1 0-1 2 0 2h6v-2z" fill="#e0e0e0"/><path d="M8 .875V5h1.5l1 4H8v3h4.5l-3-10C9.25 1.25 8.625.875 8 .875zM8 13v2h6c1 0 1-2 0-2z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/PlaceholderMaterial.svg b/editor/icons/PlaceholderMaterial.svg
new file mode 100644
index 0000000000..3b5a803e05
--- /dev/null
+++ b/editor/icons/PlaceholderMaterial.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient x2="0" y2="16" gradientUnits="userSpaceOnUse" id="a"><stop offset=".188" stop-color="#ff4545"/><stop stop-color="#ffe345"/><stop offset=".313" stop-color="#ffe345"/><stop stop-color="#80ff45"/><stop offset=".438" stop-color="#80ff45"/><stop stop-color="#45ffa2"/><stop offset=".563" stop-color="#45ffa2"/><stop stop-color="#45d7ff"/><stop offset=".688" stop-color="#45d7ff"/><stop stop-color="#8045ff"/><stop offset=".813" stop-color="#8045ff"/><stop stop-color="#ff4596"/></linearGradient><g stroke="url(#a)"><path stroke-linejoin="round" fill="none" stroke-width="2" d="M3 3h10v10H3z"/><path d="M7 5.5 5.5 7m5-1.5-5 5m5-1.5L9 10.5" stroke-linecap="round" stroke-width="1.5"/></g></svg>
diff --git a/editor/icons/PlaceholderMesh.svg b/editor/icons/PlaceholderMesh.svg
new file mode 100644
index 0000000000..c36156eb6f
--- /dev/null
+++ b/editor/icons/PlaceholderMesh.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4z" fill="#ffca5f"/><path d="m7.25 5.25-2 2M10.5 5.5l-5 5m5.25-1.75-2 2" stroke-width="1.5" stroke-linecap="round" stroke="#ffca5f"/></svg>
diff --git a/editor/icons/PlaceholderTexture2D.svg b/editor/icons/PlaceholderTexture2D.svg
new file mode 100644
index 0000000000..3e7a3754fe
--- /dev/null
+++ b/editor/icons/PlaceholderTexture2D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3z" fill="#e0e0e0"/><path d="M7 4.25 4.25 7m6.5-2.75-5.5 5.5M11.75 7 9 9.75" stroke-width="1.5" stroke-linecap="round" stroke="#e0e0e0"/></svg>
diff --git a/editor/icons/PlaceholderTexture3D.svg b/editor/icons/PlaceholderTexture3D.svg
new file mode 100644
index 0000000000..93014b92ac
--- /dev/null
+++ b/editor/icons/PlaceholderTexture3D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13z" fill="#e0e0e0"/><path d="M5.25 6.25 3.5 8m5.375-1.75-4.5 4.5M9.75 9 8 10.75" stroke-width="1.5" stroke-linecap="round" stroke="#e0e0e0"/></svg>
diff --git a/editor/icons/PortableCompressedTexture2D.svg b/editor/icons/PortableCompressedTexture2D.svg
new file mode 100644
index 0000000000..3f63ddae51
--- /dev/null
+++ b/editor/icons/PortableCompressedTexture2D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h7v-4H3V3h6V1zm6 5v1H6v1H5v1H4v1h5V6zm3-5h4v4h-4Zm0 5h4v4h-4Zm0 5h4v4h-4Z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ProxyTexture.svg b/editor/icons/ProxyTexture.svg
deleted file mode 100644
index 5fe39f4da8..0000000000
--- a/editor/icons/ProxyTexture.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1v4h4V1zm6 0v2h6v8H7v4h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm2 4v1H8v1H7v3h5V8h-1V6h-1V5zM1 6v4h4V6zm0 5v4h4v-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Texture3D.svg b/editor/icons/Texture3D.svg
deleted file mode 100644
index ca80bcc76c..0000000000
--- a/editor/icons/Texture3D.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm1.973 7.075c-.134 0-.275-.012-.424-.036a3.3 3.3 0 0 1-.432-.08 4.066 4.066 0 0 1-.381-.115 2.025 2.025 0 0 1-.268-.115l.216-1.1c.129.065.293.136.492.213.204.071.455.106.753.106.342 0 .593-.076.752-.23s.239-.361.239-.621a.806.806 0 0 0-.09-.399.643.643 0 0 0-.231-.266.851.851 0 0 0-.357-.142 2.047 2.047 0 0 0-.432-.044h-.433V6.182h.492c.109 0 .214-.012.313-.036a.813.813 0 0 0 .276-.115.611.611 0 0 0 .186-.23.835.835 0 0 0 .075-.373c0-.112-.02-.21-.06-.292a.586.586 0 0 0-.157-.204.524.524 0 0 0-.216-.116.709.709 0 0 0-.246-.044 1.53 1.53 0 0 0-.596.115 2.401 2.401 0 0 0-.491.284l-.395-.966a2.774 2.774 0 0 1 1.043-.497c.149-.035.305-.053.469-.053.303 0 .564.044.783.133.223.083.407.204.551.363.144.154.251.337.321.55.069.207.104.435.104.683 0 .242-.057.479-.172.709a1.248 1.248 0 0 1-.462.515c.269.13.475.325.619.585.149.254.223.561.223.922a2.5 2.5 0 0 1-.119.789 1.645 1.645 0 0 1-.372.621 1.801 1.801 0 0 1-.649.408 2.703 2.703 0 0 1-.924.142zm4.295-1.26.186.018h.261c.581 0 1.011-.174 1.289-.523.284-.349.425-.831.425-1.445 0-.645-.134-1.132-.402-1.463-.269-.331-.693-.497-1.274-.497-.08 0-.162.003-.246.009-.085 0-.164.006-.239.018zm3.361-1.95c0 .532-.07.996-.209 1.392s-.338.724-.596.984c-.253.26-.564.455-.931.585-.368.13-.78.195-1.237.195-.209 0-.452-.011-.731-.035a4.777 4.777 0 0 1-.819-.124V3.876c.268-.059.546-.097.834-.115.293-.023.544-.035.753-.035.442 0 .842.059 1.2.177.362.118.673.305.931.559s.457.579.596.975.209.872.209 1.428z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/UndoRedo.svg b/editor/icons/UndoRedo.svg
new file mode 100644
index 0000000000..459efeba39
--- /dev/null
+++ b/editor/icons/UndoRedo.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 9H1V2l2 2a3.875 5 30 0 1 9 11 3.5 5 10 0 0-6-8z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/XRCamera3D.svg b/editor/icons/XRCamera3D.svg
index 501a86a9f7..88e11cfe34 100644
--- a/editor/icons/XRCamera3D.svg
+++ b/editor/icons/XRCamera3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 0a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9l3 2V5l-3 2V5.23A3 3 0 0 0 9.5 0zM4 12v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1h1v-1h1v1h1v-1a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-1H6v1H5v-1zm5 0v4h1v-1h1v1h1v-1a1 1 0 0 0-.137-.5A1 1 0 0 0 12 14v-1a1 1 0 0 0-1-1zm1 1h1v1h-1z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><g fill-opacity=".996078" transform="translate(0 -2)"><path d="m7 2-2 3h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-3zm1 4c1.6568542 0 3 1.3431458 3 3 0 1.656854-1.3431458 3-3 3s-3-1.343146-3-3c0-1.6568542 1.3431458-3 3-3z"/><path d="m2 2h2v2h-2z"/></g><path d="m4 12v1c0 .552285.4477153 1 1 1-.5522847 0-1 .447715-1 1v1h1v-1h1v1h1v-1c0-.552285-.4477153-1-1-1 .5522847 0 1-.447715 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.000919-.175812-.04817-.348275-.137-.5.08883-.151725.136081-.324188.137-.5v-1c0-.552285-.447715-1-1-1zm1 1h1v1h-1z"/></g></svg>
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 3c27864eff..46882e95cb 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -474,12 +474,19 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p
ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = nullptr;
-ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() {
- singleton = this;
+ResourceImporterLayeredTexture::ResourceImporterLayeredTexture(bool p_singleton) {
+ // This should only be set through the EditorNode.
+ if (p_singleton) {
+ singleton = this;
+ }
+
mode = MODE_CUBEMAP;
}
ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
+ if (singleton == this) {
+ singleton = nullptr;
+ }
}
void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import) {
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 52fd37639d..5a21651de3 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -119,7 +119,7 @@ public:
void set_mode(Mode p_mode) { mode = p_mode; }
- ResourceImporterLayeredTexture();
+ ResourceImporterLayeredTexture(bool p_singleton = false);
~ResourceImporterLayeredTexture();
};
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index b0291f3a93..2e422d8c27 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -2607,15 +2607,28 @@ void ResourceImporterScene::ResourceImporterScene::show_advanced_options(const S
SceneImportSettings::get_singleton()->open_settings(p_path, animation_importer);
}
-ResourceImporterScene::ResourceImporterScene(bool p_animation_import) {
- if (p_animation_import) {
- animation_singleton = this;
- } else {
- scene_singleton = this;
+ResourceImporterScene::ResourceImporterScene(bool p_animation_import, bool p_singleton) {
+ // This should only be set through the EditorNode.
+ if (p_singleton) {
+ if (p_animation_import) {
+ animation_singleton = this;
+ } else {
+ scene_singleton = this;
+ }
}
+
animation_importer = p_animation_import;
}
+ResourceImporterScene::~ResourceImporterScene() {
+ if (animation_singleton == this) {
+ animation_singleton = nullptr;
+ }
+ if (scene_singleton == this) {
+ scene_singleton = nullptr;
+ }
+}
+
void ResourceImporterScene::add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) {
ERR_FAIL_COND(p_importer.is_null());
if (p_first_priority) {
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index a66fd034f8..7a07a5f646 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -296,7 +296,8 @@ public:
virtual bool can_import_threaded() const override { return false; }
- ResourceImporterScene(bool p_animation_import = false);
+ ResourceImporterScene(bool p_animation_import = false, bool p_singleton = false);
+ ~ResourceImporterScene();
template <class M>
static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 114ba5653a..8eac5ec323 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -789,10 +789,12 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr;
-ResourceImporterTexture::ResourceImporterTexture() {
- if (!singleton) {
+ResourceImporterTexture::ResourceImporterTexture(bool p_singleton) {
+ // This should only be set through the EditorNode.
+ if (p_singleton) {
singleton = this;
}
+
CompressedTexture2D::request_3d_callback = _texture_reimport_3d;
CompressedTexture2D::request_roughness_callback = _texture_reimport_roughness;
CompressedTexture2D::request_normal_callback = _texture_reimport_normal;
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index c2bdbb6fa2..f2539a8f52 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -107,7 +107,7 @@ public:
virtual bool are_import_settings_valid(const String &p_path) const override;
virtual String get_import_settings_string() const override;
- ResourceImporterTexture();
+ ResourceImporterTexture(bool p_singleton = false);
~ResourceImporterTexture();
};
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index a17f497725..fcd2d8f908 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -324,6 +324,21 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
}
}
+void ImportDock::reimport_resources(const Vector<String> &p_paths) {
+ switch (p_paths.size()) {
+ case 0:
+ ERR_FAIL_MSG("You need to select files to reimport them.");
+ case 1:
+ set_edit_path(p_paths[0]);
+ break;
+ default:
+ set_edit_multiple_paths(p_paths);
+ break;
+ }
+
+ _reimport_attempt();
+}
+
void ImportDock::_update_preset_menu() {
preset->get_popup()->clear();
diff --git a/editor/import_dock.h b/editor/import_dock.h
index 07c54f8beb..78cd6a556c 100644
--- a/editor/import_dock.h
+++ b/editor/import_dock.h
@@ -102,6 +102,7 @@ protected:
public:
void set_edit_path(const String &p_path);
void set_edit_multiple_paths(const Vector<String> &p_paths);
+ void reimport_resources(const Vector<String> &p_paths);
void initialize_import_options() const;
void clear();
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index 97398b8b69..7d7b156bd0 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "editor/editor_scale.h"
+#include "editor/gui/editor_validation_panel.h"
#include "editor/project_settings_editor.h"
#include "scene/gui/grid_container.h"
@@ -96,52 +97,28 @@ void PluginConfigDialog::_on_canceled() {
_clear_fields();
}
-void PluginConfigDialog::_on_language_changed(const int) {
- _on_required_text_changed(String());
-}
-
-void PluginConfigDialog::_on_required_text_changed(const String &) {
+void PluginConfigDialog::_on_required_text_changed() {
int lang_idx = script_option_edit->get_selected();
String ext = ScriptServer::get_language(lang_idx)->get_extension();
- Ref<Texture2D> valid_icon = get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons"));
- Ref<Texture2D> invalid_icon = get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"));
-
- // Set variables to assume all is valid
- bool is_valid = true;
- name_validation->set_texture(valid_icon);
- subfolder_validation->set_texture(valid_icon);
- script_validation->set_texture(valid_icon);
- name_validation->set_tooltip_text("");
- subfolder_validation->set_tooltip_text("");
- script_validation->set_tooltip_text("");
-
- // Change valid status to invalid depending on conditions.
- Vector<String> errors;
if (name_edit->get_text().is_empty()) {
- is_valid = false;
- name_validation->set_texture(invalid_icon);
- name_validation->set_tooltip_text(TTR("Plugin name cannot be blank."));
+ validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR);
}
if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
- is_valid = false;
- script_validation->set_texture(invalid_icon);
- script_validation->set_tooltip_text(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext));
+ validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
}
- if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
- is_valid = false;
- subfolder_validation->set_texture(invalid_icon);
- subfolder_validation->set_tooltip_text(TTR("Subfolder name is not a valid folder name."));
- } else {
- String path = "res://addons/" + _get_subfolder();
- if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode.
- is_valid = false;
- subfolder_validation->set_texture(invalid_icon);
- subfolder_validation->set_tooltip_text(TTR("Subfolder cannot be one which already exists."));
+ if (subfolder_edit->is_visible()) {
+ if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
+ validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR);
+ } else {
+ String path = "res://addons/" + _get_subfolder();
+ if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode.
+ validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder cannot be one which already exists."), EditorValidationPanel::MSG_ERROR);
+ }
}
+ } else {
+ validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK);
}
-
- get_ok_button()->set_disabled(!is_valid);
}
String PluginConfigDialog::_get_subfolder() {
@@ -182,23 +159,20 @@ void PluginConfigDialog::config(const String &p_config_path) {
_edit_mode = true;
active_edit->hide();
- Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->hide();
+ Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->hide();
subfolder_edit->hide();
- subfolder_validation->hide();
- Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->hide();
+ Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->hide();
set_title(TTR("Edit a Plugin"));
} else {
_clear_fields();
_edit_mode = false;
active_edit->show();
- Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->show();
+ Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->show();
subfolder_edit->show();
- subfolder_validation->show();
- Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->show();
+ Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->show();
set_title(TTR("Create a Plugin"));
}
- // Simulate text changing so the errors populate.
- _on_required_text_changed("");
+ validation_panel->update();
get_ok_button()->set_disabled(!_edit_mode);
set_ok_button_text(_edit_mode ? TTR("Update") : TTR("Create"));
@@ -218,7 +192,7 @@ PluginConfigDialog::PluginConfigDialog() {
add_child(vbox);
GridContainer *grid = memnew(GridContainer);
- grid->set_columns(3);
+ grid->set_columns(2);
grid->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbox->add_child(grid);
@@ -228,12 +202,7 @@ PluginConfigDialog::PluginConfigDialog() {
name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(name_lb);
- name_validation = memnew(TextureRect);
- name_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- grid->add_child(name_validation);
-
name_edit = memnew(LineEdit);
- name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
name_edit->set_placeholder("MyPlugin");
name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(name_edit);
@@ -244,14 +213,9 @@ PluginConfigDialog::PluginConfigDialog() {
subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(subfolder_lb);
- subfolder_validation = memnew(TextureRect);
- subfolder_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- grid->add_child(subfolder_validation);
-
subfolder_edit = memnew(LineEdit);
subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin");
subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
grid->add_child(subfolder_edit);
// Description
@@ -260,9 +224,6 @@ PluginConfigDialog::PluginConfigDialog() {
desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(desc_lb);
- Control *desc_spacer = memnew(Control);
- grid->add_child(desc_spacer);
-
desc_edit = memnew(TextEdit);
desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
@@ -276,9 +237,6 @@ PluginConfigDialog::PluginConfigDialog() {
author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(author_lb);
- Control *author_spacer = memnew(Control);
- grid->add_child(author_spacer);
-
author_edit = memnew(LineEdit);
author_edit->set_placeholder("Godette");
author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -290,9 +248,6 @@ PluginConfigDialog::PluginConfigDialog() {
version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(version_lb);
- Control *version_spacer = memnew(Control);
- grid->add_child(version_spacer);
-
version_edit = memnew(LineEdit);
version_edit->set_placeholder("1.0");
version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -304,9 +259,6 @@ PluginConfigDialog::PluginConfigDialog() {
script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_option_lb);
- Control *script_opt_spacer = memnew(Control);
- grid->add_child(script_opt_spacer);
-
script_option_edit = memnew(OptionButton);
int default_lang = 0;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@@ -318,7 +270,6 @@ PluginConfigDialog::PluginConfigDialog() {
}
script_option_edit->select(default_lang);
grid->add_child(script_option_edit);
- script_option_edit->connect("item_selected", callable_mp(this, &PluginConfigDialog::_on_language_changed));
// Plugin Script Name
Label *script_lb = memnew(Label);
@@ -326,12 +277,7 @@ PluginConfigDialog::PluginConfigDialog() {
script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_lb);
- script_validation = memnew(TextureRect);
- script_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- grid->add_child(script_validation);
-
script_edit = memnew(LineEdit);
- script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd");
script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(script_edit);
@@ -343,12 +289,26 @@ PluginConfigDialog::PluginConfigDialog() {
active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(active_lb);
- Control *active_spacer = memnew(Control);
- grid->add_child(active_spacer);
-
active_edit = memnew(CheckBox);
active_edit->set_pressed(true);
grid->add_child(active_edit);
+
+ Control *spacing = memnew(Control);
+ vbox->add_child(spacing);
+ spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
+
+ validation_panel = memnew(EditorValidationPanel);
+ vbox->add_child(validation_panel);
+ 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->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
+ validation_panel->set_accept_button(get_ok_button());
+
+ script_option_edit->connect("item_selected", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ subfolder_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ script_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}
PluginConfigDialog::~PluginConfigDialog() {
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index 50b901a39e..1221d347a7 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -35,12 +35,21 @@
#include "scene/gui/dialogs.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
+#include "scene/gui/panel_container.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/texture_rect.h"
+class EditorValidationPanel;
+
class PluginConfigDialog : public ConfirmationDialog {
GDCLASS(PluginConfigDialog, ConfirmationDialog);
+ enum {
+ MSG_ID_PLUGIN,
+ MSG_ID_SUBFOLDER,
+ MSG_ID_SCRIPT,
+ };
+
LineEdit *name_edit = nullptr;
LineEdit *subfolder_edit = nullptr;
TextEdit *desc_edit = nullptr;
@@ -50,17 +59,14 @@ class PluginConfigDialog : public ConfirmationDialog {
LineEdit *script_edit = nullptr;
CheckBox *active_edit = nullptr;
- TextureRect *name_validation = nullptr;
- TextureRect *subfolder_validation = nullptr;
- TextureRect *script_validation = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
bool _edit_mode = false;
void _clear_fields();
void _on_confirmed();
void _on_canceled();
- void _on_language_changed(const int p_language);
- void _on_required_text_changed(const String &p_text);
+ void _on_required_text_changed();
String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/editor/plugins/bit_map_editor_plugin.cpp b/editor/plugins/bit_map_editor_plugin.cpp
index 3388cab006..f2423cb803 100644
--- a/editor/plugins/bit_map_editor_plugin.cpp
+++ b/editor/plugins/bit_map_editor_plugin.cpp
@@ -37,7 +37,7 @@
void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) {
texture_rect->set_texture(ImageTexture::create_from_image(p_bitmap->convert_to_image()));
- size_label->set_text(vformat(String::utf8("%s×%s"), p_bitmap->get_size().width, p_bitmap->get_size().height));
+ size_label->set_text(vformat(U"%s×%s", p_bitmap->get_size().width, p_bitmap->get_size().height));
}
BitMapEditor::BitMapEditor() {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 0386a05cc2..c9c67f3d6b 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1289,7 +1289,7 @@ void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_eve
void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- // Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two.
+ // Special behavior for scroll events, as the zoom_by_increment method can smartly end up on powers of two.
int increment = p_zoom_factor > 1.0 ? 1 : -1;
bool by_integer = mb->is_alt_pressed();
@@ -2282,8 +2282,8 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) {
add_node_menu->clear();
- add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here"), ADD_NODE);
- add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here"), ADD_INSTANCE);
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here..."), ADD_NODE);
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here..."), ADD_INSTANCE);
for (Node *node : SceneTreeDock::get_singleton()->get_node_clipboard()) {
if (Object::cast_to<CanvasItem>(node)) {
add_node_menu->add_icon_item(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), TTR("Paste Node(s) Here"), ADD_PASTE);
@@ -3027,8 +3027,8 @@ void CanvasItemEditor::_draw_ruler_tool() {
Point2 v_angle_text_pos;
v_angle_text_pos.x = CLAMP(begin.x - angle_text_width / 2, angle_text_width / 2, viewport->get_rect().size.x - angle_text_width);
v_angle_text_pos.y = begin.y < end.y ? MIN(text_pos2.y - 2 * text_height, begin.y - text_height * 0.5) : MAX(text_pos2.y + text_height * 3, begin.y + text_height * 1.5);
- viewport->draw_string_outline(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
- viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color);
+ viewport->draw_string_outline(font, v_angle_text_pos, TS->format_number(vformat(U"%d°", vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
+ viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(U"%d°", vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color);
text_pos2 = text_pos;
text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y - text_height / 2) : MAX(text_pos.y + text_height * 2, end.y - text_height / 2);
@@ -3050,8 +3050,8 @@ void CanvasItemEditor::_draw_ruler_tool() {
h_angle_text_pos.y = MIN(text_pos.y - height_multiplier * text_height, MIN(end.y - text_height * 0.5, text_pos2.y - height_multiplier * text_height));
}
}
- viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
- viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color);
+ viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(U"%d°", horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
+ viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(U"%d°", horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color);
}
if (grid_snap_active) {
@@ -5079,7 +5079,7 @@ CanvasItemEditor::CanvasItemEditor() {
viewport->add_child(controls_vb);
- // Add some margin to the left for better aesthetics.
+ // Add some margin to the left for better esthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
Control *margin_left = memnew(Control);
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
index 3c9bc991d3..15829a55de 100644
--- a/editor/plugins/debugger_editor_plugin.cpp
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -54,7 +54,6 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
- db->set_theme_type_variation("BottomPanelButton");
debugger->set_tool_button(db);
// Main editor debug menu.
diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
index e4503b9c6f..2ef2e3a666 100644
--- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
@@ -41,6 +41,7 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8));
create_material("camera_material", gizmo_color);
+ create_icon_material("camera_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoCamera3D", "EditorIcons"));
create_handle_material("handles");
}
@@ -159,6 +160,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector<Vector3> handles;
Ref<Material> material = get_material("camera_material", p_gizmo);
+ Ref<Material> icon = get_material("camera_icon", p_gizmo);
const Size2i viewport_size = _get_viewport_size(camera);
const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0;
@@ -256,6 +258,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
#undef ADD_QUAD
p_gizmo->add_lines(lines, material);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_collision_segments(lines);
if (!handles.is_empty()) {
diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
index b967cb7ccd..3becc9c9fd 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
@@ -301,7 +301,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(ss.ptr(), "set_size", ss->get_size());
- ur->add_do_method(cs, "set_position", cs->get_global_position());
+ ur->add_do_method(cs, "set_global_position", cs->get_global_position());
ur->add_undo_method(ss.ptr(), "set_size", p_restore);
ur->add_undo_method(cs, "set_global_position", initial_transform.get_origin());
ur->commit_action();
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index 9582f0caa5..938a541a22 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -93,8 +93,8 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_notification(int p_what) {
}
String text;
- text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z)) + "\n";
- text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n";
+ text += vformat(TTR("Subdivisions: %s"), vformat(U"%d × %d × %d", size.x, size.y, size.z)) + "\n";
+ text += vformat(TTR("Cell size: %s"), vformat(U"%.3f × %.3f × %.3f", extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n";
text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality);
// Only update the tooltip when needed to avoid constant redrawing.
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 404711e074..e24172e761 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -39,9 +39,9 @@
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/subviewport_container.h"
-#include "scene/gui/texture_button.h"
#include "scene/main/viewport.h"
#include "scene/resources/fog_material.h"
#include "scene/resources/particle_process_material.h"
@@ -63,15 +63,11 @@ void MaterialEditor::gui_input(const Ref<InputEvent> &p_event) {
void MaterialEditor::_update_theme_item_cache() {
Control::_update_theme_item_cache();
- theme_cache.light_1_on = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons"));
- theme_cache.light_1_off = get_theme_icon(SNAME("MaterialPreviewLight1Off"), SNAME("EditorIcons"));
- theme_cache.light_2_on = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons"));
- theme_cache.light_2_off = get_theme_icon(SNAME("MaterialPreviewLight2Off"), SNAME("EditorIcons"));
+ theme_cache.light_1_icon = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons"));
+ theme_cache.light_2_icon = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons"));
- theme_cache.sphere_on = get_theme_icon(SNAME("MaterialPreviewSphere"), SNAME("EditorIcons"));
- theme_cache.sphere_off = get_theme_icon(SNAME("MaterialPreviewSphereOff"), SNAME("EditorIcons"));
- theme_cache.box_on = get_theme_icon(SNAME("MaterialPreviewCube"), SNAME("EditorIcons"));
- theme_cache.box_off = get_theme_icon(SNAME("MaterialPreviewCubeOff"), SNAME("EditorIcons"));
+ theme_cache.sphere_icon = get_theme_icon(SNAME("MaterialPreviewSphere"), SNAME("EditorIcons"));
+ theme_cache.box_icon = get_theme_icon(SNAME("MaterialPreviewCube"), SNAME("EditorIcons"));
theme_cache.checkerboard = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons"));
}
@@ -79,15 +75,11 @@ void MaterialEditor::_update_theme_item_cache() {
void MaterialEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- light_1_switch->set_texture_normal(theme_cache.light_1_on);
- light_1_switch->set_texture_pressed(theme_cache.light_1_off);
- light_2_switch->set_texture_normal(theme_cache.light_2_on);
- light_2_switch->set_texture_pressed(theme_cache.light_2_off);
-
- sphere_switch->set_texture_normal(theme_cache.sphere_off);
- sphere_switch->set_texture_pressed(theme_cache.sphere_on);
- box_switch->set_texture_normal(theme_cache.box_off);
- box_switch->set_texture_pressed(theme_cache.box_on);
+ light_1_switch->set_icon(theme_cache.light_1_icon);
+ light_2_switch->set_icon(theme_cache.light_2_icon);
+
+ sphere_switch->set_icon(theme_cache.sphere_icon);
+ box_switch->set_icon(theme_cache.box_icon);
} break;
case NOTIFICATION_DRAW: {
@@ -135,34 +127,32 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en
_update_rotation();
}
-void MaterialEditor::_button_pressed(Node *p_button) {
- if (p_button == light_1_switch) {
- light1->set_visible(!light_1_switch->is_pressed());
- }
+void MaterialEditor::_on_light_1_switch_pressed() {
+ light1->set_visible(light_1_switch->is_pressed());
+}
- if (p_button == light_2_switch) {
- light2->set_visible(!light_2_switch->is_pressed());
- }
+void MaterialEditor::_on_light_2_switch_pressed() {
+ light2->set_visible(light_2_switch->is_pressed());
+}
- if (p_button == box_switch) {
- box_instance->show();
- sphere_instance->hide();
- box_switch->set_pressed(true);
- sphere_switch->set_pressed(false);
- EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false);
- }
+void MaterialEditor::_on_sphere_switch_pressed() {
+ box_instance->hide();
+ sphere_instance->show();
+ box_switch->set_pressed(false);
+ sphere_switch->set_pressed(true);
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true);
+}
- if (p_button == sphere_switch) {
- box_instance->hide();
- sphere_instance->show();
- box_switch->set_pressed(false);
- sphere_switch->set_pressed(true);
- EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true);
- }
+void MaterialEditor::_on_box_switch_pressed() {
+ box_instance->show();
+ sphere_instance->hide();
+ box_switch->set_pressed(true);
+ sphere_switch->set_pressed(false);
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false);
}
MaterialEditor::MaterialEditor() {
- // canvas item
+ // Canvas item
vc_2d = memnew(SubViewportContainer);
vc_2d->set_stretch(true);
@@ -185,7 +175,7 @@ MaterialEditor::MaterialEditor() {
layout_2d->set_visible(false);
- // spatial
+ // Spatial
vc = memnew(SubViewportContainer);
vc->set_stretch(true);
@@ -247,32 +237,38 @@ MaterialEditor::MaterialEditor() {
VBoxContainer *vb_shape = memnew(VBoxContainer);
layout_3d->add_child(vb_shape);
- sphere_switch = memnew(TextureButton);
+ sphere_switch = memnew(Button);
+ sphere_switch->set_theme_type_variation("PreviewLightButton");
sphere_switch->set_toggle_mode(true);
sphere_switch->set_pressed(true);
vb_shape->add_child(sphere_switch);
- sphere_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(sphere_switch));
+ sphere_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_sphere_switch_pressed));
- box_switch = memnew(TextureButton);
+ box_switch = memnew(Button);
+ box_switch->set_theme_type_variation("PreviewLightButton");
box_switch->set_toggle_mode(true);
box_switch->set_pressed(false);
vb_shape->add_child(box_switch);
- box_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(box_switch));
+ box_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_box_switch_pressed));
layout_3d->add_spacer();
VBoxContainer *vb_light = memnew(VBoxContainer);
layout_3d->add_child(vb_light);
- light_1_switch = memnew(TextureButton);
+ light_1_switch = memnew(Button);
+ light_1_switch->set_theme_type_variation("PreviewLightButton");
light_1_switch->set_toggle_mode(true);
+ light_1_switch->set_pressed(true);
vb_light->add_child(light_1_switch);
- light_1_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(light_1_switch));
+ light_1_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_light_1_switch_pressed));
- light_2_switch = memnew(TextureButton);
+ light_2_switch = memnew(Button);
+ light_2_switch->set_theme_type_variation("PreviewLightButton");
light_2_switch->set_toggle_mode(true);
+ light_2_switch->set_pressed(true);
vb_light->add_child(light_2_switch);
- light_2_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(light_2_switch));
+ light_2_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_light_2_switch_pressed));
if (EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_on_sphere", true)) {
box_instance->hide();
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index ac81bdc7c7..5530592a81 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -44,7 +44,7 @@ class HBoxContainer;
class MeshInstance3D;
class SubViewport;
class SubViewportContainer;
-class TextureButton;
+class Button;
class MaterialEditor : public Control {
GDCLASS(MaterialEditor, Control);
@@ -71,27 +71,25 @@ class MaterialEditor : public Control {
HBoxContainer *layout_3d = nullptr;
- TextureButton *sphere_switch = nullptr;
- TextureButton *box_switch = nullptr;
-
- TextureButton *light_1_switch = nullptr;
- TextureButton *light_2_switch = nullptr;
-
Ref<Material> material;
+ Button *sphere_switch = nullptr;
+ Button *box_switch = nullptr;
+ Button *light_1_switch = nullptr;
+ Button *light_2_switch = nullptr;
+
struct ThemeCache {
- Ref<Texture2D> light_1_on;
- Ref<Texture2D> light_1_off;
- Ref<Texture2D> light_2_on;
- Ref<Texture2D> light_2_off;
- Ref<Texture2D> sphere_on;
- Ref<Texture2D> sphere_off;
- Ref<Texture2D> box_on;
- Ref<Texture2D> box_off;
+ Ref<Texture2D> light_1_icon;
+ Ref<Texture2D> light_2_icon;
+ Ref<Texture2D> sphere_icon;
+ Ref<Texture2D> box_icon;
Ref<Texture2D> checkerboard;
} theme_cache;
- void _button_pressed(Node *p_button);
+ void _on_light_1_switch_pressed();
+ void _on_light_2_switch_pressed();
+ void _on_sphere_switch_pressed();
+ void _on_box_switch_pressed();
protected:
virtual void _update_theme_item_cache() override;
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index ee555ae8d4..b6be971370 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -32,7 +32,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_scale.h"
-#include "scene/gui/texture_button.h"
+#include "scene/gui/button.h"
#include "scene/main/viewport.h"
void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
@@ -42,11 +42,8 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
rot_x -= mm->get_relative().y * 0.01;
rot_y -= mm->get_relative().x * 0.01;
- if (rot_x < -Math_PI / 2) {
- rot_x = -Math_PI / 2;
- } else if (rot_x > Math_PI / 2) {
- rot_x = Math_PI / 2;
- }
+
+ rot_x = CLAMP(rot_x, -Math_PI / 2, Math_PI / 2);
_update_rotation();
}
}
@@ -54,19 +51,15 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
void MeshEditor::_update_theme_item_cache() {
SubViewportContainer::_update_theme_item_cache();
- theme_cache.light_1_on = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons"));
- theme_cache.light_1_off = get_theme_icon(SNAME("MaterialPreviewLight1Off"), SNAME("EditorIcons"));
- theme_cache.light_2_on = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons"));
- theme_cache.light_2_off = get_theme_icon(SNAME("MaterialPreviewLight2Off"), SNAME("EditorIcons"));
+ theme_cache.light_1_icon = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons"));
+ theme_cache.light_2_icon = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons"));
}
void MeshEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- light_1_switch->set_texture_normal(theme_cache.light_1_on);
- light_1_switch->set_texture_pressed(theme_cache.light_1_off);
- light_2_switch->set_texture_normal(theme_cache.light_2_on);
- light_2_switch->set_texture_pressed(theme_cache.light_2_off);
+ light_1_switch->set_icon(theme_cache.light_1_icon);
+ light_2_switch->set_icon(theme_cache.light_2_icon);
} break;
}
}
@@ -100,21 +93,19 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) {
}
}
-void MeshEditor::_button_pressed(Node *p_button) {
- if (p_button == light_1_switch) {
- light1->set_visible(!light_1_switch->is_pressed());
- }
+void MeshEditor::_on_light_1_switch_pressed() {
+ light1->set_visible(light_1_switch->is_pressed());
+}
- if (p_button == light_2_switch) {
- light2->set_visible(!light_2_switch->is_pressed());
- }
+void MeshEditor::_on_light_2_switch_pressed() {
+ light2->set_visible(light_2_switch->is_pressed());
}
MeshEditor::MeshEditor() {
viewport = memnew(SubViewport);
Ref<World3D> world_3d;
world_3d.instantiate();
- viewport->set_world_3d(world_3d); //use own world
+ viewport->set_world_3d(world_3d); // Use own world.
add_child(viewport);
viewport->set_disable_input(true);
viewport->set_msaa_3d(Viewport::MSAA_4X);
@@ -154,15 +145,19 @@ MeshEditor::MeshEditor() {
VBoxContainer *vb_light = memnew(VBoxContainer);
hb->add_child(vb_light);
- light_1_switch = memnew(TextureButton);
+ light_1_switch = memnew(Button);
+ light_1_switch->set_theme_type_variation("PreviewLightButton");
light_1_switch->set_toggle_mode(true);
+ light_1_switch->set_pressed(true);
vb_light->add_child(light_1_switch);
- light_1_switch->connect("pressed", callable_mp(this, &MeshEditor::_button_pressed).bind(light_1_switch));
+ light_1_switch->connect("pressed", callable_mp(this, &MeshEditor::_on_light_1_switch_pressed));
- light_2_switch = memnew(TextureButton);
+ light_2_switch = memnew(Button);
+ light_2_switch->set_theme_type_variation("PreviewLightButton");
light_2_switch->set_toggle_mode(true);
+ light_2_switch->set_pressed(true);
vb_light->add_child(light_2_switch);
- light_2_switch->connect("pressed", callable_mp(this, &MeshEditor::_button_pressed).bind(light_2_switch));
+ light_2_switch->connect("pressed", callable_mp(this, &MeshEditor::_on_light_2_switch_pressed));
rot_x = 0;
rot_y = 0;
diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h
index 335244ffd2..a8ef476f84 100644
--- a/editor/plugins/mesh_editor_plugin.h
+++ b/editor/plugins/mesh_editor_plugin.h
@@ -41,7 +41,7 @@
#include "scene/resources/material.h"
class SubViewport;
-class TextureButton;
+class Button;
class MeshEditor : public SubViewportContainer {
GDCLASS(MeshEditor, SubViewportContainer);
@@ -59,17 +59,16 @@ class MeshEditor : public SubViewportContainer {
Ref<Mesh> mesh;
- TextureButton *light_1_switch = nullptr;
- TextureButton *light_2_switch = nullptr;
+ Button *light_1_switch = nullptr;
+ Button *light_2_switch = nullptr;
struct ThemeCache {
- Ref<Texture2D> light_1_on;
- Ref<Texture2D> light_1_off;
- Ref<Texture2D> light_2_on;
- Ref<Texture2D> light_2_off;
+ Ref<Texture2D> light_1_icon;
+ Ref<Texture2D> light_2_icon;
} theme_cache;
- void _button_pressed(Node *p_button);
+ void _on_light_1_switch_pressed();
+ void _on_light_2_switch_pressed();
void _update_rotation();
protected:
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 78805324c9..65563bd1a3 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1675,6 +1675,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) {
cancel_transform();
+ break;
}
if (b->is_pressed()) {
@@ -2007,7 +2008,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_edit.mode = TRANSFORM_TRANSLATE;
}
- if (_edit.mode == TRANSFORM_NONE) {
+ if (_edit.mode == TRANSFORM_NONE || _edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) {
return;
}
@@ -2145,6 +2146,43 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
+ if (_edit.instant) {
+ // In a Blender-style transform, numbers set the magnitude of the transform.
+ // E.g. pressing g4.5x means "translate 4.5 units along the X axis".
+ // Use the Unicode value because we care about the text, not the actual keycode.
+ // This ensures numbers work consistently across different keyboard language layouts.
+ bool processed = true;
+ Key key = k->get_physical_keycode();
+ char32_t unicode = k->get_unicode();
+ if (unicode >= '0' && unicode <= '9') {
+ uint32_t value = uint32_t(unicode - Key::KEY_0);
+ if (_edit.numeric_next_decimal < 0) {
+ _edit.numeric_input = _edit.numeric_input + value * Math::pow(10.0, _edit.numeric_next_decimal--);
+ } else {
+ _edit.numeric_input = _edit.numeric_input * 10 + value;
+ }
+ update_transform_numeric();
+ } else if (unicode == '-') {
+ _edit.numeric_negate = !_edit.numeric_negate;
+ update_transform_numeric();
+ } else if (unicode == '.') {
+ if (_edit.numeric_next_decimal == 0) {
+ _edit.numeric_next_decimal = -1;
+ }
+ } else if (key == Key::ENTER || key == Key::KP_ENTER || key == Key::SPACE) {
+ commit_transform();
+ } else {
+ processed = false;
+ }
+
+ if (processed) {
+ // Ignore mouse inputs once we receive a numeric input.
+ set_process_input(false);
+ accept_event();
+ return;
+ }
+ }
+
if (EDITOR_GET("editors/3d/navigation/emulate_numpad")) {
const Key code = k->get_physical_keycode();
if (code >= Key::KEY_0 && code <= Key::KEY_9) {
@@ -2165,26 +2203,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else {
// We're actively transforming, handle keys specially
TransformPlane new_plane = TRANSFORM_VIEW;
- String new_message;
if (ED_IS_SHORTCUT("spatial_editor/lock_transform_x", p_event)) {
new_plane = TRANSFORM_X_AXIS;
- new_message = TTR("X-Axis Transform.");
} else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_y", p_event)) {
new_plane = TRANSFORM_Y_AXIS;
- new_message = TTR("Y-Axis Transform.");
} else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_z", p_event)) {
new_plane = TRANSFORM_Z_AXIS;
- new_message = TTR("Z-Axis Transform.");
} else if (_edit.mode != TRANSFORM_ROTATE) { // rotating on a plane doesn't make sense
if (ED_IS_SHORTCUT("spatial_editor/lock_transform_yz", p_event)) {
new_plane = TRANSFORM_YZ;
- new_message = TTR("YZ-Plane Transform.");
} else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xz", p_event)) {
new_plane = TRANSFORM_XZ;
- new_message = TTR("XZ-Plane Transform.");
} else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xy", p_event)) {
new_plane = TRANSFORM_XY;
- new_message = TTR("XY-Plane Transform.");
}
}
@@ -2201,8 +2232,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_edit.plane = TRANSFORM_VIEW;
spatial_editor->set_local_coords_enabled(false);
}
- update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT));
- set_message(new_message, 2);
+ if (_edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) {
+ update_transform_numeric();
+ } else {
+ update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT));
+ }
accept_event();
return;
}
@@ -2659,6 +2693,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool transparent_background = GLOBAL_GET("rendering/viewport/transparent_background");
viewport->set_transparent_background(transparent_background);
+ const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
+ viewport->set_use_hdr_2d(use_hdr_2d);
+
const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
viewport->set_use_debanding(use_debanding);
@@ -2822,7 +2859,7 @@ void Node3DEditorViewport::_notification(int p_what) {
}
if (show_info) {
- const String viewport_size = vformat(String::utf8("%d × %d"), viewport->get_size().x, viewport->get_size().y);
+ const String viewport_size = vformat(U"%d × %d", viewport->get_size().x, viewport->get_size().y);
String text;
text += vformat(TTR("X: %s\n"), rtos(current_camera->get_position().x).pad_decimals(1));
text += vformat(TTR("Y: %s\n"), rtos(current_camera->get_position().y).pad_decimals(1));
@@ -4572,6 +4609,43 @@ void Node3DEditorViewport::commit_transform() {
set_message("");
}
+void Node3DEditorViewport::apply_transform(Vector3 p_motion, double p_snap) {
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ for (Node *E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E);
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
+ if (se->gizmo.is_valid()) {
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
+ Transform3D new_xform = _compute_transform(_edit.mode, se->original * xform, xform, p_motion, p_snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo.
+ if (!local_coords) {
+ new_xform = se->original.affine_inverse() * new_xform;
+ }
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
+ }
+ } else {
+ Transform3D new_xform = _compute_transform(_edit.mode, se->original, se->original_local, p_motion, p_snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW);
+ _transform_gizmo_apply(se->sp, new_xform, local_coords);
+ }
+ }
+
+ spatial_editor->update_transform_gizmo();
+ surface->queue_redraw();
+}
+
// Update the current transform operation in response to an input.
void Node3DEditorViewport::update_transform(bool p_shift) {
Vector3 ray_pos = _get_ray_pos(_edit.mouse_pos);
@@ -4667,43 +4741,11 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
if (local_coords) {
+ // TODO: needed?
motion = _edit.original.basis.inverse().xform(motion);
}
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
- Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo.
- if (!local_coords) {
- new_xform = se->original.affine_inverse() * new_xform;
- }
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW);
- _transform_gizmo_apply(se->sp, new_xform, local_coords);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->queue_redraw();
-
+ apply_transform(motion, snap);
} break;
case TRANSFORM_TRANSLATE: {
@@ -4773,38 +4815,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion);
}
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
- Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
- new_xform = se->original.affine_inverse() * new_xform;
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
- _transform_gizmo_apply(se->sp, new_xform, false);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->queue_redraw();
-
+ apply_transform(motion, snap);
} break;
case TRANSFORM_ROTATE: {
@@ -4873,53 +4884,85 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- Vector3 compute_axis = local_coords ? local_axis : global_axis;
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
-
- Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo.
- if (!local_coords) {
- new_xform = se->original.affine_inverse() * new_xform;
- }
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
- _transform_gizmo_apply(se->sp, new_xform, local_coords);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->queue_redraw();
-
+ Vector3 compute_axis = local_coords ? local_axis : global_axis;
+ apply_transform(compute_axis, angle);
} break;
default: {
}
}
}
+void Node3DEditorViewport::update_transform_numeric() {
+ Vector3 motion;
+ switch (_edit.plane) {
+ case TRANSFORM_VIEW: {
+ switch (_edit.mode) {
+ case TRANSFORM_TRANSLATE:
+ motion = Vector3(1, 0, 0);
+ break;
+ case TRANSFORM_ROTATE:
+ motion = spatial_editor->get_gizmo_transform().basis.xform_inv(_get_camera_normal()).normalized();
+ break;
+ case TRANSFORM_SCALE:
+ motion = Vector3(1, 1, 1);
+ break;
+ case TRANSFORM_NONE:
+ ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric.");
+ }
+ break;
+ }
+ case TRANSFORM_X_AXIS:
+ motion = Vector3(1, 0, 0);
+ break;
+ case TRANSFORM_Y_AXIS:
+ motion = Vector3(0, 1, 0);
+ break;
+ case TRANSFORM_Z_AXIS:
+ motion = Vector3(0, 0, 1);
+ break;
+ case TRANSFORM_XY:
+ motion = Vector3(1, 1, 0);
+ break;
+ case TRANSFORM_XZ:
+ motion = Vector3(1, 0, 1);
+ break;
+ case TRANSFORM_YZ:
+ motion = Vector3(0, 1, 1);
+ break;
+ }
+
+ double value = _edit.numeric_input * (_edit.numeric_negate ? -1 : 1);
+ double extra = 0.0;
+ switch (_edit.mode) {
+ case TRANSFORM_TRANSLATE:
+ motion *= value;
+ set_message(vformat(TTR("Translating %s."), motion));
+ break;
+ case TRANSFORM_ROTATE:
+ extra = Math::deg_to_rad(value);
+ set_message(vformat(TTR("Rotating %f degrees."), value));
+ break;
+ case TRANSFORM_SCALE:
+ // To halve the size of an object in Blender, you scale it by 0.5.
+ // Doing the same in Godot is considered scaling it by -0.5.
+ motion *= (value - 1.0);
+ set_message(vformat(TTR("Scaling %s."), motion));
+ break;
+ case TRANSFORM_NONE:
+ ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric.");
+ }
+
+ apply_transform(motion, extra);
+}
+
// Perform cleanup after a transform operation is committed or cancelled.
void Node3DEditorViewport::finish_transform() {
- spatial_editor->set_local_coords_enabled(_edit.original_local);
_edit.mode = TRANSFORM_NONE;
_edit.instant = false;
+ _edit.numeric_input = 0;
+ _edit.numeric_next_decimal = 0;
+ _edit.numeric_negate = false;
+ spatial_editor->set_local_coords_enabled(_edit.original_local);
spatial_editor->update_transform_gizmo();
surface->queue_redraw();
set_process_input(false);
@@ -8126,7 +8169,7 @@ Node3DEditor::Node3DEditor() {
String sct;
- // Add some margin to the left for better aesthetics.
+ // Add some margin to the left for better esthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
Control *margin_left = memnew(Control);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 79674bdd64..e58e224ff4 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -349,6 +349,15 @@ private:
Variant gizmo_initial_value;
bool original_local;
bool instant;
+
+ // Numeric blender-style transforms (e.g. 'g5x').
+ // numeric_input tracks the current input value, e.g. 1.23.
+ // numeric_negate indicates whether '-' has been pressed to negate the value
+ // while numeric_next_decimal is 0, numbers are input before the decimal point
+ // after pressing '.', numeric next decimal changes to -1, and decrements after each press.
+ double numeric_input = 0.0;
+ bool numeric_negate = false;
+ int numeric_next_decimal = 0;
} _edit;
struct Cursor {
@@ -445,7 +454,9 @@ private:
void begin_transform(TransformMode p_mode, bool instant);
void commit_transform();
+ void apply_transform(Vector3 p_motion, double p_snap);
void update_transform(bool p_shift);
+ void update_transform_numeric();
void finish_transform();
void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 67df349e48..eb0e20a12e 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1791,6 +1791,10 @@ void ScriptEditor::ensure_select_current() {
_update_selected_editor_menu();
}
+bool ScriptEditor::is_editor_floating() {
+ return is_floating;
+}
+
void ScriptEditor::_find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used) {
if (p_current != p_base && p_current->get_owner() != p_base) {
return;
@@ -3747,6 +3751,7 @@ void ScriptEditor::_on_find_in_files_modified_files(PackedStringArray paths) {
void ScriptEditor::_window_changed(bool p_visible) {
make_floating->set_visible(!p_visible);
+ is_floating = p_visible;
}
void ScriptEditor::_filter_scripts_text_changed(const String &p_newtext) {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 198aaa6c4e..a33c877d2d 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -274,6 +274,7 @@ class ScriptEditor : public PanelContainer {
Button *help_search = nullptr;
Button *site_search = nullptr;
Button *make_floating = nullptr;
+ bool is_floating = false;
EditorHelpSearch *help_search_dialog = nullptr;
ItemList *script_list = nullptr;
@@ -507,6 +508,8 @@ public:
void ensure_select_current();
+ bool is_editor_floating();
+
_FORCE_INLINE_ bool edit(const Ref<Resource> &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); }
bool edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus = true);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 4e5a3eb095..0a3de70e2a 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -52,10 +52,8 @@
#include "scene/scene_string_names.h"
void BoneTransformEditor::create_editors() {
- const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
-
section = memnew(EditorInspectorSection);
- section->setup("trf_properties", label, this, section_color, true);
+ section->setup("trf_properties", label, this, Color(0.0f, 0.0f, 0.0f), true);
section->unfold();
add_child(section);
@@ -94,7 +92,7 @@ void BoneTransformEditor::create_editors() {
// Transform/Matrix section.
rest_section = memnew(EditorInspectorSection);
- rest_section->setup("trf_properties_transform", "Rest", this, section_color, true);
+ rest_section->setup("trf_properties_transform", "Rest", this, Color(0.0f, 0.0f, 0.0f), true);
section->get_vbox()->add_child(rest_section);
// Transform/Matrix property.
@@ -107,8 +105,10 @@ void BoneTransformEditor::create_editors() {
void BoneTransformEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- create_editors();
+ case NOTIFICATION_THEME_CHANGED: {
+ const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
+ section->set_bg_color(section_color);
+ rest_section->set_bg_color(section_color);
} break;
}
}
@@ -128,6 +128,7 @@ void BoneTransformEditor::_value_changed(const String &p_property, Variant p_val
BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
skeleton(p_skeleton) {
+ create_editors();
}
void BoneTransformEditor::set_keyable(const bool p_keyable) {
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 7b35351457..720cfb5928 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -1282,7 +1282,7 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) {
// Frame is often saved as an AtlasTexture subresource within a scene/resource file,
// thus its path might be not what the user is looking for. So we're also showing
// subsequent source texture paths.
- String prefix = String::utf8("┖╴");
+ String prefix = U"┖╴";
Ref<AtlasTexture> at = texture;
while (at.is_valid() && at->get_atlas().is_valid()) {
tooltip += "\n" + prefix + at->get_atlas()->get_path();
diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
index 1d14f5e60b..9364b96c90 100644
--- a/editor/plugins/style_box_editor_plugin.cpp
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -110,14 +110,11 @@ StyleBoxPreview::StyleBoxPreview() {
set_anchors_and_offsets_preset(PRESET_FULL_RECT);
grid_preview = memnew(Button);
+ // This theme variation works better than the normal theme because there's no focus highlight.
+ grid_preview->set_theme_type_variation("PreviewLightButton");
grid_preview->set_toggle_mode(true);
grid_preview->connect("toggled", callable_mp(this, &StyleBoxPreview::_grid_preview_toggled));
grid_preview->set_pressed(grid_preview_enabled);
- grid_preview->set_flat(true);
- grid_preview->add_theme_style_override("normal", memnew(StyleBoxEmpty));
- grid_preview->add_theme_style_override("hover", memnew(StyleBoxEmpty));
- grid_preview->add_theme_style_override("focus", memnew(StyleBoxEmpty));
- grid_preview->add_theme_style_override("pressed", memnew(StyleBoxEmpty));
add_child(grid_preview);
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 8b58a45ea5..5815c85718 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -562,6 +562,16 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
return true;
}
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (BaseButton *b : viewport_shortcut_buttons) {
+ if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
+ return true;
+ }
+ }
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
has_mouse = true;
@@ -2075,6 +2085,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", TTR("Selection"), Key::S));
select_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(select_tool_button);
+ viewport_shortcut_buttons.push_back(select_tool_button);
paint_tool_button = memnew(Button);
paint_tool_button->set_flat(true);
@@ -2084,6 +2095,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle."));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ viewport_shortcut_buttons.push_back(paint_tool_button);
line_tool_button = memnew(Button);
line_tool_button->set_flat(true);
@@ -2093,6 +2105,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L));
line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
+ viewport_shortcut_buttons.push_back(line_tool_button);
rect_tool_button = memnew(Button);
rect_tool_button->set_flat(true);
@@ -2101,6 +2114,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+ viewport_shortcut_buttons.push_back(rect_tool_button);
bucket_tool_button = memnew(Button);
bucket_tool_button->set_flat(true);
@@ -2110,6 +2124,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
+ viewport_shortcut_buttons.push_back(bucket_tool_button);
// -- TileMap tool settings --
tools_settings = memnew(HBoxContainer);
@@ -2126,6 +2141,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
picker_button->set_tooltip_text(TTR("Alternatively hold Ctrl with other tools to pick tile."));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
+ viewport_shortcut_buttons.push_back(picker_button);
// Erase button.
erase_button = memnew(Button);
@@ -2135,6 +2151,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles."));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+ viewport_shortcut_buttons.push_back(erase_button);
// Separator 2.
tools_settings_vsep_2 = memnew(VSeparator);
@@ -2860,6 +2877,16 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
_update_selection();
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (BaseButton *b : viewport_shortcut_buttons) {
+ if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
+ return true;
+ }
+ }
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
has_mouse = true;
@@ -3388,6 +3415,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ viewport_shortcut_buttons.push_back(paint_tool_button);
line_tool_button = memnew(Button);
line_tool_button->set_flat(true);
@@ -3396,6 +3424,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L));
line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
+ viewport_shortcut_buttons.push_back(line_tool_button);
rect_tool_button = memnew(Button);
rect_tool_button->set_flat(true);
@@ -3404,6 +3433,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+ viewport_shortcut_buttons.push_back(rect_tool_button);
bucket_tool_button = memnew(Button);
bucket_tool_button->set_flat(true);
@@ -3412,6 +3442,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
+ viewport_shortcut_buttons.push_back(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
@@ -3429,6 +3460,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
+ viewport_shortcut_buttons.push_back(picker_button);
// Erase button.
erase_button = memnew(Button);
@@ -3437,6 +3469,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+ viewport_shortcut_buttons.push_back(erase_button);
// Separator 2.
tools_settings_vsep_2 = memnew(VSeparator);
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index eb8c3937a0..ab5787b78f 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -203,6 +203,7 @@ private:
// General
void _update_theme();
+ List<BaseButton *> viewport_shortcut_buttons;
// Update callback
virtual void tile_set_changed() override;
@@ -293,6 +294,7 @@ private:
// Cache.
LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
+ List<BaseButton *> viewport_shortcut_buttons;
// Update functions.
void _update_terrains_cache();
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index c98d9086d1..b1111be006 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2655,6 +2655,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
memdelete(tile_proxy_object);
memdelete(atlas_source_proxy_object);
+
+ // Remove listener for old objects, so the TileSet doesn't
+ // try to call the destroyed TileSetAtlasSourceEditor.
+ if (tile_set.is_valid()) {
+ tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
+ }
}
////// EditorPropertyTilePolygon //////
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 911316822a..e432704702 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -38,7 +38,6 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-#include "editor/inspector_dock.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/tile_map.h"
@@ -326,8 +325,18 @@ void TileMapEditorPlugin::_tile_map_changed() {
}
void TileMapEditorPlugin::_update_tile_map() {
- if (tile_map && tile_map->get_tileset().is_valid()) {
- EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton());
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (tile_set.is_valid() && edited_tileset != tile_set->get_instance_id()) {
+ tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr());
+ tile_set_plugin_singleton->make_visible(true);
+ edited_tileset = tile_set->get_instance_id();
+ } else if (tile_set.is_null()) {
+ tile_set_plugin_singleton->edit(nullptr);
+ tile_set_plugin_singleton->make_visible(false);
+ edited_tileset = ObjectID();
+ }
}
tile_map_changed_needs_update = false;
}
@@ -339,18 +348,26 @@ void TileMapEditorPlugin::_notification(int p_notification) {
}
void TileMapEditorPlugin::edit(Object *p_object) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
}
tile_map = Object::cast_to<TileMap>(p_object);
+ if (tile_map) {
+ tile_map_id = tile_map->get_instance_id();
+ } else {
+ tile_map_id = ObjectID();
+ }
editor->edit(tile_map);
if (tile_map) {
tile_map->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
if (tile_map->get_tileset().is_valid()) {
- EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton());
+ tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr());
+ tile_set_plugin_singleton->make_visible(true);
+ edited_tileset = tile_map->get_tileset()->get_instance_id();
}
}
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 0bb45b746d..81cb48eb00 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -115,9 +115,11 @@ class TileMapEditorPlugin : public EditorPlugin {
TileMapEditor *editor = nullptr;
Button *button = nullptr;
- TileMap *tile_map = nullptr;
+ ObjectID tile_map_id;
bool tile_map_changed_needs_update = false;
+ ObjectID edited_tileset;
+
void _tile_map_changed();
void _update_tile_map();
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 43b133f4b5..af4a027f78 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -119,8 +119,8 @@ void VoxelGIEditorPlugin::_notification(int p_what) {
}
String text;
- text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), cell_size.x, cell_size.y, cell_size.z)) + "\n";
- text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), half_size.x / cell_size.x, half_size.y / cell_size.y, half_size.z / cell_size.z)) + "\n";
+ text += vformat(TTR("Subdivisions: %s"), vformat(U"%d × %d × %d", cell_size.x, cell_size.y, cell_size.z)) + "\n";
+ text += vformat(TTR("Cell size: %s"), vformat(U"%.3f × %.3f × %.3f", half_size.x / cell_size.x, half_size.y / cell_size.y, half_size.z / cell_size.z)) + "\n";
text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality);
// Only update the tooltip when needed to avoid constant redrawing.
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index fbb724906b..849a6cc097 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -275,6 +275,7 @@ void register_editor_types() {
GLOBAL_DEF("editor/version_control/autoload_on_startup", false);
EditorInterface::create();
+ Engine::get_singleton()->add_singleton(Engine::Singleton("EditorInterface", EditorInterface::get_singleton()));
OS::get_singleton()->benchmark_end_measure("register_editor_types");
}
diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp
index 986f6bb87a..8f56267123 100644
--- a/editor/scene_create_dialog.cpp
+++ b/editor/scene_create_dialog.cpp
@@ -34,6 +34,7 @@
#include "editor/create_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/box_container.h"
@@ -41,7 +42,6 @@
#include "scene/gui/grid_container.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
-#include "scene/gui/panel_container.h"
#include "scene/resources/packed_scene.h"
void SceneCreateDialog::_notification(int p_what) {
@@ -53,7 +53,6 @@ void SceneCreateDialog::_notification(int p_what) {
node_type_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
node_type_gui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
node_type_other->add_theme_icon_override(SNAME("icon"), get_theme_icon(SNAME("Node"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -63,7 +62,7 @@ void SceneCreateDialog::config(const String &p_dir) {
root_name_edit->set_text("");
scene_name_edit->set_text("");
scene_name_edit->call_deferred(SNAME("grab_focus"));
- update_dialog();
+ validation_panel->update();
}
void SceneCreateDialog::accept_create() {
@@ -82,40 +81,35 @@ void SceneCreateDialog::browse_types() {
void SceneCreateDialog::on_type_picked() {
other_type_display->set_text(select_node_dialog->get_selected_type().get_slice(" ", 0));
if (node_type_other->is_pressed()) {
- update_dialog();
+ validation_panel->update();
} else {
- node_type_other->set_pressed(true); // Calls update_dialog() via group.
+ node_type_other->set_pressed(true); // Calls validation_panel->update() via group.
}
}
void SceneCreateDialog::update_dialog() {
scene_name = scene_name_edit->get_text().strip_edges();
- update_error(file_error_label, MSG_OK, TTR("Scene name is valid."));
- bool is_valid = true;
if (scene_name.is_empty()) {
- update_error(file_error_label, MSG_ERROR, TTR("Scene name is empty."));
- is_valid = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("Scene name is empty."), EditorValidationPanel::MSG_ERROR);
}
- if (is_valid) {
+ if (validation_panel->is_valid()) {
if (!scene_name.ends_with(".")) {
scene_name += ".";
}
scene_name += scene_extension_picker->get_selected_metadata().operator String();
}
- if (is_valid && !scene_name.is_valid_filename()) {
- update_error(file_error_label, MSG_ERROR, TTR("File name invalid."));
- is_valid = false;
+ if (validation_panel->is_valid() && !scene_name.is_valid_filename()) {
+ validation_panel->set_message(MSG_ID_PATH, TTR("File name invalid."), EditorValidationPanel::MSG_ERROR);
}
- if (is_valid) {
+ if (validation_panel->is_valid()) {
scene_name = directory.path_join(scene_name);
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists(scene_name)) {
- update_error(file_error_label, MSG_ERROR, TTR("File already exists."));
- is_valid = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("File already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -126,8 +120,6 @@ void SceneCreateDialog::update_dialog() {
node_type_other->set_icon(nullptr);
}
- update_error(node_error_label, MSG_OK, TTR("Root node valid."));
-
root_name = root_name_edit->get_text().strip_edges();
if (root_name.is_empty()) {
root_name = scene_name_edit->get_text().strip_edges();
@@ -135,39 +127,16 @@ void SceneCreateDialog::update_dialog() {
if (root_name.is_empty()) {
root_name_edit->set_placeholder(TTR("Leave empty to derive from scene name"));
} else {
- // Respect the desired root node casing from ProjectSettings and ensure it's a valid node name.
- String adjusted_root_name = Node::adjust_name_casing(root_name);
- root_name = adjusted_root_name.validate_node_name();
-
- bool has_invalid_characters = root_name != adjusted_root_name;
- if (has_invalid_characters) {
- update_error(node_error_label, MSG_WARNING, TTR("Invalid root node name characters have been replaced."));
- }
-
- root_name_edit->set_placeholder(root_name);
+ // Respect the desired root node casing from ProjectSettings.
+ root_name = Node::adjust_name_casing(root_name);
+ root_name_edit->set_placeholder(root_name.validate_node_name());
}
}
- if (root_name.is_empty() || root_name.validate_node_name() != root_name) {
- update_error(node_error_label, MSG_ERROR, TTR("Invalid root node name."));
- is_valid = false;
- }
-
- get_ok_button()->set_disabled(!is_valid);
-}
-
-void SceneCreateDialog::update_error(Label *p_label, MsgType p_type, const String &p_msg) {
- p_label->set_text(String::utf8("• ") + p_msg);
- switch (p_type) {
- case MSG_OK:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- break;
- case MSG_ERROR:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- break;
- case MSG_WARNING:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- break;
+ if (root_name.is_empty()) {
+ validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name."), EditorValidationPanel::MSG_ERROR);
+ } else if (root_name != root_name.validate_node_name()) {
+ validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name characters have been replaced."), EditorValidationPanel::MSG_WARNING);
}
}
@@ -268,8 +237,6 @@ SceneCreateDialog::SceneCreateDialog() {
select_node_button = memnew(Button);
hb->add_child(select_node_button);
select_node_button->connect("pressed", callable_mp(this, &SceneCreateDialog::browse_types));
-
- node_type_group->connect("pressed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
}
{
@@ -282,7 +249,6 @@ SceneCreateDialog::SceneCreateDialog() {
scene_name_edit = memnew(LineEdit);
hb->add_child(scene_name_edit);
scene_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- scene_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
scene_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1));
List<String> extensions;
@@ -305,7 +271,6 @@ SceneCreateDialog::SceneCreateDialog() {
gc->add_child(root_name_edit);
root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting."));
root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- root_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1));
}
@@ -313,19 +278,16 @@ SceneCreateDialog::SceneCreateDialog() {
main_vb->add_child(spacing);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- status_panel = memnew(PanelContainer);
- main_vb->add_child(status_panel);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
- VBoxContainer *status_vb = memnew(VBoxContainer);
- status_panel->add_child(status_vb);
-
- file_error_label = memnew(Label);
- status_vb->add_child(file_error_label);
+ validation_panel = memnew(EditorValidationPanel);
+ main_vb->add_child(validation_panel);
+ validation_panel->add_line(MSG_ID_PATH, TTR("Scene name is valid."));
+ validation_panel->add_line(MSG_ID_ROOT, TTR("Root node valid."));
+ validation_panel->set_update_callback(callable_mp(this, &SceneCreateDialog::update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
- node_error_label = memnew(Label);
- status_vb->add_child(node_error_label);
+ node_type_group->connect("pressed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ scene_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ root_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
set_title(TTR("Create New Scene"));
set_min_size(Size2i(400 * EDSCALE, 0));
diff --git a/editor/scene_create_dialog.h b/editor/scene_create_dialog.h
index c6f40b928e..c7a4a09850 100644
--- a/editor/scene_create_dialog.h
+++ b/editor/scene_create_dialog.h
@@ -37,18 +37,17 @@ class ButtonGroup;
class CheckBox;
class CreateDialog;
class EditorFileDialog;
+class EditorValidationPanel;
class Label;
class LineEdit;
class OptionButton;
-class PanelContainer;
class SceneCreateDialog : public ConfirmationDialog {
GDCLASS(SceneCreateDialog, ConfirmationDialog);
- enum MsgType {
- MSG_OK,
- MSG_ERROR,
- MSG_WARNING,
+ enum {
+ MSG_ID_PATH,
+ MSG_ID_ROOT,
};
const StringName type_meta = StringName("type");
@@ -80,15 +79,12 @@ private:
OptionButton *scene_extension_picker = nullptr;
LineEdit *root_name_edit = nullptr;
- PanelContainer *status_panel = nullptr;
- Label *file_error_label = nullptr;
- Label *node_error_label = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
void accept_create();
void browse_types();
void on_type_picked();
void update_dialog();
- void update_error(Label *p_label, MsgType p_type, const String &p_msg);
protected:
void _notification(int p_what);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 0ac3875ad6..3096d20c19 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -121,6 +121,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
_tool_selected(TOOL_COPY);
} else if (ED_IS_SHORTCUT("scene_tree/paste_node", p_event)) {
_tool_selected(TOOL_PASTE);
+ } else if (ED_IS_SHORTCUT("scene_tree/paste_node_as_sibling", p_event)) {
+ _tool_selected(TOOL_PASTE_AS_SIBLING);
} else if (ED_IS_SHORTCUT("scene_tree/change_node_type", p_event)) {
_tool_selected(TOOL_REPLACE);
} else if (ED_IS_SHORTCUT("scene_tree/duplicate", p_event)) {
@@ -262,6 +264,9 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) {
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported.");
+
Ref<PackedScene> sdata = ResourceLoader::load(p_file);
if (!sdata.is_valid()) {
accept->set_text(vformat(TTR("Error loading scene from %s"), p_file));
@@ -282,7 +287,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
undo_redo->create_action(TTR("Replace with Branch Scene"));
Node *parent = base->get_parent();
- int pos = base->get_index();
+ int pos = base->get_index(false);
undo_redo->add_do_method(parent, "remove_child", base);
undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
@@ -511,7 +516,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
} break;
case TOOL_PASTE: {
- paste_nodes();
+ paste_nodes(false);
+ } break;
+ case TOOL_PASTE_AS_SIBLING: {
+ paste_nodes(true);
} break;
case TOOL_REPLACE: {
if (!profile_allow_editing) {
@@ -607,10 +615,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
selection.reverse();
}
- int lowest_id = common_parent->get_child_count() - 1;
+ int lowest_id = common_parent->get_child_count(false) - 1;
int highest_id = 0;
for (Node *E : selection) {
- int index = E->get_index();
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported.");
+ int index = E->get_index(false);
if (index > highest_id) {
highest_id = index;
@@ -624,7 +634,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
}
- if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count() - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) {
+ if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count(false) - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) {
break; // one or more nodes can not be moved
}
@@ -643,8 +653,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
ERR_FAIL_COND(!top_node->get_parent());
ERR_FAIL_COND(!bottom_node->get_parent());
- int bottom_node_pos = bottom_node->get_index();
- int top_node_pos_next = top_node->get_index() + (MOVING_DOWN ? 1 : -1);
+ int bottom_node_pos = bottom_node->get_index(false);
+ int top_node_pos_next = top_node->get_index(false) + (MOVING_DOWN ? 1 : -1);
undo_redo->add_do_method(top_node->get_parent(), "move_child", top_node, top_node_pos_next);
undo_redo->add_undo_method(bottom_node->get_parent(), "move_child", bottom_node, bottom_node_pos);
@@ -775,6 +785,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(node->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to set internal node as scene root, this is not supported.");
+
//check that from node to root, all owners are right
if (root->get_scene_inherited_state().is_valid()) {
@@ -811,7 +824,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
undo_redo->add_undo_method(node, "remove_child", root);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root);
undo_redo->add_undo_method(node->get_parent(), "add_child", node, true);
- undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index());
+ undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index(false));
undo_redo->add_undo_method(root, "set_owner", (Object *)nullptr);
undo_redo->add_undo_method(node, "set_owner", root);
_node_replace_owner(root, root, root, MODE_UNDO);
@@ -1903,8 +1916,10 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
if (p_nodes[ni] == p_new_parent) {
return; // Attempt to reparent to itself.
}
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(p_nodes[ni]->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported.");
- if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index()) {
+ if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index(false)) {
no_change = false;
}
}
@@ -1945,7 +1960,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
}
bool same_parent = new_parent == node->get_parent();
- if (same_parent && node->get_index() < p_position_in_parent + ni) {
+ if (same_parent && node->get_index(false) < p_position_in_parent + ni) {
inc--; // If the child will generate a gap when moved, adjust.
}
@@ -1987,7 +2002,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
}
undo_redo->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, new_position_in_parent);
- undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
+ undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index(false));
if (p_keep_global_xform) {
if (Object::cast_to<Node2D>(node)) {
@@ -2024,7 +2039,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
owners.push_back(E);
}
- int child_pos = node->get_index();
+ int child_pos = node->get_index(false);
undo_redo->add_undo_method(node->get_parent(), "add_child", node, true);
undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos);
@@ -2172,6 +2187,10 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
undo_redo->add_undo_method(scene_tree, "update_tree");
undo_redo->add_undo_reference(edited_scene);
} else {
+ for (const Node *E : remove_list) {
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to remove internal node, this is not supported.");
+ }
if (delete_tracks_checkbox->is_pressed() || p_cut) {
remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
HashMap<Node *, NodePath> path_renames;
@@ -2203,7 +2222,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
undo_redo->add_do_method(n->get_parent(), "remove_child", n);
undo_redo->add_undo_method(n->get_parent(), "add_child", n, true);
- undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index());
+ undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index(false));
if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == n) {
undo_redo->add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n);
}
@@ -2212,7 +2231,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
- undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
+ undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index(false));
}
}
undo_redo->commit_action();
@@ -2705,7 +2724,7 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
ERR_FAIL_MSG("Cannot perform drop above the root node!");
}
- to_pos = to_node->get_index();
+ to_pos = to_node->get_index(false);
to_node = to_node->get_parent();
} else if (p_type == 1) {
@@ -2721,15 +2740,15 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
if (_has_visible_children(to_node)) {
to_pos = 0;
} else {
- for (int i = to_node->get_index() + 1; i < to_node->get_parent()->get_child_count(); i++) {
- Node *c = to_node->get_parent()->get_child(i);
+ for (int i = to_node->get_index(false) + 1; i < to_node->get_parent()->get_child_count(false); i++) {
+ Node *c = to_node->get_parent()->get_child(i, false);
if (_is_node_visible(c)) {
lower_sibling = c;
break;
}
}
if (lower_sibling) {
- to_pos = lower_sibling->get_index();
+ to_pos = lower_sibling->get_index(false);
}
to_node = to_node->get_parent();
@@ -2957,6 +2976,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
if (selection.size() == 1 && !node_clipboard.is_empty()) {
menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node_as_sibling"), TOOL_PASTE);
}
menu->add_separator();
}
@@ -3330,7 +3350,7 @@ void SceneTreeDock::open_instance_child_dialog() {
_tool_selected(TOOL_INSTANTIATE, true);
}
-List<Node *> SceneTreeDock::paste_nodes() {
+List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
List<Node *> pasted_nodes;
if (node_clipboard.is_empty()) {
@@ -3360,6 +3380,10 @@ List<Node *> SceneTreeDock::paste_nodes() {
paste_parent = selection.back()->get();
}
+ if (p_paste_as_sibling) {
+ paste_parent = paste_parent->get_parent();
+ }
+
Node *owner = nullptr;
if (paste_parent) {
owner = paste_parent->get_owner();
@@ -3369,7 +3393,7 @@ List<Node *> SceneTreeDock::paste_nodes() {
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
- ur->create_action(TTR("Paste Node(s)"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ ur->create_action(vformat(p_paste_as_sibling ? TTR("Paste Node(s) as Sibling of %s") : TTR("Paste Node(s) as Child of %s"), paste_parent->get_name()), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
ur->add_do_method(editor_selection, "clear");
HashMap<Ref<Resource>, Ref<Resource>> resource_remap;
@@ -3761,15 +3785,16 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
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);
- ED_SHORTCUT("scene_tree/instantiate_scene", TTR("Instantiate Child Scene"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A);
+ ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node..."), KeyModifierMask::CMD_OR_CTRL | Key::A);
+ ED_SHORTCUT("scene_tree/instantiate_scene", TTR("Instantiate Child Scene..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A);
ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse Branch"));
ED_SHORTCUT("scene_tree/cut_node", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
ED_SHORTCUT("scene_tree/copy_node", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
ED_SHORTCUT("scene_tree/paste_node", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
- ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type"));
- ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script"));
- ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script"));
+ ED_SHORTCUT("scene_tree/paste_node_as_sibling", TTR("Paste as Sibling"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::V);
+ ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type..."));
+ ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script..."));
+ ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script..."));
ED_SHORTCUT("scene_tree/detach_script", TTR("Detach Script"));
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);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 21424925c5..9c111bce71 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -60,6 +60,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_CUT,
TOOL_COPY,
TOOL_PASTE,
+ TOOL_PASTE_AS_SIBLING,
TOOL_RENAME,
#ifdef MODULE_REGEX_ENABLED
TOOL_BATCH_RENAME,
@@ -329,7 +330,7 @@ public:
void open_add_child_dialog();
void open_instance_child_dialog();
- List<Node *> paste_nodes();
+ List<Node *> paste_nodes(bool p_paste_as_sibling = false);
List<Node *> get_node_clipboard() const;
ScriptCreateDialog *get_script_create_dialog() {
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index a4eabf409a..daac755529 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_validation_panel.h"
static String _get_parent_class_of_script(String p_path) {
if (!ResourceLoader::exists(p_path, "Script")) {
@@ -136,7 +137,6 @@ void ScriptCreateDialog::_notification(int p_what) {
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
parent_search_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -295,13 +295,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
}
// Let ScriptLanguage do custom validation.
- String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
- if (!path_error.is_empty()) {
- return path_error;
- }
-
- // All checks passed.
- return "";
+ return ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
}
String ScriptCreateDialog::_get_class_name() const {
@@ -314,12 +308,12 @@ String ScriptCreateDialog::_get_class_name() const {
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
is_class_name_valid = _validate_class(class_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_parent_name_changed(const String &p_parent) {
is_parent_name_valid = _validate_parent(parent_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_template_changed(int p_template) {
@@ -347,15 +341,15 @@ void ScriptCreateDialog::_template_changed(int p_template) {
}
}
}
+
// Update template label information.
- String template_info = String::utf8("• ");
+ String template_info = U"• ";
template_info += TTR("Template:");
template_info += " " + sinfo.name;
if (!sinfo.description.is_empty()) {
template_info += " - " + sinfo.description;
}
- template_info_label->set_text(template_info);
- template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
+ validation_panel->set_message(MSG_ID_TEMPLATE, template_info, EditorValidationPanel::MSG_INFO, false);
}
void ScriptCreateDialog::ok_pressed() {
@@ -367,7 +361,7 @@ void ScriptCreateDialog::ok_pressed() {
EditorSettings::get_singleton()->save();
is_new_script_created = true;
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_create_new() {
@@ -471,7 +465,7 @@ void ScriptCreateDialog::_language_changed(int l) {
EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected()));
_parent_name_changed(parent_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_built_in_pressed() {
@@ -482,13 +476,13 @@ void ScriptCreateDialog::_built_in_pressed() {
is_built_in = false;
_path_changed(file_path->get_text());
}
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_use_template_pressed() {
is_using_templates = use_templates->is_pressed();
EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates);
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) {
@@ -555,10 +549,9 @@ void ScriptCreateDialog::_path_changed(const String &p_path) {
is_path_valid = false;
is_new_script_created = true;
- String path_error = _validate_path(p_path, false);
+ path_error = _validate_path(p_path, false);
if (!path_error.is_empty()) {
- _msg_path_valid(false, path_error);
- _update_dialog();
+ validation_panel->update();
return;
}
@@ -567,32 +560,15 @@ void ScriptCreateDialog::_path_changed(const String &p_path) {
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (da->file_exists(p)) {
is_new_script_created = false;
- _msg_path_valid(true, TTR("File exists, it will be reused."));
}
is_path_valid = true;
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_path_submitted(const String &p_path) {
- ok_pressed();
-}
-
-void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
- error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
-void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
- path_error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ if (!get_ok_button()->is_disabled()) {
+ ok_pressed();
}
}
@@ -688,25 +664,25 @@ void ScriptCreateDialog::_update_template_menu() {
void ScriptCreateDialog::_update_dialog() {
// "Add Script Dialog" GUI logic and script checks.
_update_template_menu();
- bool script_ok = true;
// Is script path/name valid (order from top to bottom)?
if (!is_built_in && !is_path_valid) {
- _msg_script_valid(false, TTR("Invalid path."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR);
}
if (has_named_classes && (is_new_script_created && !is_class_name_valid)) {
- _msg_script_valid(false, TTR("Invalid class name."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid class name."), EditorValidationPanel::MSG_ERROR);
}
if (!is_parent_name_valid && is_new_script_created) {
- _msg_script_valid(false, TTR("Invalid inherited parent name or path."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid inherited parent name or path."), EditorValidationPanel::MSG_ERROR);
}
- if (script_ok) {
- _msg_script_valid(true, TTR("Script path/name is valid."));
+ if (validation_panel->is_valid() && !is_new_script_created) {
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK);
+ }
+
+ if (!path_error.is_empty()) {
+ validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR);
}
// Does script have named classes?
@@ -752,7 +728,11 @@ void ScriptCreateDialog::_update_dialog() {
// Is Script created or loaded from existing file?
- builtin_warning_label->set_visible(is_built_in);
+ if (is_built_in) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false);
+ } else if (_get_class_name() == parent_name->get_text()) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Warning: Having the script name be the same as a built-in type is usually not desired."), EditorValidationPanel::MSG_WARNING, false);
+ }
path_controls[0]->set_visible(!is_built_in);
path_controls[1]->set_visible(!is_built_in);
@@ -761,7 +741,6 @@ void ScriptCreateDialog::_update_dialog() {
// Check if the script name is the same as the parent class.
// This warning isn't relevant if the script is built-in.
- script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text());
bool is_new_file = is_built_in || is_new_script_created;
@@ -774,21 +753,16 @@ void ScriptCreateDialog::_update_dialog() {
if (is_new_file) {
if (is_built_in) {
- _msg_path_valid(true, TTR("Built-in script (into scene file)."));
- }
- if (is_new_script_created && is_path_valid) {
- _msg_path_valid(true, TTR("Will create a new script file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Built-in script (into scene file)."), EditorValidationPanel::MSG_OK);
}
} else {
+ template_inactive_message = TTR("Using existing script file.");
if (load_enabled) {
- template_inactive_message = TTR("Using existing script file.");
if (is_path_valid) {
- _msg_path_valid(true, TTR("Will load an existing script file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing script file."), EditorValidationPanel::MSG_OK);
}
} else {
- template_inactive_message = TTR("Using existing script file.");
- _msg_path_valid(false, TTR("Script file already exists."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("Script file already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -806,18 +780,7 @@ void ScriptCreateDialog::_update_dialog() {
template_menu->set_disabled(true);
template_menu->clear();
template_menu->add_item(template_inactive_message);
- }
- template_info_label->set_visible(!template_menu->is_disabled());
-
- get_ok_button()->set_disabled(!script_ok);
-
- Callable entered_call = callable_mp(this, &ScriptCreateDialog::_path_submitted);
- if (script_ok) {
- if (!file_path->is_connected("text_submitted", entered_call)) {
- file_path->connect("text_submitted", entered_call);
- }
- } else if (file_path->is_connected("text_submitted", entered_call)) {
- file_path->disconnect("text_submitted", entered_call);
+ validation_panel->set_message(MSG_ID_TEMPLATE, "", EditorValidationPanel::MSG_INFO);
}
}
@@ -967,47 +930,23 @@ ScriptCreateDialog::ScriptCreateDialog() {
/* Information Messages Field */
- VBoxContainer *vb = memnew(VBoxContainer);
-
- error_label = memnew(Label);
- vb->add_child(error_label);
-
- path_error_label = memnew(Label);
- vb->add_child(path_error_label);
-
- builtin_warning_label = memnew(Label);
- builtin_warning_label->set_text(
- TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."));
- vb->add_child(builtin_warning_label);
- builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- builtin_warning_label->hide();
-
- script_name_warning_label = memnew(Label);
- script_name_warning_label->set_text(
- TTR("Warning: Having the script name be the same as a built-in type is usually not desired."));
- vb->add_child(script_name_warning_label);
- script_name_warning_label->add_theme_color_override("font_color", Color(1, 0.85, 0.4));
- script_name_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- script_name_warning_label->hide();
-
- template_info_label = memnew(Label);
- vb->add_child(template_info_label);
- template_info_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
-
- status_panel = memnew(PanelContainer);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(vb);
+ validation_panel = memnew(EditorValidationPanel);
+ validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script path/name is valid."));
+ validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new script file."));
+ validation_panel->add_line(MSG_ID_BUILT_IN);
+ validation_panel->add_line(MSG_ID_TEMPLATE);
+ validation_panel->set_update_callback(callable_mp(this, &ScriptCreateDialog::_update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
/* Spacing */
Control *spacing = memnew(Control);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- vb = memnew(VBoxContainer);
+ VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(gc);
vb->add_child(spacing);
- vb->add_child(status_panel);
+ vb->add_child(validation_panel);
add_child(vb);
/* Language */
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index fa42b96746..ea2f480c57 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -41,17 +41,20 @@
class CreateDialog;
class EditorFileDialog;
+class EditorValidationPanel;
class ScriptCreateDialog : public ConfirmationDialog {
GDCLASS(ScriptCreateDialog, ConfirmationDialog);
+ enum {
+ MSG_ID_SCRIPT,
+ MSG_ID_PATH,
+ MSG_ID_BUILT_IN,
+ MSG_ID_TEMPLATE,
+ };
+
LineEdit *class_name = nullptr;
- Label *error_label = nullptr;
- Label *path_error_label = nullptr;
- Label *builtin_warning_label = nullptr;
- Label *script_name_warning_label = nullptr;
- Label *template_info_label = nullptr;
- PanelContainer *status_panel = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
LineEdit *parent_name = nullptr;
Button *parent_browse_button = nullptr;
Button *parent_search_button = nullptr;
@@ -67,6 +70,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
AcceptDialog *alert = nullptr;
CreateDialog *select_class = nullptr;
bool is_browsing_parent = false;
+ String path_error;
String template_inactive_message;
String initial_bp;
bool is_new_script_created = true;
@@ -113,8 +117,6 @@ class ScriptCreateDialog : public ConfirmationDialog {
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
- void _msg_script_valid(bool valid, const String &p_msg = String());
- void _msg_path_valid(bool valid, const String &p_msg = String());
void _update_template_menu();
void _update_dialog();
ScriptLanguage::ScriptTemplate _get_current_template() const;
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index 607b53718b..9a7b9bc84d 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_scale.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/resources/shader_include.h"
#include "scene/resources/visual_shader.h"
#include "servers/rendering/shader_types.h"
@@ -89,7 +90,6 @@ void ShaderCreateDialog::_update_theme() {
}
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
}
void ShaderCreateDialog::_update_language_info() {
@@ -147,7 +147,7 @@ void ShaderCreateDialog::ok_pressed() {
}
is_new_shader_created = true;
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_create_new() {
@@ -327,7 +327,7 @@ void ShaderCreateDialog::_type_changed(int p_language) {
}
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected()));
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_built_in_toggled(bool p_enabled) {
@@ -337,7 +337,7 @@ void ShaderCreateDialog::_built_in_toggled(bool p_enabled) {
} else {
_path_changed(file_path->get_text());
}
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_browse_path() {
@@ -378,10 +378,9 @@ void ShaderCreateDialog::_path_changed(const String &p_path) {
is_path_valid = false;
is_new_shader_created = true;
- String path_error = _validate_path(p_path);
+ path_error = _validate_path(p_path);
if (!path_error.is_empty()) {
- _msg_path_valid(false, path_error);
- _update_dialog();
+ validation_panel->update();
return;
}
@@ -389,15 +388,16 @@ void ShaderCreateDialog::_path_changed(const String &p_path) {
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (f->file_exists(p)) {
is_new_shader_created = false;
- _msg_path_valid(true, TTR("File exists, it will be reused."));
}
is_path_valid = true;
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_path_submitted(const String &p_path) {
- ok_pressed();
+ if (!get_ok_button()->is_disabled()) {
+ ok_pressed();
+ }
}
void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) {
@@ -490,33 +490,14 @@ String ShaderCreateDialog::_validate_path(const String &p_path) {
return "";
}
-void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
- error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
-void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
- path_error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
void ShaderCreateDialog::_update_dialog() {
- bool shader_ok = true;
-
if (!is_built_in && !is_path_valid) {
- _msg_script_valid(false, TTR("Invalid path."));
- shader_ok = false;
+ validation_panel->set_message(MSG_ID_SHADER, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR);
}
- if (shader_ok) {
- _msg_script_valid(true, TTR("Shader path/name is valid."));
+ if (!path_error.is_empty()) {
+ validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR);
+ } else if (validation_panel->is_valid() && !is_new_shader_created) {
+ validation_panel->set_message(MSG_ID_SHADER, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK);
}
if (!built_in_enabled) {
internal->set_pressed(false);
@@ -537,37 +518,23 @@ void ShaderCreateDialog::_update_dialog() {
internal->set_disabled(!built_in_enabled);
- builtin_warning_label->set_visible(is_built_in);
+ if (is_built_in) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in shaders can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false);
+ }
if (is_built_in) {
set_ok_button_text(TTR("Create"));
- _msg_path_valid(true, TTR("Built-in shader (into scene file)."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Built-in shader (into scene file)."), EditorValidationPanel::MSG_OK);
} else if (is_new_shader_created) {
set_ok_button_text(TTR("Create"));
- if (is_path_valid) {
- _msg_path_valid(true, TTR("Will create a new shader file."));
- }
} else if (load_enabled) {
set_ok_button_text(TTR("Load"));
if (is_path_valid) {
- _msg_path_valid(true, TTR("Will load an existing shader file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing shader file."), EditorValidationPanel::MSG_OK);
}
} else {
set_ok_button_text(TTR("Create"));
- _msg_path_valid(false, TTR("Shader file already exists."));
-
- shader_ok = false;
- }
-
- get_ok_button()->set_disabled(!shader_ok);
-
- Callable entered_call = callable_mp(this, &ShaderCreateDialog::_path_submitted);
- if (shader_ok) {
- if (!file_path->is_connected("text_submitted", entered_call)) {
- file_path->connect("text_submitted", entered_call);
- }
- } else if (file_path->is_connected("text_submitted", entered_call)) {
- file_path->disconnect("text_submitted", entered_call);
+ validation_panel->set_message(MSG_ID_PATH, TTR("Shader file already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -588,35 +555,22 @@ ShaderCreateDialog::ShaderCreateDialog() {
// Error Fields.
- VBoxContainer *vb = memnew(VBoxContainer);
-
- error_label = memnew(Label);
- vb->add_child(error_label);
-
- path_error_label = memnew(Label);
- vb->add_child(path_error_label);
-
- builtin_warning_label = memnew(Label);
- builtin_warning_label->set_text(
- TTR("Note: Built-in shaders can't be edited using an external editor."));
- vb->add_child(builtin_warning_label);
- builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- builtin_warning_label->hide();
-
- status_panel = memnew(PanelContainer);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(vb);
+ validation_panel = memnew(EditorValidationPanel);
+ validation_panel->add_line(MSG_ID_SHADER, TTR("Shader path/name is valid."));
+ validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new shader file."));
+ validation_panel->add_line(MSG_ID_BUILT_IN);
+ validation_panel->set_update_callback(callable_mp(this, &ShaderCreateDialog::_update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
// Spacing.
Control *spacing = memnew(Control);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- vb = memnew(VBoxContainer);
+ VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(gc);
vb->add_child(spacing);
- vb->add_child(status_panel);
+ vb->add_child(validation_panel);
add_child(vb);
// Type.
diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h
index 729a7b5bd4..d6d9f10020 100644
--- a/editor/shader_create_dialog.h
+++ b/editor/shader_create_dialog.h
@@ -40,10 +40,17 @@
#include "scene/gui/panel_container.h"
class EditorFileDialog;
+class EditorValidationPanel;
class ShaderCreateDialog : public ConfirmationDialog {
GDCLASS(ShaderCreateDialog, ConfirmationDialog);
+ enum {
+ MSG_ID_SHADER,
+ MSG_ID_PATH,
+ MSG_ID_BUILT_IN,
+ };
+
struct ShaderTypeData {
List<String> extensions;
String default_extension;
@@ -53,10 +60,7 @@ class ShaderCreateDialog : public ConfirmationDialog {
List<ShaderTypeData> type_data;
GridContainer *gc = nullptr;
- Label *error_label = nullptr;
- Label *path_error_label = nullptr;
- Label *builtin_warning_label = nullptr;
- PanelContainer *status_panel = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
OptionButton *type_menu = nullptr;
OptionButton *mode_menu = nullptr;
OptionButton *template_menu = nullptr;
@@ -67,6 +71,7 @@ class ShaderCreateDialog : public ConfirmationDialog {
AcceptDialog *alert = nullptr;
String initial_base_path;
+ String path_error;
bool is_new_shader_created = true;
bool is_path_valid = false;
bool is_built_in = false;
@@ -93,8 +98,6 @@ class ShaderCreateDialog : public ConfirmationDialog {
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
- void _msg_script_valid(bool valid, const String &p_msg = String());
- void _msg_path_valid(bool valid, const String &p_msg = String());
void _update_dialog();
protected:
diff --git a/main/main.cpp b/main/main.cpp
index 2d582f1a96..e6dd576b8a 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -318,6 +318,7 @@ void finalize_display() {
void initialize_navigation_server() {
ERR_FAIL_COND(navigation_server_3d != nullptr);
+ ERR_FAIL_COND(navigation_server_2d != nullptr);
// Init 3D Navigation Server
navigation_server_3d = NavigationServer3DManager::new_default_server();
@@ -330,6 +331,7 @@ void initialize_navigation_server() {
// Should be impossible, but make sure it's not null.
ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D.");
+ navigation_server_3d->init();
// Init 2D Navigation Server
navigation_server_2d = memnew(NavigationServer2D);
@@ -337,9 +339,12 @@ void initialize_navigation_server() {
}
void finalize_navigation_server() {
+ ERR_FAIL_NULL(navigation_server_3d);
+ navigation_server_3d->finish();
memdelete(navigation_server_3d);
navigation_server_3d = nullptr;
+ ERR_FAIL_NULL(navigation_server_2d);
memdelete(navigation_server_2d);
navigation_server_2d = nullptr;
}
@@ -453,6 +458,7 @@ void Main::print_help(const char *p_binary) {
#if DEBUG_ENABLED
OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
#endif
+ OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
#if defined(DEBUG_ENABLED)
@@ -581,6 +587,8 @@ Error Main::test_setup() {
theme_db->initialize_theme();
register_scene_singletons();
+ initialize_navigation_server();
+
ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE);
/* Use one with the most features available. */
@@ -639,6 +647,8 @@ void Main::test_cleanup() {
finalize_theme_db();
+ finalize_navigation_server();
+
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
unregister_server_types();
@@ -1010,6 +1020,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--gpu-abort") {
Engine::singleton->abort_on_gpu_errors = true;
#endif
+ } else if (I->get() == "--generate-spirv-debug-info") {
+ Engine::singleton->generate_spirv_debug_info = true;
} else if (I->get() == "--tablet-driver") {
if (I->next()) {
tablet_driver = I->next()->get();
@@ -1531,6 +1543,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
}
+#ifdef TOOLS_ENABLED
+ if (editor) {
+ Engine::get_singleton()->set_editor_hint(true);
+ }
+#endif
+
// Initialize user data dir.
OS::get_singleton()->ensure_user_data_dir();
@@ -1558,7 +1576,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#ifdef TOOLS_ENABLED
if (editor) {
packed_data->set_disabled(true);
- Engine::get_singleton()->set_editor_hint(true);
main_args.push_back("--editor");
if (!init_windowed) {
init_maximized = true;
@@ -3113,6 +3130,7 @@ bool Main::start() {
Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"),
GLOBAL_GET("display/window/size/viewport_height"));
real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale");
+ String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode");
Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
if (stretch_mode == "canvas_items") {
@@ -3132,8 +3150,14 @@ bool Main::start() {
cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND;
}
+ Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL;
+ if (stretch_scale_mode == "integer") {
+ cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER;
+ }
+
sml->get_root()->set_content_scale_mode(cs_sm);
sml->get_root()->set_content_scale_aspect(cs_aspect);
+ sml->get_root()->set_content_scale_stretch(cs_stretch);
sml->get_root()->set_content_scale_size(stretch_size);
sml->get_root()->set_content_scale_factor(stretch_scale);
diff --git a/methods.py b/methods.py
index 571a3f739e..7c1781d699 100644
--- a/methods.py
+++ b/methods.py
@@ -868,6 +868,9 @@ def generate_vs_project(env, num_jobs, project_name="godot"):
if env["custom_modules"]:
common_build_postfix.append("custom_modules=%s" % env["custom_modules"])
+ if env["windows_subsystem"] == "console":
+ common_build_postfix.append("windows_subsystem=console")
+
if env["precision"] == "double":
common_build_postfix.append("precision=double")
diff --git a/misc/error_suppressions/tsan.txt b/misc/error_suppressions/tsan.txt
index a67a22810b..7c3d836f6c 100644
--- a/misc/error_suppressions/tsan.txt
+++ b/misc/error_suppressions/tsan.txt
@@ -4,4 +4,5 @@
deadlock:tests/core/templates/test_command_queue.h
deadlock:modules/text_server_adv/text_server_adv.cpp
deadlock:modules/text_server_fb/text_server_fb.cpp
+race:modules/navigation/nav_map.cpp
diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected
index 9dc852d5b9..5f982cbff6 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -433,3 +433,21 @@ GH-79606
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/shader_create_from_bytecode/arguments': size changed value in new API, from 1 to 2.
Added optional argument. Compatibility method registered.
+
+
+GH-78266
+--------
+Validate extension JSON: API was removed: classes/FontFile/properties/fallbacks
+Validate extension JSON: API was removed: classes/FontVariation/properties/fallbacks
+Validate extension JSON: API was removed: classes/SystemFont/properties/fallbacks
+
+The property was moved to their common base class Font.
+The setters and getters were already in Font, so this shouldn't affect compatibility.
+
+
+GH-36493
+--------
+Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_icon_shortcut/arguments': size changed value in new API, from 4 to 5.
+Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_shortcut/arguments': size changed value in new API, from 3 to 4.
+
+Added optional argument. Compatibility methods registered.
diff --git a/misc/scripts/validate_extension_api.sh b/misc/scripts/validate_extension_api.sh
index e06d52115a..f2f7c28e70 100755
--- a/misc/scripts/validate_extension_api.sh
+++ b/misc/scripts/validate_extension_api.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-set -uo pipefail
+set -o pipefail
if [ ! -f "version.py" ]; then
echo "Warning: This script is intended to be run from the root of the Godot repository."
@@ -18,7 +18,7 @@ make_annotation()
local body=$2
local type=$3
local file=$4
- if [ ! -v GITHUB_OUTPUT ]; then
+ if [[ "$GITHUB_OUTPUT" == "" ]]; then
echo "$title"
echo "$body"
else
@@ -43,8 +43,8 @@ while read -r file; do
awk '/^Validate extension JSON:/' - < "$file" | sort > "$allowed_errors"
# Differences between the expected and actual errors
- new_validation_error="$(comm "$validation_output" "$allowed_errors" -23)"
- obsolete_validation_error="$(comm "$validation_output" "$allowed_errors" -13)"
+ new_validation_error="$(comm -23 "$validation_output" "$allowed_errors")"
+ obsolete_validation_error="$(comm -13 "$validation_output" "$allowed_errors")"
if [ -n "$obsolete_validation_error" ]; then
make_annotation "The following validation errors no longer occur (compared to $reference_tag):" "$obsolete_validation_error" warning "$file"
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 9a0f121e19..96d89ff486 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -16,7 +16,8 @@
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
The [Mesh] resource to use as a CSG shape.
- [b]Note:[/b] When using an [ArrayMesh], avoid meshes with vertex normals unless a flat shader is required. By default, CSGMesh will ignore the mesh's vertex normals and use a smooth shader calculated using the faces' normals. If a flat shader is required, ensure that all faces' vertex normals are parallel.
+ [b]Note:[/b] When using an [ArrayMesh], all vertex attributes except [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL] and [constant Mesh.ARRAY_TEX_UV] are left unused. Only [constant Mesh.ARRAY_VERTEX] and [constant Mesh.ARRAY_TEX_UV] will be passed to the GPU.
+ [constant Mesh.ARRAY_NORMAL] is only used to determine which faces require the use of flat shading. By default, CSGMesh will ignore the mesh's vertex normals, recalculate them for each vertex and use a smooth shader. If a flat shader is required for a face, ensure that all vertex normals of the face are approximately equal.
</member>
</members>
</class>
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index 779ce165d2..967a511e1e 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -109,6 +109,15 @@ env_oidn.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds
env_thirdparty = env_oidn.Clone()
env_thirdparty.disable_warnings()
+
+if env["disable_exceptions"]:
+ # OIDN hard-requires exceptions, so we re-enable them here.
+ if env.msvc and ("_HAS_EXCEPTIONS", 0) in env_thirdparty["CPPDEFINES"]:
+ env_thirdparty["CPPDEFINES"].remove(("_HAS_EXCEPTIONS", 0))
+ env_thirdparty.AppendUnique(CCFLAGS=["/EHsc"])
+ elif not env.msvc and "-fno-exceptions" in env_thirdparty["CCFLAGS"]:
+ env_thirdparty["CCFLAGS"].remove("-fno-exceptions")
+
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp
index 0ace89caa5..88aaa006b5 100644
--- a/modules/enet/enet_connection.cpp
+++ b/modules/enet/enet_connection.cpp
@@ -142,7 +142,7 @@ ENetConnection::EventType ENetConnection::_parse_event(const ENetEvent &p_event,
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_RECEIVE: {
- // Packet reveived.
+ // Packet received.
if (p_event.peer->data != nullptr) {
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index af5bd2929c..63f12ea1c1 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -343,23 +343,22 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
int packet_flags = 0;
int channel = SYSCH_RELIABLE;
int tr_channel = get_transfer_channel();
+ switch (get_transfer_mode()) {
+ case TRANSFER_MODE_UNRELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case TRANSFER_MODE_RELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_RELIABLE;
+ channel = SYSCH_RELIABLE;
+ } break;
+ }
if (tr_channel > 0) {
channel = SYSCH_MAX + tr_channel - 1;
- } else {
- switch (get_transfer_mode()) {
- case TRANSFER_MODE_UNRELIABLE: {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED: {
- packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_RELIABLE: {
- packet_flags = ENET_PACKET_FLAG_RELIABLE;
- channel = SYSCH_RELIABLE;
- } break;
- }
}
#ifdef DEBUG_ENABLED
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index b54dc502ae..862799a6ec 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -145,6 +145,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
// Check if it's the whole line.
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+ // Don't skip comments, for highlighting markers.
+ if (color_regions[in_region].start_key == "#") {
+ break;
+ }
if (from + end_key_length > line_length) {
// If it's key length and there is a '\', dont skip to highlight esc chars.
if (str.find("\\", from) >= 0) {
@@ -163,7 +167,8 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
break;
}
- if (j == line_length) {
+ // Don't skip comments, for highlighting markers.
+ if (j == line_length && color_regions[in_region].start_key != "#") {
continue;
}
}
@@ -185,56 +190,83 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
highlighter_info["color"] = region_color;
color_map[j] = highlighter_info;
- // Search the line.
- int region_end_index = -1;
- int end_key_length = color_regions[in_region].end_key.length();
- const char32_t *end_key = color_regions[in_region].end_key.get_data();
- for (; from < line_length; from++) {
- if (line_length - from < end_key_length) {
- // Don't break if '\' to highlight esc chars.
- if (str.find("\\", from) < 0) {
- break;
+ if (color_regions[in_region].start_key == "#") {
+ int marker_start_pos = from;
+ int marker_len = 0;
+ while (from <= line_length) {
+ if (from < line_length && is_unicode_identifier_continue(str[from])) {
+ marker_len++;
+ } else {
+ if (marker_len > 0) {
+ HashMap<String, CommentMarkerLevel>::ConstIterator E = comment_markers.find(str.substr(marker_start_pos, marker_len));
+ if (E) {
+ Dictionary marker_highlighter_info;
+ marker_highlighter_info["color"] = comment_marker_colors[E->value];
+ color_map[marker_start_pos] = marker_highlighter_info;
+
+ Dictionary marker_continue_highlighter_info;
+ marker_continue_highlighter_info["color"] = region_color;
+ color_map[from] = marker_continue_highlighter_info;
+ }
+ }
+ marker_start_pos = from + 1;
+ marker_len = 0;
}
+ from++;
}
+ from = line_length - 1;
+ j = from;
+ } else {
+ // Search the line.
+ int region_end_index = -1;
+ int end_key_length = color_regions[in_region].end_key.length();
+ const char32_t *end_key = color_regions[in_region].end_key.get_data();
+ for (; from < line_length; from++) {
+ if (line_length - from < end_key_length) {
+ // Don't break if '\' to highlight esc chars.
+ if (str.find("\\", from) < 0) {
+ break;
+ }
+ }
- if (!is_symbol(str[from])) {
- continue;
- }
+ if (!is_symbol(str[from])) {
+ continue;
+ }
- if (str[from] == '\\') {
- Dictionary escape_char_highlighter_info;
- escape_char_highlighter_info["color"] = symbol_color;
- color_map[from] = escape_char_highlighter_info;
+ if (str[from] == '\\') {
+ Dictionary escape_char_highlighter_info;
+ escape_char_highlighter_info["color"] = symbol_color;
+ color_map[from] = escape_char_highlighter_info;
- from++;
+ from++;
- Dictionary region_continue_highlighter_info;
- prev_color = region_color;
- region_continue_highlighter_info["color"] = region_color;
- color_map[from + 1] = region_continue_highlighter_info;
- continue;
- }
+ Dictionary region_continue_highlighter_info;
+ region_continue_highlighter_info["color"] = region_color;
+ color_map[from + 1] = region_continue_highlighter_info;
+ continue;
+ }
- region_end_index = from;
- for (int k = 0; k < end_key_length; k++) {
- if (end_key[k] != str[from + k]) {
- region_end_index = -1;
+ region_end_index = from;
+ for (int k = 0; k < end_key_length; k++) {
+ if (end_key[k] != str[from + k]) {
+ region_end_index = -1;
+ break;
+ }
+ }
+
+ if (region_end_index != -1) {
break;
}
}
-
- if (region_end_index != -1) {
- break;
+ j = from + (end_key_length - 1);
+ if (region_end_index == -1) {
+ color_region_cache[p_line] = in_region;
}
}
prev_type = REGION;
prev_text = "";
prev_column = j;
- j = from + (end_key_length - 1);
- if (region_end_index == -1) {
- color_region_cache[p_line] = in_region;
- }
in_region = -1;
prev_is_char = false;
@@ -706,6 +738,9 @@ void GDScriptSyntaxHighlighter::_update_cache() {
node_ref_color = Color(0.39, 0.76, 0.35);
annotation_color = Color(1.0, 0.7, 0.45);
string_name_color = Color(1.0, 0.76, 0.65);
+ comment_marker_colors[COMMENT_MARKER_CRITICAL] = Color(0.77, 0.35, 0.35);
+ comment_marker_colors[COMMENT_MARKER_WARNING] = Color(0.72, 0.61, 0.48);
+ comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.56, 0.67, 0.51);
} else {
function_definition_color = Color(0, 0.6, 0.6);
global_function_color = Color(0.36, 0.18, 0.72);
@@ -713,6 +748,9 @@ void GDScriptSyntaxHighlighter::_update_cache() {
node_ref_color = Color(0.0, 0.5, 0);
annotation_color = Color(0.8, 0.37, 0);
string_name_color = Color(0.8, 0.56, 0.45);
+ comment_marker_colors[COMMENT_MARKER_CRITICAL] = Color(0.8, 0.14, 0.14);
+ comment_marker_colors[COMMENT_MARKER_WARNING] = Color(0.75, 0.39, 0.03);
+ comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.24, 0.54, 0.09);
}
EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
@@ -721,6 +759,14 @@ void GDScriptSyntaxHighlighter::_update_cache() {
EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_reference_color", node_ref_color);
EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color);
EDITOR_DEF("text_editor/theme/highlighting/gdscript/string_name_color", string_name_color);
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/critical_color", comment_marker_colors[COMMENT_MARKER_CRITICAL]);
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/warning_color", comment_marker_colors[COMMENT_MARKER_WARNING]);
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/notice_color", comment_marker_colors[COMMENT_MARKER_NOTICE]);
+ // The list is based on <https://github.com/KDE/syntax-highlighting/blob/master/data/syntax/alert.xml>.
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/critical_list", "ALERT,ATTENTION,CAUTION,CRITICAL,DANGER,SECURITY");
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/warning_list", "BUG,DEPRECATED,FIXME,HACK,TASK,TBD,TODO,WARNING");
+ EDITOR_DEF("text_editor/theme/highlighting/comment_markers/notice_list", "INFO,NOTE,NOTICE,TEST,TESTING");
+
if (text_edit_color_theme == "Default" || godot_2_theme) {
EditorSettings::get_singleton()->set_initial_value(
"text_editor/theme/highlighting/gdscript/function_definition_color",
@@ -746,6 +792,18 @@ void GDScriptSyntaxHighlighter::_update_cache() {
"text_editor/theme/highlighting/gdscript/string_name_color",
string_name_color,
true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/comment_markers/critical_color",
+ comment_marker_colors[COMMENT_MARKER_CRITICAL],
+ true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/comment_markers/warning_color",
+ comment_marker_colors[COMMENT_MARKER_WARNING],
+ true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/comment_markers/notice_color",
+ comment_marker_colors[COMMENT_MARKER_NOTICE],
+ true);
}
function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color");
@@ -755,6 +813,23 @@ void GDScriptSyntaxHighlighter::_update_cache() {
annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color");
string_name_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/string_name_color");
type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
+ comment_marker_colors[COMMENT_MARKER_CRITICAL] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/critical_color");
+ comment_marker_colors[COMMENT_MARKER_WARNING] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/warning_color");
+ comment_marker_colors[COMMENT_MARKER_NOTICE] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/notice_color");
+
+ comment_markers.clear();
+ Vector<String> critical_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/critical_list").operator String().split(",", false);
+ for (int i = 0; i < critical_list.size(); i++) {
+ comment_markers[critical_list[i]] = COMMENT_MARKER_CRITICAL;
+ }
+ Vector<String> warning_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/warning_list").operator String().split(",", false);
+ for (int i = 0; i < warning_list.size(); i++) {
+ comment_markers[warning_list[i]] = COMMENT_MARKER_WARNING;
+ }
+ Vector<String> notice_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/notice_list").operator String().split(",", false);
+ for (int i = 0; i < notice_list.size(); i++) {
+ comment_markers[notice_list[i]] = COMMENT_MARKER_NOTICE;
+ }
}
void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index aceb644658..fe3b63d713 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -84,6 +84,15 @@ private:
Color string_name_color;
Color type_color;
+ enum CommentMarkerLevel {
+ COMMENT_MARKER_CRITICAL,
+ COMMENT_MARKER_WARNING,
+ COMMENT_MARKER_NOTICE,
+ COMMENT_MARKER_MAX,
+ };
+ Color comment_marker_colors[COMMENT_MARKER_MAX];
+ HashMap<String, CommentMarkerLevel> comment_markers;
+
void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
public:
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index cb04913620..9f9accf507 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3301,17 +3301,26 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) {
GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = SNAME("Node");
- result.builtin_type = Variant::OBJECT;
+ result.kind = GDScriptParser::DataType::VARIANT;
- if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
- push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
+ if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, SNAME("Node"))) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") on a class that isn't a node.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
+ }
+
+ if (static_context) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") in a static function.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
}
mark_lambda_use_self();
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = SNAME("Node");
p_get_node->set_datatype(result);
}
@@ -3469,6 +3478,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
for (GDScriptParser::ClassNode *script_class : script_classes) {
if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
+ if (script_class->outer != nullptr) {
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
+ }
return;
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index d27ea974e3..3019354d93 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1076,7 +1076,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const PropertyInfo &E : members) {
- int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.class_name);
+ int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
@@ -1150,7 +1150,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.name.contains("/")) {
continue;
}
- int location = p_recursion_depth + _get_property_location(type, E.class_name);
+ int location = p_recursion_depth + _get_property_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 9e14e43a58..3b89f077bd 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -67,6 +67,10 @@ ObjectID GDScriptLambdaCallable::get_object() const {
return script->get_instance_id();
}
+StringName GDScriptLambdaCallable::get_method() const {
+ return function->get_name();
+}
+
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
int captures_amount = captures.size();
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 33bdf6dfc1..1c7a18fb9d 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -56,6 +56,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ StringName get_method() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index debc85ebbf..b76ceea11f 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2446,7 +2446,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
complete_extents(operation);
if (operation->right_operand == nullptr) {
- push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
+ push_error(vformat(R"(Expected expression after "%s" operator.)", op.get_name()));
}
// TODO: Also for unary, ternary, and assignment.
@@ -3035,10 +3035,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
if (previous.type == GDScriptTokenizer::Token::DOLLAR) {
// Detect initial slash, which will be handled in the loop if it matches.
match(GDScriptTokenizer::Token::SLASH);
-#ifdef DEBUG_ENABLED
} else {
get_node->use_dollar = false;
-#endif
}
int context_argument = 0;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 20f5dcf06d..71660d8f60 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -848,9 +848,7 @@ public:
struct GetNodeNode : public ExpressionNode {
String full_path;
-#ifdef DEBUG_ENABLED
bool use_dollar = true;
-#endif
GetNodeNode() {
type = GET_NODE;
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index a4dd8a8d3c..265e624b6c 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -63,6 +63,10 @@ ObjectID GDScriptRPCCallable::get_object() const {
return object->get_instance_id();
}
+StringName GDScriptRPCCallable::get_method() const {
+ return method;
+}
+
void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error);
}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
index c1007b18b0..66052157be 100644
--- a/modules/gdscript/gdscript_rpc_callable.h
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -51,6 +51,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ StringName get_method() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 4f374b63b0..42b983ef45 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1162,15 +1162,6 @@ void GDScriptTokenizer::check_indent() {
_advance();
}
- if (mixed && !(line_continuation || multiline_mode)) {
- Token error = make_error("Mixed use of tabs and spaces for indentation.");
- error.start_line = line;
- error.start_column = 1;
- error.leftmost_column = 1;
- error.rightmost_column = column;
- push_error(error);
- }
-
if (_is_at_end()) {
// Reached the end with an empty line, so just dedent as much as needed.
pending_indents -= indent_level();
@@ -1214,6 +1205,15 @@ void GDScriptTokenizer::check_indent() {
continue;
}
+ if (mixed && !line_continuation && !multiline_mode) {
+ Token error = make_error("Mixed use of tabs and spaces for indentation.");
+ error.start_line = line;
+ error.start_column = 1;
+ error.leftmost_column = 1;
+ error.rightmost_column = column;
+ push_error(error);
+ }
+
if (line_continuation || multiline_mode) {
// We cleared up all the whitespace at the beginning of the line.
// But if this is a continuation or multiline mode and we don't want any indentation change.
diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h
index b9a54cf818..3782945e07 100644
--- a/modules/gdscript/language_server/godot_lsp.h
+++ b/modules/gdscript/language_server/godot_lsp.h
@@ -1566,7 +1566,7 @@ struct SignatureHelp {
/**
* The active signature. If omitted or the value lies outside the
* range of `signatures` the value defaults to zero or is ignored if
- * `signatures.length === 0`. Whenever possible implementors should
+ * `signatures.length === 0`. Whenever possible implementers should
* make an active decision about the active signature and shouldn't
* rely on a default value.
* In future version of the protocol this property might become
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd
new file mode 100644
index 0000000000..caeea46977
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd
@@ -0,0 +1,9 @@
+# GH-75645
+
+extends Node
+
+static func static_func():
+ var a = $Node
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out
new file mode 100644
index 0000000000..1910b3e66b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot use shorthand "get_node()" notation ("$") in a static function.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd
new file mode 100644
index 0000000000..51c589b8e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd
@@ -0,0 +1,21 @@
+# GH-80508
+
+class A:
+ func a():
+ return A.new()
+ func b():
+ return B.new()
+
+class B:
+ func a():
+ return A.new()
+ func b():
+ return B.new()
+
+func test():
+ var a := A.new()
+ var b := B.new()
+ print(a.a() is A)
+ print(a.b() is B)
+ print(b.a() is A)
+ print(b.b() is B)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out
new file mode 100644
index 0000000000..f9783e4362
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/.editorconfig b/modules/gdscript/tests/scripts/parser/.editorconfig
new file mode 100644
index 0000000000..fa43b3ad78
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/.editorconfig
@@ -0,0 +1,2 @@
+[*.{gd,out}]
+trim_trailing_whitespace = false
diff --git a/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd
new file mode 100644
index 0000000000..7ee2708999
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd
@@ -0,0 +1,20 @@
+# Empty line:
+
+
+# Comment line:
+ # Comment.
+
+func test():
+ print(1)
+
+ if true:
+
+ # Empty line:
+
+
+ print(2)
+
+ # Comment line:
+ # Comment.
+
+ print(3)
diff --git a/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out
new file mode 100644
index 0000000000..c40e402ba3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+1
+2
+3
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index 622910761d..2b070c24b8 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -32,6 +32,7 @@
#include "glslang_resource_limits.h"
+#include "core/config/engine.h"
#include "servers/rendering/rendering_device.h"
#include <glslang/Include/Types.h>
@@ -56,7 +57,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2;
glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
- glslang::TShader::ForbidIncluder includer;
if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
if (capabilities->version_major == 1 && capabilities->version_minor == 0) {
@@ -127,23 +127,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
}
EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
- const int DefaultVersion = 100;
- std::string pre_processed_code;
-
- //preprocess
- if (!shader.preprocess(&DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
- if (r_error) {
- (*r_error) = "Failed pre-process:\n";
- (*r_error) += shader.getInfoLog();
- (*r_error) += "\n";
- (*r_error) += shader.getInfoDebugLog();
- }
-
- return ret;
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ messages = (EShMessages)(messages | EShMsgDebugInfo);
}
- //set back..
- cs_strings = pre_processed_code.c_str();
- shader.setStrings(&cs_strings, 1);
+ const int DefaultVersion = 100;
//parse
if (!shader.parse(&DefaultTBuiltInResource, DefaultVersion, false, messages)) {
@@ -174,6 +161,13 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
std::vector<uint32_t> SpirV;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
+
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ spvOptions.generateDebugInfo = true;
+ spvOptions.emitNonSemanticShaderDebugInfo = true;
+ spvOptions.emitNonSemanticShaderDebugSource = true;
+ }
+
glslang::GlslangToSpv(*program.getIntermediate(stages[p_stage]), SpirV, &logger, &spvOptions);
ret.resize(SpirV.size() * sizeof(uint32_t));
@@ -188,7 +182,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) {
const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
String version;
- version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled());
return version;
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 572ef44876..9e7611bc5e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3400,10 +3400,11 @@ Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFText
const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image();
ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref<Texture2D>());
if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) {
+ ERR_FAIL_INDEX_V(image, p_state->source_images.size(), Ref<Texture2D>());
Ref<PortableCompressedTexture2D> portable_texture;
portable_texture.instantiate();
portable_texture->set_keep_compressed_buffer(true);
- Ref<Image> new_img = p_state->source_images[p_texture]->duplicate();
+ Ref<Image> new_img = p_state->source_images[image]->duplicate();
ERR_FAIL_COND_V(new_img.is_null(), Ref<Texture2D>());
new_img->generate_mipmaps();
if (p_texture_types) {
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index a300f7d2e2..d7ecf4ef1a 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -743,6 +743,17 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
}
+ // Consume input to avoid conflicts with other plugins.
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (int i = 0; i < options->get_popup()->get_item_count(); ++i) {
+ const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i);
+ if (shortcut.is_valid() && shortcut->matches_event(p_event)) {
+ _menu_option(options->get_popup()->get_item_id(i));
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+
if (k->is_shift_pressed() && selection.active && input_action != INPUT_PASTE) {
if (k->get_keycode() == (Key)options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
selection.click[edit_axis]--;
@@ -1154,6 +1165,24 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor() {
+ ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::Q, true);
+ ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::E, true);
+ ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), Key::Z, true);
+ ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), Key::X, true);
+ ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), Key::C, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_x", TTR("Cursor Back Rotate X"), KeyModifierMask::SHIFT + Key::A, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_y", TTR("Cursor Back Rotate Y"), KeyModifierMask::SHIFT + Key::S, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true);
+ ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true);
+ ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects"));
+ ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C, true);
+ ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X, true);
+ ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE);
+ ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F);
+
int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230);
Control *ec = memnew(Control);
ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE);
@@ -1186,29 +1215,29 @@ GridMapEditor::GridMapEditor() {
spatial_editor_hb->hide();
options->set_text(TTR("Grid Map"));
- options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, Key::Q);
- options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, Key::E);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/previous_floor"), MENU_OPTION_PREV_LEVEL);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/next_floor"), MENU_OPTION_NEXT_LEVEL);
options->get_popup()->add_separator();
- options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, Key::Z);
- options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, Key::X);
- options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, Key::C);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, Key::A);
- options->get_popup()->add_item(TTR("Cursor Rotate Y"), MENU_OPTION_CURSOR_ROTATE_Y, Key::S);
- options->get_popup()->add_item(TTR("Cursor Rotate Z"), MENU_OPTION_CURSOR_ROTATE_Z, Key::D);
- options->get_popup()->add_item(TTR("Cursor Back Rotate X"), MENU_OPTION_CURSOR_BACK_ROTATE_X, KeyModifierMask::SHIFT + Key::A);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y, KeyModifierMask::SHIFT + Key::S);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KeyModifierMask::SHIFT + Key::D);
- options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, Key::W);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_x"), MENU_OPTION_CURSOR_ROTATE_X);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_y"), MENU_OPTION_CURSOR_ROTATE_Y);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_z"), MENU_OPTION_CURSOR_ROTATE_Z);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_x"), MENU_OPTION_CURSOR_BACK_ROTATE_X);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
options->get_popup()->add_separator();
// TRANSLATORS: This is a toggle to select after pasting the new content.
- options->get_popup()->add_check_item(TTR("Paste Selects"), MENU_OPTION_PASTE_SELECTS);
+ options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/paste_selects"), MENU_OPTION_PASTE_SELECTS);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KeyModifierMask::CTRL + Key::C);
- options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KeyModifierMask::CTRL + Key::X);
- options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, Key::KEY_DELETE);
- options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KeyModifierMask::CTRL + Key::F);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/duplicate_selection"), MENU_OPTION_SELECTION_DUPLICATE);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cut_selection"), MENU_OPTION_SELECTION_CUT);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_selection"), MENU_OPTION_SELECTION_CLEAR);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/fill_selection"), MENU_OPTION_SELECTION_FILL);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index f592533a5a..2971706c75 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -72,7 +72,7 @@
// Types that will be skipped over (in favor of their base types) when setting up instance bindings.
// This must be a superset of `ignored_types` in bindings_generator.cpp.
-const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" };
+const Vector<String> ignored_types = {};
#ifdef TOOLS_ENABLED
static bool _create_project_solution_if_needed() {
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index d7d484d166..8a292fd73a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -23,7 +23,7 @@ namespace GodotTools.Build
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var startInfo = new ProcessStartInfo(dotnetPath);
@@ -94,7 +94,7 @@ namespace GodotTools.Build
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var startInfo = new ProcessStartInfo(dotnetPath);
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 618d255938..ec28658557 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -473,10 +473,9 @@ namespace GodotTools
}
}
- var editorInterface = GetEditorInterface();
- var editorBaseControl = editorInterface.GetBaseControl();
+ var editorBaseControl = EditorInterface.Singleton.GetBaseControl();
- _editorSettings = editorInterface.GetEditorSettings();
+ _editorSettings = EditorInterface.Singleton.GetEditorSettings();
_errorDialog = new AcceptDialog();
editorBaseControl.AddChild(_errorDialog);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 5d1a2277f9..65b77112aa 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -79,7 +79,7 @@ namespace GodotTools.Ides
public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
{
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var editorId = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>();
string editorIdentity = GetExternalEditorIdentity(editorId);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 0d77b8999a..fbbd01dafd 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -23,7 +23,7 @@ namespace GodotTools.Ides.Rider
private static string GetRiderPathFromSettings()
{
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
if (editorSettings.HasSetting(EditorPathSettingName))
return (string)editorSettings.GetSetting(EditorPathSettingName);
return null;
@@ -31,7 +31,7 @@ namespace GodotTools.Ides.Rider
public static void Initialize()
{
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
var editor = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>();
if (editor == ExternalEditorId.Rider)
{
@@ -92,7 +92,7 @@ namespace GodotTools.Ides.Rider
string newPath = riderInfos.Length > 0
? riderInfos[riderInfos.Length - 1].Path
: allInfos[allInfos.Length - 1].Path;
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
editorSettings.SetSetting(EditorPathSettingName, newPath);
Globals.EditorDef(EditorPathSettingName, newPath);
return newPath;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index bed93cd69e..006aca6c73 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -82,6 +82,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
#define CS_METHOD_CALL "Call"
#define CS_PROPERTY_SINGLETON "Singleton"
+#define CS_SINGLETON_INSTANCE_SUFFIX "Instance"
#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
#define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
#define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal"
@@ -116,7 +117,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
// Types that will be ignored by the generator and won't be available in C#.
// This must be kept in sync with `ignored_types` in csharp_script.cpp
-const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" };
+const Vector<String> ignored_types = {};
void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
// C interface for enums is the same as that of 'uint32_t'. Remember to apply
@@ -147,7 +148,7 @@ static String fix_doc_description(const String &p_bbcode) {
.strip_edges();
}
-String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) {
+String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal) {
// Based on the version in EditorHelp
if (p_bbcode.is_empty()) {
@@ -304,11 +305,11 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
_append_xml_enum(xml_output, target_itype, target_cname, link_target, link_target_parts);
} else if (link_tag == "constant") {
_append_xml_constant(xml_output, target_itype, target_cname, link_target, link_target_parts);
+ } else if (link_tag == "param") {
+ _append_xml_param(xml_output, link_target, p_is_signal);
} else if (link_tag == "theme_item") {
// We do not declare theme_items in any way in C#, so there is nothing to reference
_append_xml_undeclared(xml_output, link_target);
- } else if (link_tag == "param") {
- _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false));
}
pos = brk_end + 1;
@@ -653,6 +654,11 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const
_append_xml_undeclared(p_xml_output, p_link_target);
} else {
// Try to find the constant in the current class
+ if (p_target_itype->is_singleton_instance) {
+ // Constants and enums are declared in the static singleton class.
+ p_target_itype = &obj_types[p_target_itype->cname];
+ }
+
const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, p_target_itype->constants);
if (target_iconst) {
@@ -678,7 +684,7 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const
p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
p_xml_output.append(p_target_itype->proxy_name);
p_xml_output.append(".");
- p_xml_output.append(target_ienum->cname);
+ p_xml_output.append(target_ienum->proxy_name);
p_xml_output.append(".");
p_xml_output.append(target_iconst->proxy_name);
p_xml_output.append("\"/>");
@@ -719,7 +725,7 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm
if (target_iconst) {
p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
- p_xml_output.append(target_ienum->cname);
+ p_xml_output.append(target_ienum->proxy_name);
p_xml_output.append(".");
p_xml_output.append(target_iconst->proxy_name);
p_xml_output.append("\"/>");
@@ -730,6 +736,21 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm
}
}
+void BindingsGenerator::_append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal) {
+ const String link_target = snake_to_camel_case(p_link_target);
+
+ if (!p_is_signal) {
+ p_xml_output.append("<paramref name=\"");
+ p_xml_output.append(link_target);
+ p_xml_output.append("\"/>");
+ } else {
+ // Documentation in C# is added to an event, not the delegate itself;
+ // as such, we treat these parameters as codeblocks instead.
+ // See: https://github.com/godotengine/godot/pull/65529
+ _append_xml_undeclared(p_xml_output, link_target);
+ }
+}
+
void BindingsGenerator::_append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target) {
p_xml_output.append("<c>");
p_xml_output.append(p_link_target);
@@ -975,7 +996,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
for (const EnumInterface &ienum : global_enums) {
CRASH_COND(ienum.constants.is_empty());
- String enum_proxy_name = ienum.cname.operator String();
+ String enum_proxy_name = ienum.proxy_name;
bool enum_in_static_class = false;
@@ -989,7 +1010,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
_log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
p_output.append("\npublic partial struct ");
- p_output.append(pascal_to_pascal_case(enum_class_name));
+ p_output.append(enum_class_name);
p_output.append("\n" OPEN_BLOCK);
}
@@ -998,7 +1019,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
}
p_output.append("\npublic enum ");
- p_output.append(pascal_to_pascal_case(enum_proxy_name));
+ p_output.append(enum_proxy_name);
p_output.append(" : long");
p_output.append("\n" OPEN_BLOCK);
@@ -1403,8 +1424,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (is_derived_type && !itype.is_singleton) {
if (obj_types.has(itype.base_name)) {
+ TypeInterface base_type = obj_types[itype.base_name];
output.append(" : ");
- output.append(obj_types[itype.base_name].proxy_name);
+ output.append(base_type.proxy_name);
+ if (base_type.is_singleton) {
+ // If the type is a singleton, use the instance type.
+ output.append(CS_SINGLETON_INSTANCE_SUFFIX);
+ }
} else {
ERR_PRINT("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'.");
return ERR_INVALID_DATA;
@@ -1458,7 +1484,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.append(MEMBER_BEGIN "public enum ");
- output.append(pascal_to_pascal_case(ienum.cname.operator String()));
+ output.append(ienum.proxy_name);
output.append(" : long");
output.append(MEMBER_BEGIN OPEN_BLOCK);
@@ -1504,37 +1530,44 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"' for class '" + itype.name + "'.");
}
- if (itype.is_singleton) {
- // Add the type name and the singleton pointer as static fields
+ // Add native name static field and cached type.
+
+ if (is_derived_type && !itype.is_singleton) {
+ output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n";
+ }
- output.append(MEMBER_BEGIN "private static GodotObject singleton;\n");
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(itype.name);
+ output.append("\";\n");
- output << MEMBER_BEGIN "public static GodotObject " CS_PROPERTY_SINGLETON "\n" INDENT1 "{\n"
- << INDENT2 "get\n" INDENT2 "{\n" INDENT3 "if (singleton == null)\n"
- << INDENT4 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(\""
- << itype.name
- << "\");\n" INDENT3 "return singleton;\n" INDENT2 "}\n" INDENT1 "}\n";
+ if (itype.is_singleton || itype.is_compat_singleton) {
+ // Add the Singleton static property.
- output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
- output.append(itype.name);
- output.append("\";\n");
- } else {
+ String instance_type_name;
+
+ if (itype.is_singleton) {
+ StringName instance_name = itype.name + CS_SINGLETON_INSTANCE_SUFFIX;
+ instance_type_name = obj_types.has(instance_name)
+ ? obj_types[instance_name].proxy_name
+ : "GodotObject";
+ } else {
+ instance_type_name = itype.proxy_name;
+ }
+
+ output.append(MEMBER_BEGIN "private static " + instance_type_name + " singleton;\n");
+
+ output << MEMBER_BEGIN "public static " + instance_type_name + " " CS_PROPERTY_SINGLETON " =>\n"
+ << INDENT2 "singleton \?\?= (" + instance_type_name + ")"
+ << C_METHOD_ENGINE_GET_SINGLETON "(\"" << itype.name << "\");\n";
+ }
+
+ if (!itype.is_singleton) {
// IMPORTANT: We also generate the static fields for GodotObject instead of declaring
// them manually in the `GodotObject.base.cs` partial class declaration, because they're
// required by other static fields in this generated partial class declaration.
// Static fields are initialized in order of declaration, but when they're in different
// partial class declarations then it becomes harder to tell (Rider warns about this).
- // Add native name static field
-
- if (is_derived_type) {
- output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n";
- }
-
- output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
- output.append(itype.name);
- output.append("\";\n");
-
if (itype.is_instantiable) {
// Add native constructor static field
@@ -1749,7 +1782,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< INDENT1 "/// This method is used by Godot to check if a signal exists before raising it.\n"
<< INDENT1 "/// Do not call or override this method.\n"
<< INDENT1 "/// </summary>\n"
- << INDENT1 "/// <param name=\"method\">Name of the method to check for.</param>\n";
+ << INDENT1 "/// <param name=\"signal\">Name of the signal to check for.</param>\n";
output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n"
@@ -1894,7 +1927,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
- const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
+ const TypeInterface *prop_itype = _get_type_or_singleton_or_null(proptype_name);
ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
ERR_FAIL_COND_V_MSG(prop_itype->is_singleton, ERR_BUG,
@@ -1983,7 +2016,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
- const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_singleton_or_null(p_imethod.return_type);
ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
@@ -2004,12 +2037,17 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String icall_params = method_bind_field;
if (!p_imethod.is_static) {
+ String self_reference = "this";
+ if (p_itype.is_singleton) {
+ self_reference = CS_PROPERTY_SINGLETON;
+ }
+
if (p_itype.cs_in.size()) {
- cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, "this",
+ cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, self_reference,
String(), String(), String(), INDENT2);
}
- icall_params += ", " + sformat(p_itype.cs_in_expr, "this");
+ icall_params += ", " + sformat(p_itype.cs_in_expr, self_reference);
}
StringBuilder default_args_doc;
@@ -2017,7 +2055,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
const ArgumentInterface &first = p_imethod.arguments.front()->get();
for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type);
ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
@@ -2275,7 +2313,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
const ArgumentInterface &first = p_isignal.arguments.front()->get();
for (const ArgumentInterface &iarg : p_isignal.arguments) {
- const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type);
ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
@@ -2307,31 +2345,31 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate signal
{
- p_output.append(MEMBER_BEGIN "/// <summary>\n");
- p_output.append(INDENT1 "/// ");
- p_output.append("Represents the method that handles the ");
- p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>");
- p_output.append(" event of a ");
- p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>");
- p_output.append(" class.\n");
- p_output.append(INDENT1 "/// </summary>");
-
- if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.is_empty()) {
- WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
- }
-
- p_output.append(MEMBER_BEGIN "[Obsolete(\"");
- p_output.append(p_isignal.deprecation_message);
- p_output.append("\")]");
- }
-
bool is_parameterless = p_isignal.arguments.size() == 0;
// Delegate name is [SignalName]EventHandler
String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler";
if (!is_parameterless) {
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
+ p_output.append(INDENT1 "/// ");
+ p_output.append("Represents the method that handles the ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>");
+ p_output.append(" event of a ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>");
+ p_output.append(" class.\n");
+ p_output.append(INDENT1 "/// </summary>");
+
+ if (p_isignal.is_deprecated) {
+ if (p_isignal.deprecation_message.is_empty()) {
+ WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+ }
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_isignal.deprecation_message);
+ p_output.append("\")]");
+ }
+
// Generate delegate
p_output.append(MEMBER_BEGIN "public delegate void ");
p_output.append(delegate_name);
@@ -2367,7 +2405,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
- String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype, true);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
@@ -2702,6 +2740,20 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
return nullptr;
}
+const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_singleton_or_null(const TypeReference &p_typeref) {
+ const TypeInterface *itype = _get_type_or_null(p_typeref);
+ if (itype == nullptr) {
+ return nullptr;
+ }
+
+ if (itype->is_singleton) {
+ StringName instance_type_name = itype->name + CS_SINGLETON_INSTANCE_SUFFIX;
+ itype = &obj_types.find(instance_type_name)->value;
+ }
+
+ return itype;
+}
+
const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
if (p_generic_type_parameters.is_empty()) {
return "";
@@ -2715,8 +2767,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
int i = 0;
String params = "<";
for (const TypeReference &param_type : p_generic_type_parameters) {
- const TypeInterface *param_itype = _get_type_or_null(param_type);
- ERR_FAIL_NULL_V(param_itype, "");
+ const TypeInterface *param_itype = _get_type_or_singleton_or_null(param_type);
+ ERR_FAIL_NULL_V(param_itype, ""); // Parameter type not found
ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
"Generic type parameter is a singleton: '" + param_itype->name + "'.");
@@ -2932,17 +2984,18 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
itype.memory_own = itype.is_ref_counted;
+ if (itype.is_singleton && compat_singletons.has(itype.cname)) {
+ itype.is_singleton = false;
+ itype.is_compat_singleton = true;
+ }
+
itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
itype.cs_type = itype.proxy_name;
- if (itype.is_singleton) {
- itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")";
- } else {
- itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)";
- }
+ itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)";
itype.cs_out = "%5return (%2)%0(%1);";
@@ -3278,8 +3331,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_proxy_name += "Enum";
enum_proxy_cname = StringName(enum_proxy_name);
}
- EnumInterface ienum(enum_proxy_cname);
- ienum.is_flags = E.value.is_bitfield;
+ EnumInterface ienum(enum_proxy_cname, enum_proxy_name, E.value.is_bitfield);
const List<StringName> &enum_constants = E.value.constants;
for (const StringName &constant_cname : enum_constants) {
String constant_name = constant_cname.operator String();
@@ -3346,6 +3398,19 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
obj_types.insert(itype.cname, itype);
+ if (itype.is_singleton) {
+ // Add singleton instance type.
+ itype.proxy_name += CS_SINGLETON_INSTANCE_SUFFIX;
+ itype.is_singleton = false;
+ itype.is_singleton_instance = true;
+
+ // Remove constants and enums, those will remain in the static class.
+ itype.constants.clear();
+ itype.enums.clear();
+
+ obj_types.insert(itype.name + CS_SINGLETON_INSTANCE_SUFFIX, itype);
+ }
+
class_list.pop_front();
}
@@ -3821,7 +3886,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
builtin_types.insert(itype.cname, itype);
// Array_@generic
- // Re-use Array's itype
+ // Reuse Array's itype
itype.name = "Array_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
@@ -3848,7 +3913,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
builtin_types.insert(itype.cname, itype);
// Dictionary_@generic
- // Re-use Dictionary's itype
+ // Reuse Dictionary's itype
itype.name = "Dictionary_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
@@ -3899,8 +3964,7 @@ void BindingsGenerator::_populate_global_constants() {
iconstant.const_doc = const_doc;
if (enum_name != StringName()) {
- EnumInterface ienum(enum_name);
- ienum.is_flags = CoreConstants::is_global_constant_bitfield(i);
+ EnumInterface ienum(enum_name, pascal_to_pascal_case(enum_name.operator String()), CoreConstants::is_global_constant_bitfield(i));
List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
if (enum_match) {
enum_match->get().constants.push_back(iconstant);
@@ -3918,7 +3982,7 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.is_enum = true;
enum_itype.name = ienum.cname.operator String();
enum_itype.cname = ienum.cname;
- enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name);
+ enum_itype.proxy_name = ienum.proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
@@ -3962,6 +4026,10 @@ void BindingsGenerator::_initialize_blacklisted_methods() {
blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
}
+void BindingsGenerator::_initialize_compat_singletons() {
+ compat_singletons.insert("EditorInterface");
+}
+
void BindingsGenerator::_log(const char *p_format, ...) {
if (log_print_enabled) {
va_list list;
@@ -3981,6 +4049,8 @@ void BindingsGenerator::_initialize() {
_initialize_blacklisted_methods();
+ _initialize_compat_singletons();
+
bool obj_type_ok = _populate_object_type_interfaces();
ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces");
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 38347a5181..6118576bb6 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -60,6 +60,7 @@ class BindingsGenerator {
struct EnumInterface {
StringName cname;
+ String proxy_name;
List<ConstantInterface> constants;
bool is_flags = false;
@@ -69,8 +70,10 @@ class BindingsGenerator {
EnumInterface() {}
- EnumInterface(const StringName &p_cname) {
+ EnumInterface(const StringName &p_cname, const String &p_proxy_name, bool p_is_flags) {
cname = p_cname;
+ proxy_name = p_proxy_name;
+ is_flags = p_is_flags;
}
};
@@ -227,9 +230,18 @@ class BindingsGenerator {
bool is_enum = false;
bool is_object_type = false;
bool is_singleton = false;
+ bool is_singleton_instance = false;
bool is_ref_counted = false;
/**
+ * Class is a singleton, but can't be declared as a static class as that would
+ * break backwards compatibility. As such, instead of going with a static class,
+ * we use the actual singleton pattern (private constructor with instance property),
+ * which doesn't break compatibility.
+ */
+ bool is_compat_singleton = false;
+
+ /**
* Determines whether the native return value of this type must be disposed
* by the generated internal call (think of `godot_string`, whose destructor
* must be called). Some structs that are disposable may still disable this
@@ -614,8 +626,10 @@ class BindingsGenerator {
HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
HashMap<StringName, List<StringName>> blacklisted_methods;
+ HashSet<StringName> compat_singletons;
void _initialize_blacklisted_methods();
+ void _initialize_compat_singletons();
struct NameCache {
StringName type_void = StaticCString::create("void");
@@ -740,7 +754,7 @@ class BindingsGenerator {
return p_type->name;
}
- String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
+ String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal = false);
void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
void _append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
@@ -748,6 +762,7 @@ class BindingsGenerator {
void _append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
void _append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
void _append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target);
+ void _append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal);
void _append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target);
int _determine_enum_prefix(const EnumInterface &p_ienum);
@@ -756,6 +771,7 @@ class BindingsGenerator {
Error _populate_method_icalls_table(const TypeInterface &p_itype);
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
+ const TypeInterface *_get_type_or_singleton_or_null(const TypeReference &p_typeref);
const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index dfae85b667..0fe4bcdfce 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -276,8 +276,13 @@ namespace Godot.Bridge
if (wrapperType != null && IsStatic(wrapperType))
{
- // A static class means this is a Godot singleton class. If an instance is needed we use GodotObject.
- return typeof(GodotObject);
+ // A static class means this is a Godot singleton class. Try to get the Instance proxy type.
+ wrapperType = TypeGetProxyClass($"{nativeTypeNameStr}Instance");
+ if (wrapperType == null)
+ {
+ // Otherwise, fallback to GodotObject.
+ return typeof(GodotObject);
+ }
}
return wrapperType;
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 00ef4ccdde..c84ecf4ceb 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -33,10 +33,6 @@
#include "mono_gd/gd_mono.h"
#include "utils/path_utils.h"
-#ifdef ANDROID_ENABLED
-#include "mono_gd/support/android_support.h"
-#endif
-
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/os/os.h"
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index 634d70d3bd..535091e5b6 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -32,12 +32,11 @@
#ifdef TOOLS_ENABLED
-#include "../navigation_mesh_generator.h"
-
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "editor/editor_node.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/navigation_region_3d.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
@@ -99,18 +98,16 @@ void NavigationMeshEditor::_bake_pressed() {
}
}
- NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
- Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
- source_geometry_data.instantiate();
- NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(node->get_navigation_mesh(), source_geometry_data, node);
- NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(node->get_navigation_mesh(), source_geometry_data);
+ node->bake_navigation_mesh(false);
node->update_gizmos();
}
void NavigationMeshEditor::_clear_pressed() {
if (node) {
- NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
+ if (node->get_navigation_mesh().is_valid()) {
+ node->get_navigation_mesh()->clear();
+ }
}
button_bake->set_pressed(false);
@@ -139,14 +136,15 @@ NavigationMeshEditor::NavigationMeshEditor() {
button_bake->set_flat(true);
bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
- button_bake->set_text(TTR("Bake NavMesh"));
+ button_bake->set_text(TTR("Bake NavigationMesh"));
+ button_bake->set_tooltip_text(TTR("Bakes the NavigationMesh by first parsing the scene for source geometry and then creating the navigation mesh vertices and polygons."));
button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed));
button_reset = memnew(Button);
button_reset->set_flat(true);
bake_hbox->add_child(button_reset);
- // No button text, we only use a revert icon which is set when entering the tree.
- button_reset->set_tooltip_text(TTR("Clear the navigation mesh."));
+ button_reset->set_text(TTR("Clear NavigationMesh"));
+ button_reset->set_tooltip_text(TTR("Clears the internal NavigationMesh vertices and polygons."));
button_reset->connect("pressed", callable_mp(this, &NavigationMeshEditor::_clear_pressed));
bake_info = memnew(Label);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index c0fa6eef9e..9162fcf171 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -31,8 +31,8 @@
#include "godot_navigation_server.h"
#ifndef _3D_DISABLED
-#include "navigation_mesh_generator.h"
-#endif
+#include "nav_mesh_generator_3d.h"
+#endif // _3D_DISABLED
#include "core/os/mutex.h"
@@ -468,11 +468,11 @@ void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_na
WARN_PRINT_ONCE("NavigationServer3D::region_bake_navigation_mesh() 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..");
#ifndef _3D_DISABLED
- NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh);
+ p_navigation_mesh->clear();
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
source_geometry_data.instantiate();
- NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(p_navigation_mesh, source_geometry_data, p_root_node);
- NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data);
+ parse_source_geometry_data(p_navigation_mesh, source_geometry_data, p_root_node);
+ bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data);
#endif
}
#endif // DISABLE_DEPRECATED
@@ -930,16 +930,36 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
obstacle->set_avoidance_layers(p_layers);
}
-void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
#ifndef _3D_DISABLED
- NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback);
-#endif
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
+ ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
+ ERR_FAIL_COND_MSG(p_root_node == nullptr, "No parsing root node specified.");
+ ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree.");
+
+ ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
+ NavMeshGenerator3D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback);
+#endif // _3D_DISABLED
}
-void GodotNavigationServer::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
#ifndef _3D_DISABLED
- NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
-#endif
+ ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
+ ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
+
+ ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
+ NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
+#endif // _3D_DISABLED
+}
+
+void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+#ifndef _3D_DISABLED
+ ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
+ ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
+
+ ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
+ NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
+#endif // _3D_DISABLED
}
COMMAND_1(free, RID, p_object) {
@@ -1073,6 +1093,16 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return;
}
+#ifndef _3D_DISABLED
+ // Sync finished navmesh bakes before doing NavMap updates.
+ if (navmesh_generator_3d) {
+ navmesh_generator_3d->sync();
+ // Finished bakes emit callbacks and users might have reacted to those.
+ // Flush queue again so users do not have to wait for the next sync.
+ flush_queries();
+ }
+#endif // _3D_DISABLED
+
int _new_pm_region_count = 0;
int _new_pm_agent_count = 0;
int _new_pm_link_count = 0;
@@ -1117,6 +1147,23 @@ void GodotNavigationServer::process(real_t p_delta_time) {
pm_edge_free_count = _new_pm_edge_free_count;
}
+void GodotNavigationServer::init() {
+#ifndef _3D_DISABLED
+ navmesh_generator_3d = memnew(NavMeshGenerator3D);
+#endif // _3D_DISABLED
+}
+
+void GodotNavigationServer::finish() {
+ flush_queries();
+#ifndef _3D_DISABLED
+ if (navmesh_generator_3d) {
+ navmesh_generator_3d->finish();
+ memdelete(navmesh_generator_3d);
+ navmesh_generator_3d = nullptr;
+ }
+#endif // _3D_DISABLED
+}
+
PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const {
PathQueryResult r_query_result;
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 0b3789102c..40893bada6 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -56,6 +56,7 @@
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
class GodotNavigationServer;
+class NavMeshGenerator3D;
struct SetCommand {
virtual ~SetCommand() {}
@@ -79,6 +80,8 @@ class GodotNavigationServer : public NavigationServer3D {
LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
+ NavMeshGenerator3D *navmesh_generator_3d = nullptr;
+
// Performance Monitor
int pm_region_count = 0;
int pm_agent_count = 0;
@@ -225,8 +228,9 @@ public:
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
- virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
- virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+ virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
+ virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+ virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
COMMAND_1(free, RID, p_object);
@@ -234,6 +238,8 @@ public:
void flush_queries();
virtual void process(real_t p_delta_time) override;
+ virtual void init() override;
+ virtual void finish() override;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;
diff --git a/modules/navigation/nav_mesh_generator_3d.cpp b/modules/navigation/nav_mesh_generator_3d.cpp
new file mode 100644
index 0000000000..7c27417e5f
--- /dev/null
+++ b/modules/navigation/nav_mesh_generator_3d.cpp
@@ -0,0 +1,838 @@
+/**************************************************************************/
+/* nav_mesh_generator_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. */
+/**************************************************************************/
+
+#ifndef _3D_DISABLED
+
+#include "nav_mesh_generator_3d.h"
+
+#include "core/config/project_settings.h"
+#include "core/math/convex_hull.h"
+#include "core/os/thread.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/multimesh_instance_3d.h"
+#include "scene/3d/physics_body_3d.h"
+#include "scene/resources/box_shape_3d.h"
+#include "scene/resources/capsule_shape_3d.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/convex_polygon_shape_3d.h"
+#include "scene/resources/cylinder_shape_3d.h"
+#include "scene/resources/height_map_shape_3d.h"
+#include "scene/resources/navigation_mesh.h"
+#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/primitive_meshes.h"
+#include "scene/resources/shape_3d.h"
+#include "scene/resources/sphere_shape_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
+
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
+#ifdef MODULE_CSG_ENABLED
+#include "modules/csg/csg_shape.h"
+#endif
+#ifdef MODULE_GRIDMAP_ENABLED
+#include "modules/gridmap/grid_map.h"
+#endif
+
+#include <Recast.h>
+
+NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
+Mutex NavMeshGenerator3D::baking_navmesh_mutex;
+Mutex NavMeshGenerator3D::generator_task_mutex;
+bool NavMeshGenerator3D::use_threads = true;
+bool NavMeshGenerator3D::baking_use_multiple_threads = true;
+bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
+HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
+HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
+
+NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
+ return singleton;
+}
+
+NavMeshGenerator3D::NavMeshGenerator3D() {
+ ERR_FAIL_COND(singleton != nullptr);
+ singleton = this;
+
+ baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
+ baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
+
+ // Using threads might cause problems on certain exports or with the Editor on certain devices.
+ // This is the main switch to turn threaded navmesh baking off should the need arise.
+ use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
+}
+
+NavMeshGenerator3D::~NavMeshGenerator3D() {
+ cleanup();
+}
+
+void NavMeshGenerator3D::sync() {
+ if (generator_tasks.size() == 0) {
+ return;
+ }
+
+ baking_navmesh_mutex.lock();
+ generator_task_mutex.lock();
+
+ LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
+
+ for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+ if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+ finished_task_ids.push_back(E.key);
+
+ NavMeshGeneratorTask3D *generator_task = E.value;
+ DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
+
+ baking_navmeshes.erase(generator_task->navigation_mesh);
+ if (generator_task->callback.is_valid()) {
+ generator_emit_callback(generator_task->callback);
+ }
+ memdelete(generator_task);
+ }
+ }
+
+ for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
+ generator_tasks.erase(finished_task_id);
+ }
+
+ generator_task_mutex.unlock();
+ baking_navmesh_mutex.unlock();
+}
+
+void NavMeshGenerator3D::cleanup() {
+ baking_navmesh_mutex.lock();
+ generator_task_mutex.lock();
+
+ baking_navmeshes.clear();
+
+ for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+ NavMeshGeneratorTask3D *generator_task = E.value;
+ memdelete(generator_task);
+ }
+ generator_tasks.clear();
+
+ generator_task_mutex.unlock();
+ baking_navmesh_mutex.unlock();
+}
+
+void NavMeshGenerator3D::finish() {
+ cleanup();
+}
+
+void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+ ERR_FAIL_COND(!Thread::is_main_thread());
+ ERR_FAIL_COND(!p_navigation_mesh.is_valid());
+ ERR_FAIL_COND(p_root_node == nullptr);
+ ERR_FAIL_COND(!p_root_node->is_inside_tree());
+ ERR_FAIL_COND(!p_source_geometry_data.is_valid());
+
+ generator_parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node);
+
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+}
+
+void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
+ ERR_FAIL_COND(!p_navigation_mesh.is_valid());
+ ERR_FAIL_COND(!p_source_geometry_data.is_valid());
+
+ if (!p_source_geometry_data->has_data()) {
+ p_navigation_mesh->clear();
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+ return;
+ }
+
+ baking_navmesh_mutex.lock();
+ if (baking_navmeshes.has(p_navigation_mesh)) {
+ baking_navmesh_mutex.unlock();
+ ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
+ }
+ baking_navmeshes.insert(p_navigation_mesh);
+ baking_navmesh_mutex.unlock();
+
+ generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
+
+ baking_navmesh_mutex.lock();
+ baking_navmeshes.erase(p_navigation_mesh);
+ baking_navmesh_mutex.unlock();
+
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+}
+
+void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
+ ERR_FAIL_COND(!p_navigation_mesh.is_valid());
+ ERR_FAIL_COND(!p_source_geometry_data.is_valid());
+
+ if (!p_source_geometry_data->has_data()) {
+ p_navigation_mesh->clear();
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+ return;
+ }
+
+ if (!use_threads) {
+ bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
+ return;
+ }
+
+ baking_navmesh_mutex.lock();
+ if (baking_navmeshes.has(p_navigation_mesh)) {
+ baking_navmesh_mutex.unlock();
+ ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
+ return;
+ }
+ baking_navmeshes.insert(p_navigation_mesh);
+ baking_navmesh_mutex.unlock();
+
+ generator_task_mutex.lock();
+ NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
+ generator_task->navigation_mesh = p_navigation_mesh;
+ generator_task->source_geometry_data = p_source_geometry_data;
+ generator_task->callback = p_callback;
+ generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+ generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
+ generator_tasks.insert(generator_task->thread_task_id, generator_task);
+ generator_task_mutex.unlock();
+}
+
+void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
+ NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
+
+ generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
+
+ generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
+}
+
+void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
+ generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_parse_staticbody3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
+#ifdef MODULE_CSG_ENABLED
+ generator_parse_csgshape3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
+#endif
+#ifdef MODULE_GRIDMAP_ENABLED
+ generator_parse_gridmap_node(p_navigation_mesh, p_source_geometry_data, p_node);
+#endif
+
+ if (p_recurse_children) {
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
+ }
+ }
+}
+
+void NavMeshGenerator3D::generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
+ MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
+
+ if (mesh_instance) {
+ NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+
+ if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
+ Ref<Mesh> mesh = mesh_instance->get_mesh();
+ if (mesh.is_valid()) {
+ p_source_geometry_data->add_mesh(mesh, mesh_instance->get_global_transform());
+ }
+ }
+ }
+}
+
+void NavMeshGenerator3D::generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
+ MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node);
+
+ if (multimesh_instance) {
+ NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+
+ if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
+ Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
+ if (multimesh.is_valid()) {
+ Ref<Mesh> mesh = multimesh->get_mesh();
+ if (mesh.is_valid()) {
+ int n = multimesh->get_visible_instance_count();
+ if (n == -1) {
+ n = multimesh->get_instance_count();
+ }
+ for (int i = 0; i < n; i++) {
+ p_source_geometry_data->add_mesh(mesh, multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i));
+ }
+ }
+ }
+ }
+ }
+}
+
+void NavMeshGenerator3D::generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
+ StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node);
+
+ if (static_body) {
+ NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+ uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
+
+ if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (static_body->get_collision_layer() & parsed_collision_mask)) {
+ List<uint32_t> shape_owners;
+ static_body->get_shape_owners(&shape_owners);
+ for (uint32_t shape_owner : shape_owners) {
+ if (static_body->is_shape_owner_disabled(shape_owner)) {
+ continue;
+ }
+ const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
+ for (int shape_index = 0; shape_index < shape_count; shape_index++) {
+ Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, shape_index);
+ if (s.is_null()) {
+ continue;
+ }
+
+ const Transform3D transform = static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
+
+ BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
+ if (box) {
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ BoxMesh::create_mesh_array(arr, box->get_size());
+ p_source_geometry_data->add_mesh_array(arr, transform);
+ }
+
+ CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
+ if (capsule) {
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height());
+ p_source_geometry_data->add_mesh_array(arr, transform);
+ }
+
+ CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s);
+ if (cylinder) {
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height());
+ p_source_geometry_data->add_mesh_array(arr, transform);
+ }
+
+ SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s);
+ if (sphere) {
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0);
+ p_source_geometry_data->add_mesh_array(arr, transform);
+ }
+
+ ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s);
+ if (concave_polygon) {
+ p_source_geometry_data->add_faces(concave_polygon->get_faces(), transform);
+ }
+
+ ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
+ if (convex_polygon) {
+ Vector<Vector3> varr = Variant(convex_polygon->get_points());
+ Geometry3D::MeshData md;
+
+ Error err = ConvexHullComputer::convex_hull(varr, md);
+
+ if (err == OK) {
+ PackedVector3Array faces;
+
+ for (const Geometry3D::MeshData::Face &face : md.faces) {
+ for (uint32_t k = 2; k < face.indices.size(); ++k) {
+ faces.push_back(md.vertices[face.indices[0]]);
+ faces.push_back(md.vertices[face.indices[k - 1]]);
+ faces.push_back(md.vertices[face.indices[k]]);
+ }
+ }
+
+ p_source_geometry_data->add_faces(faces, transform);
+ }
+ }
+
+ HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s);
+ if (heightmap_shape) {
+ int heightmap_depth = heightmap_shape->get_map_depth();
+ int heightmap_width = heightmap_shape->get_map_width();
+
+ if (heightmap_depth >= 2 && heightmap_width >= 2) {
+ const Vector<real_t> &map_data = heightmap_shape->get_map_data();
+
+ Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
+ Vector2 start = heightmap_gridsize * -0.5;
+
+ Vector<Vector3> vertex_array;
+ vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
+ int map_data_current_index = 0;
+
+ for (int d = 0; d < heightmap_depth; d++) {
+ for (int w = 0; w < heightmap_width; w++) {
+ if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
+ float top_left_height = map_data[map_data_current_index];
+ float top_right_height = map_data[map_data_current_index + 1];
+ float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
+ float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
+
+ Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
+ Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
+ Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
+ Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
+
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_left);
+ vertex_array.push_back(top_left);
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_right);
+ vertex_array.push_back(bottom_left);
+ }
+ map_data_current_index += 1;
+ }
+ }
+ if (vertex_array.size() > 0) {
+ p_source_geometry_data->add_faces(vertex_array, transform);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#ifdef MODULE_CSG_ENABLED
+void NavMeshGenerator3D::generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
+ CSGShape3D *csgshape3d = Object::cast_to<CSGShape3D>(p_node);
+
+ if (csgshape3d) {
+ NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+ uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
+
+ if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS && csgshape3d->is_using_collision() && (csgshape3d->get_collision_layer() & parsed_collision_mask)) || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
+ CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node);
+ Array meshes = csg_shape->get_meshes();
+ if (!meshes.is_empty()) {
+ Ref<Mesh> mesh = meshes[1];
+ if (mesh.is_valid()) {
+ p_source_geometry_data->add_mesh(mesh, csg_shape->get_global_transform());
+ }
+ }
+ }
+ }
+}
+#endif // MODULE_CSG_ENABLED
+
+#ifdef MODULE_GRIDMAP_ENABLED
+void NavMeshGenerator3D::generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
+ GridMap *gridmap = Object::cast_to<GridMap>(p_node);
+
+ if (gridmap) {
+ NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+ uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
+
+ if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) {
+ Array meshes = gridmap->get_meshes();
+ Transform3D xform = gridmap->get_global_transform();
+ for (int i = 0; i < meshes.size(); i += 2) {
+ Ref<Mesh> mesh = meshes[i + 1];
+ if (mesh.is_valid()) {
+ p_source_geometry_data->add_mesh(mesh, xform * (Transform3D)meshes[i]);
+ }
+ }
+ }
+
+ else if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (gridmap->get_collision_layer() & parsed_collision_mask)) {
+ Array shapes = gridmap->get_collision_shapes();
+ for (int i = 0; i < shapes.size(); i += 2) {
+ RID shape = shapes[i + 1];
+ PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
+ Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
+
+ switch (type) {
+ case PhysicsServer3D::SHAPE_SPHERE: {
+ real_t radius = data;
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ SphereMesh::create_mesh_array(arr, radius, radius * 2.0);
+ p_source_geometry_data->add_mesh_array(arr, shapes[i]);
+ } break;
+ case PhysicsServer3D::SHAPE_BOX: {
+ Vector3 extents = data;
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ BoxMesh::create_mesh_array(arr, extents * 2.0);
+ p_source_geometry_data->add_mesh_array(arr, shapes[i]);
+ } break;
+ case PhysicsServer3D::SHAPE_CAPSULE: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ CapsuleMesh::create_mesh_array(arr, radius, height);
+ p_source_geometry_data->add_mesh_array(arr, shapes[i]);
+ } break;
+ case PhysicsServer3D::SHAPE_CYLINDER: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ CylinderMesh::create_mesh_array(arr, radius, radius, height);
+ p_source_geometry_data->add_mesh_array(arr, shapes[i]);
+ } break;
+ case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
+ PackedVector3Array vertices = data;
+ Geometry3D::MeshData md;
+
+ Error err = ConvexHullComputer::convex_hull(vertices, md);
+
+ if (err == OK) {
+ PackedVector3Array faces;
+
+ for (const Geometry3D::MeshData::Face &face : md.faces) {
+ for (uint32_t k = 2; k < face.indices.size(); ++k) {
+ faces.push_back(md.vertices[face.indices[0]]);
+ faces.push_back(md.vertices[face.indices[k - 1]]);
+ faces.push_back(md.vertices[face.indices[k]]);
+ }
+ }
+
+ p_source_geometry_data->add_faces(faces, shapes[i]);
+ }
+ } break;
+ case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
+ Dictionary dict = data;
+ PackedVector3Array faces = Variant(dict["faces"]);
+ p_source_geometry_data->add_faces(faces, shapes[i]);
+ } break;
+ case PhysicsServer3D::SHAPE_HEIGHTMAP: {
+ Dictionary dict = data;
+ ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
+ int heightmap_depth = dict["depth"];
+ int heightmap_width = dict["width"];
+
+ if (heightmap_depth >= 2 && heightmap_width >= 2) {
+ const Vector<real_t> &map_data = dict["heights"];
+
+ Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
+ Vector2 start = heightmap_gridsize * -0.5;
+
+ Vector<Vector3> vertex_array;
+ vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
+ int map_data_current_index = 0;
+
+ for (int d = 0; d < heightmap_depth; d++) {
+ for (int w = 0; w < heightmap_width; w++) {
+ if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
+ float top_left_height = map_data[map_data_current_index];
+ float top_right_height = map_data[map_data_current_index + 1];
+ float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
+ float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
+
+ Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
+ Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
+ Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
+ Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
+
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_left);
+ vertex_array.push_back(top_left);
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_right);
+ vertex_array.push_back(bottom_left);
+ }
+ map_data_current_index += 1;
+ }
+ }
+ if (vertex_array.size() > 0) {
+ p_source_geometry_data->add_faces(vertex_array, shapes[i]);
+ }
+ }
+ } break;
+ default: {
+ WARN_PRINT("Unsupported collision shape type.");
+ } break;
+ }
+ }
+ }
+ }
+}
+#endif // MODULE_GRIDMAP_ENABLED
+
+void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) {
+ List<Node *> parse_nodes;
+
+ if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
+ parse_nodes.push_back(p_root_node);
+ } else {
+ p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes);
+ }
+
+ Transform3D root_node_transform = Transform3D();
+ if (Object::cast_to<Node3D>(p_root_node)) {
+ root_node_transform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse();
+ }
+
+ p_source_geometry_data->clear();
+ p_source_geometry_data->root_node_transform = root_node_transform;
+
+ bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
+
+ for (Node *parse_node : parse_nodes) {
+ generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, parse_node, recurse_children);
+ }
+};
+
+void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data) {
+ if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
+ return;
+ }
+
+ const Vector<float> &vertices = p_source_geometry_data->get_vertices();
+ const Vector<int> &indices = p_source_geometry_data->get_indices();
+
+ if (vertices.size() < 3 || indices.size() < 3) {
+ return;
+ }
+
+ rcHeightfield *hf = nullptr;
+ rcCompactHeightfield *chf = nullptr;
+ rcContourSet *cset = nullptr;
+ rcPolyMesh *poly_mesh = nullptr;
+ rcPolyMeshDetail *detail_mesh = nullptr;
+ rcContext ctx;
+
+ // added to keep track of steps, no functionality right now
+ String bake_state = "";
+
+ bake_state = "Setting up Configuration..."; // step #1
+
+ const float *verts = vertices.ptr();
+ const int nverts = vertices.size() / 3;
+ const int *tris = indices.ptr();
+ const int ntris = indices.size() / 3;
+
+ float bmin[3], bmax[3];
+ rcCalcBounds(verts, nverts, bmin, bmax);
+
+ rcConfig cfg;
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.cs = p_navigation_mesh->get_cell_size();
+ cfg.ch = p_navigation_mesh->get_cell_height();
+ cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope();
+ cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch);
+ cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch);
+ cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs);
+ cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size());
+ cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error();
+ cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size());
+ cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size());
+ cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon();
+ cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
+ cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
+
+ if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
+ WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) {
+ WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) {
+ WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) {
+ WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) {
+ WARN_PRINT("Property region_min_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) {
+ WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) {
+ WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision.");
+ }
+ if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) {
+ WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
+ }
+
+ cfg.bmin[0] = bmin[0];
+ cfg.bmin[1] = bmin[1];
+ cfg.bmin[2] = bmin[2];
+ cfg.bmax[0] = bmax[0];
+ cfg.bmax[1] = bmax[1];
+ cfg.bmax[2] = bmax[2];
+
+ AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb();
+ if (baking_aabb.has_volume()) {
+ Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset();
+ cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x;
+ cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y;
+ cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z;
+ cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0];
+ cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1];
+ cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
+ }
+
+ bake_state = "Calculating grid size..."; // step #2
+ rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
+
+ // ~30000000 seems to be around sweetspot where Editor baking breaks
+ if ((cfg.width * cfg.height) > 30000000) {
+ WARN_PRINT("NavigationMesh baking process will likely fail."
+ "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings."
+ "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues."
+ "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry.");
+ }
+
+ bake_state = "Creating heightfield..."; // step #3
+ hf = rcAllocHeightfield();
+
+ ERR_FAIL_COND(!hf);
+ ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
+
+ bake_state = "Marking walkable triangles..."; // step #4
+ {
+ Vector<unsigned char> tri_areas;
+ tri_areas.resize(ntris);
+
+ ERR_FAIL_COND(tri_areas.size() == 0);
+
+ memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char));
+ rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw());
+
+ ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb));
+ }
+
+ if (p_navigation_mesh->get_filter_low_hanging_obstacles()) {
+ rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf);
+ }
+ if (p_navigation_mesh->get_filter_ledge_spans()) {
+ rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf);
+ }
+ if (p_navigation_mesh->get_filter_walkable_low_height_spans()) {
+ rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
+ }
+
+ bake_state = "Constructing compact heightfield..."; // step #5
+
+ chf = rcAllocCompactHeightfield();
+
+ ERR_FAIL_COND(!chf);
+ ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf));
+
+ rcFreeHeightField(hf);
+ hf = nullptr;
+
+ bake_state = "Eroding walkable area..."; // step #6
+
+ ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
+
+ bake_state = "Partitioning..."; // step #7
+
+ if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
+ ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
+ ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
+ } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) {
+ ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
+ } else {
+ ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea));
+ }
+
+ bake_state = "Creating contours..."; // step #8
+
+ cset = rcAllocContourSet();
+
+ ERR_FAIL_COND(!cset);
+ ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
+
+ bake_state = "Creating polymesh..."; // step #9
+
+ poly_mesh = rcAllocPolyMesh();
+ ERR_FAIL_COND(!poly_mesh);
+ ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh));
+
+ detail_mesh = rcAllocPolyMeshDetail();
+ ERR_FAIL_COND(!detail_mesh);
+ ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh));
+
+ rcFreeCompactHeightfield(chf);
+ chf = nullptr;
+ rcFreeContourSet(cset);
+ cset = nullptr;
+
+ bake_state = "Converting to native navigation mesh..."; // step #10
+
+ Vector<Vector3> nav_vertices;
+
+ for (int i = 0; i < detail_mesh->nverts; i++) {
+ const float *v = &detail_mesh->verts[i * 3];
+ nav_vertices.push_back(Vector3(v[0], v[1], v[2]));
+ }
+ p_navigation_mesh->set_vertices(nav_vertices);
+ p_navigation_mesh->clear_polygons();
+
+ for (int i = 0; i < detail_mesh->nmeshes; i++) {
+ const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4];
+ const unsigned int detail_mesh_bverts = detail_mesh_m[0];
+ const unsigned int detail_mesh_m_btris = detail_mesh_m[2];
+ const unsigned int detail_mesh_ntris = detail_mesh_m[3];
+ const unsigned char *detail_mesh_tris = &detail_mesh->tris[detail_mesh_m_btris * 4];
+ for (unsigned int j = 0; j < detail_mesh_ntris; j++) {
+ Vector<int> nav_indices;
+ nav_indices.resize(3);
+ // Polygon order in recast is opposite than godot's
+ nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0]));
+ nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2]));
+ nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1]));
+ p_navigation_mesh->add_polygon(nav_indices);
+ }
+ }
+
+ bake_state = "Cleanup..."; // step #11
+
+ rcFreePolyMesh(poly_mesh);
+ poly_mesh = nullptr;
+ rcFreePolyMeshDetail(detail_mesh);
+ detail_mesh = nullptr;
+
+ bake_state = "Baking finished."; // step #12
+}
+
+bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
+ ERR_FAIL_COND_V(!p_callback.is_valid(), false);
+
+ Callable::CallError ce;
+ Variant result;
+ p_callback.callp(nullptr, 0, result, ce);
+
+ return ce.error == Callable::CallError::CALL_OK;
+}
+
+#endif // _3D_DISABLED
diff --git a/modules/navigation/nav_mesh_generator_3d.h b/modules/navigation/nav_mesh_generator_3d.h
new file mode 100644
index 0000000000..4220927641
--- /dev/null
+++ b/modules/navigation/nav_mesh_generator_3d.h
@@ -0,0 +1,109 @@
+/**************************************************************************/
+/* nav_mesh_generator_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 NAV_MESH_GENERATOR_3D_H
+#define NAV_MESH_GENERATOR_3D_H
+
+#ifndef _3D_DISABLED
+
+#include "core/object/class_db.h"
+#include "core/object/worker_thread_pool.h"
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
+class Node;
+class NavigationMesh;
+class NavigationMeshSourceGeometryData3D;
+
+class NavMeshGenerator3D : public Object {
+ static NavMeshGenerator3D *singleton;
+
+ static Mutex baking_navmesh_mutex;
+ static Mutex generator_task_mutex;
+
+ static bool use_threads;
+ static bool baking_use_multiple_threads;
+ static bool baking_use_high_priority_threads;
+
+ struct NavMeshGeneratorTask3D {
+ enum TaskStatus {
+ BAKING_STARTED,
+ BAKING_FINISHED,
+ BAKING_FAILED,
+ CALLBACK_DISPATCHED,
+ CALLBACK_FAILED,
+ };
+
+ Ref<NavigationMesh> navigation_mesh;
+ Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
+ Callable callback;
+ WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+ };
+
+ static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
+
+ static void generator_thread_bake(void *p_arg);
+
+ static HashSet<Ref<NavigationMesh>> baking_navmeshes;
+
+ static void generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children);
+ static void generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node);
+ static void generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data);
+
+ static void generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
+ static void generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
+ static void generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
+#ifdef MODULE_CSG_ENABLED
+ static void generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
+#endif // MODULE_CSG_ENABLED
+#ifdef MODULE_GRIDMAP_ENABLED
+ static void generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
+#endif // MODULE_GRIDMAP_ENABLED
+
+ static bool generator_emit_callback(const Callable &p_callback);
+
+public:
+ static NavMeshGenerator3D *get_singleton();
+
+ static void sync();
+ static void cleanup();
+ static void finish();
+
+ static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
+ static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
+ static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
+
+ NavMeshGenerator3D();
+ ~NavMeshGenerator3D();
+};
+
+#endif // _3D_DISABLED
+
+#endif // NAV_MESH_GENERATOR_3D_H
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 89afb4a8ea..8393896db1 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -32,451 +32,11 @@
#include "navigation_mesh_generator.h"
-#include "core/math/convex_hull.h"
-#include "core/os/thread.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/multimesh_instance_3d.h"
-#include "scene/3d/physics_body_3d.h"
-#include "scene/resources/box_shape_3d.h"
-#include "scene/resources/capsule_shape_3d.h"
-#include "scene/resources/concave_polygon_shape_3d.h"
-#include "scene/resources/convex_polygon_shape_3d.h"
-#include "scene/resources/cylinder_shape_3d.h"
-#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
-#include "scene/resources/primitive_meshes.h"
-#include "scene/resources/shape_3d.h"
-#include "scene/resources/sphere_shape_3d.h"
-#include "scene/resources/world_boundary_shape_3d.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
-#endif
-
-#include "modules/modules_enabled.gen.h" // For csg, gridmap.
-
-#ifdef MODULE_CSG_ENABLED
-#include "modules/csg/csg_shape.h"
-#endif
-#ifdef MODULE_GRIDMAP_ENABLED
-#include "modules/gridmap/grid_map.h"
-#endif
+#include "servers/navigation_server_3d.h"
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;
-void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices) {
- p_vertices.push_back(p_vec3.x);
- p_vertices.push_back(p_vec3.y);
- p_vertices.push_back(p_vec3.z);
-}
-
-void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
- int current_vertex_count;
-
- for (int i = 0; i < p_mesh->get_surface_count(); i++) {
- current_vertex_count = p_vertices.size() / 3;
-
- if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
- continue;
- }
-
- int index_count = 0;
- if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
- index_count = p_mesh->surface_get_array_index_len(i);
- } else {
- index_count = p_mesh->surface_get_array_len(i);
- }
-
- ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0));
-
- int face_count = index_count / 3;
-
- Array a = p_mesh->surface_get_arrays(i);
- ERR_CONTINUE(a.is_empty() || (a.size() != Mesh::ARRAY_MAX));
-
- Vector<Vector3> mesh_vertices = a[Mesh::ARRAY_VERTEX];
- ERR_CONTINUE(mesh_vertices.is_empty());
- const Vector3 *vr = mesh_vertices.ptr();
-
- if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
- Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX];
- ERR_CONTINUE(mesh_indices.is_empty() || (mesh_indices.size() != index_count));
- const int *ir = mesh_indices.ptr();
-
- for (int j = 0; j < mesh_vertices.size(); j++) {
- _add_vertex(p_xform.xform(vr[j]), p_vertices);
- }
-
- for (int j = 0; j < face_count; j++) {
- // CCW
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 0]));
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 2]));
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 1]));
- }
- } else {
- ERR_CONTINUE(mesh_vertices.size() != index_count);
- face_count = mesh_vertices.size() / 3;
- for (int j = 0; j < face_count; j++) {
- _add_vertex(p_xform.xform(vr[j * 3 + 0]), p_vertices);
- _add_vertex(p_xform.xform(vr[j * 3 + 2]), p_vertices);
- _add_vertex(p_xform.xform(vr[j * 3 + 1]), p_vertices);
-
- p_indices.push_back(current_vertex_count + (j * 3 + 0));
- p_indices.push_back(current_vertex_count + (j * 3 + 1));
- p_indices.push_back(current_vertex_count + (j * 3 + 2));
- }
- }
- }
-}
-
-void NavigationMeshGenerator::_add_mesh_array(const Array &p_array, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
- ERR_FAIL_COND(p_array.size() != Mesh::ARRAY_MAX);
-
- Vector<Vector3> mesh_vertices = p_array[Mesh::ARRAY_VERTEX];
- ERR_FAIL_COND(mesh_vertices.is_empty());
- const Vector3 *vr = mesh_vertices.ptr();
-
- Vector<int> mesh_indices = p_array[Mesh::ARRAY_INDEX];
- ERR_FAIL_COND(mesh_indices.is_empty());
- const int *ir = mesh_indices.ptr();
-
- const int face_count = mesh_indices.size() / 3;
- const int current_vertex_count = p_vertices.size() / 3;
-
- for (int j = 0; j < mesh_vertices.size(); j++) {
- _add_vertex(p_xform.xform(vr[j]), p_vertices);
- }
-
- for (int j = 0; j < face_count; j++) {
- // CCW
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 0]));
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 2]));
- p_indices.push_back(current_vertex_count + (ir[j * 3 + 1]));
- }
-}
-
-void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
- ERR_FAIL_COND(p_faces.is_empty());
- ERR_FAIL_COND(p_faces.size() % 3 != 0);
- int face_count = p_faces.size() / 3;
- int current_vertex_count = p_vertices.size() / 3;
-
- for (int j = 0; j < face_count; j++) {
- _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_vertices);
- _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_vertices);
- _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_vertices);
-
- p_indices.push_back(current_vertex_count + (j * 3 + 0));
- p_indices.push_back(current_vertex_count + (j * 3 + 2));
- p_indices.push_back(current_vertex_count + (j * 3 + 1));
- }
-}
-
-void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
- if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
- Ref<Mesh> mesh = mesh_instance->get_mesh();
- if (mesh.is_valid()) {
- _add_mesh(mesh, p_navmesh_transform * mesh_instance->get_global_transform(), p_vertices, p_indices);
- }
- }
-
- if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node);
- Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
- if (multimesh.is_valid()) {
- Ref<Mesh> mesh = multimesh->get_mesh();
- if (mesh.is_valid()) {
- int n = multimesh->get_visible_instance_count();
- if (n == -1) {
- n = multimesh->get_instance_count();
- }
- for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
- }
- }
- }
- }
-
-#ifdef MODULE_CSG_ENABLED
- if (Object::cast_to<CSGShape3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node);
- Array meshes = csg_shape->get_meshes();
- if (!meshes.is_empty()) {
- Ref<Mesh> mesh = meshes[1];
- if (mesh.is_valid()) {
- _add_mesh(mesh, p_navmesh_transform * csg_shape->get_global_transform(), p_vertices, p_indices);
- }
- }
- }
-#endif
-
- if (Object::cast_to<StaticBody3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) {
- StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node);
-
- if (static_body->get_collision_layer() & p_collision_mask) {
- List<uint32_t> shape_owners;
- static_body->get_shape_owners(&shape_owners);
- for (uint32_t shape_owner : shape_owners) {
- if (static_body->is_shape_owner_disabled(shape_owner)) {
- continue;
- }
- const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
- for (int i = 0; i < shape_count; i++) {
- Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i);
- if (s.is_null()) {
- continue;
- }
-
- const Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
-
- BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
- if (box) {
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- BoxMesh::create_mesh_array(arr, box->get_size());
- _add_mesh_array(arr, transform, p_vertices, p_indices);
- }
-
- CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
- if (capsule) {
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height());
- _add_mesh_array(arr, transform, p_vertices, p_indices);
- }
-
- CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s);
- if (cylinder) {
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height());
- _add_mesh_array(arr, transform, p_vertices, p_indices);
- }
-
- SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s);
- if (sphere) {
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0);
- _add_mesh_array(arr, transform, p_vertices, p_indices);
- }
-
- ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s);
- if (concave_polygon) {
- _add_faces(concave_polygon->get_faces(), transform, p_vertices, p_indices);
- }
-
- ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
- if (convex_polygon) {
- Vector<Vector3> varr = Variant(convex_polygon->get_points());
- Geometry3D::MeshData md;
-
- Error err = ConvexHullComputer::convex_hull(varr, md);
-
- if (err == OK) {
- PackedVector3Array faces;
-
- for (const Geometry3D::MeshData::Face &face : md.faces) {
- for (uint32_t k = 2; k < face.indices.size(); ++k) {
- faces.push_back(md.vertices[face.indices[0]]);
- faces.push_back(md.vertices[face.indices[k - 1]]);
- faces.push_back(md.vertices[face.indices[k]]);
- }
- }
-
- _add_faces(faces, transform, p_vertices, p_indices);
- }
- }
-
- HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s);
- if (heightmap_shape) {
- int heightmap_depth = heightmap_shape->get_map_depth();
- int heightmap_width = heightmap_shape->get_map_width();
-
- if (heightmap_depth >= 2 && heightmap_width >= 2) {
- const Vector<real_t> &map_data = heightmap_shape->get_map_data();
-
- Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
- Vector2 start = heightmap_gridsize * -0.5;
-
- Vector<Vector3> vertex_array;
- vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
- int map_data_current_index = 0;
-
- for (int d = 0; d < heightmap_depth; d++) {
- for (int w = 0; w < heightmap_width; w++) {
- if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
- float top_left_height = map_data[map_data_current_index];
- float top_right_height = map_data[map_data_current_index + 1];
- float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
- float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
-
- Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
- Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
- Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
- Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
-
- vertex_array.push_back(top_right);
- vertex_array.push_back(bottom_left);
- vertex_array.push_back(top_left);
- vertex_array.push_back(top_right);
- vertex_array.push_back(bottom_right);
- vertex_array.push_back(bottom_left);
- }
- map_data_current_index += 1;
- }
- }
- if (vertex_array.size() > 0) {
- _add_faces(vertex_array, transform, p_vertices, p_indices);
- }
- }
- }
- }
- }
- }
- }
-
-#ifdef MODULE_GRIDMAP_ENABLED
- GridMap *gridmap = Object::cast_to<GridMap>(p_node);
-
- if (gridmap) {
- if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- Array meshes = gridmap->get_meshes();
- Transform3D xform = gridmap->get_global_transform();
- for (int i = 0; i < meshes.size(); i += 2) {
- Ref<Mesh> mesh = meshes[i + 1];
- if (mesh.is_valid()) {
- _add_mesh(mesh, p_navmesh_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
- }
- }
- }
-
- if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) {
- Array shapes = gridmap->get_collision_shapes();
- for (int i = 0; i < shapes.size(); i += 2) {
- RID shape = shapes[i + 1];
- PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
- Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
-
- switch (type) {
- case PhysicsServer3D::SHAPE_SPHERE: {
- real_t radius = data;
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- SphereMesh::create_mesh_array(arr, radius, radius * 2.0);
- _add_mesh_array(arr, shapes[i], p_vertices, p_indices);
- } break;
- case PhysicsServer3D::SHAPE_BOX: {
- Vector3 extents = data;
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- BoxMesh::create_mesh_array(arr, extents * 2.0);
- _add_mesh_array(arr, shapes[i], p_vertices, p_indices);
- } break;
- case PhysicsServer3D::SHAPE_CAPSULE: {
- Dictionary dict = data;
- real_t radius = dict["radius"];
- real_t height = dict["height"];
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- CapsuleMesh::create_mesh_array(arr, radius, height);
- _add_mesh_array(arr, shapes[i], p_vertices, p_indices);
- } break;
- case PhysicsServer3D::SHAPE_CYLINDER: {
- Dictionary dict = data;
- real_t radius = dict["radius"];
- real_t height = dict["height"];
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- CylinderMesh::create_mesh_array(arr, radius, radius, height);
- _add_mesh_array(arr, shapes[i], p_vertices, p_indices);
- } break;
- case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
- PackedVector3Array vertices = data;
- Geometry3D::MeshData md;
-
- Error err = ConvexHullComputer::convex_hull(vertices, md);
-
- if (err == OK) {
- PackedVector3Array faces;
-
- for (const Geometry3D::MeshData::Face &face : md.faces) {
- for (uint32_t k = 2; k < face.indices.size(); ++k) {
- faces.push_back(md.vertices[face.indices[0]]);
- faces.push_back(md.vertices[face.indices[k - 1]]);
- faces.push_back(md.vertices[face.indices[k]]);
- }
- }
-
- _add_faces(faces, shapes[i], p_vertices, p_indices);
- }
- } break;
- case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
- Dictionary dict = data;
- PackedVector3Array faces = Variant(dict["faces"]);
- _add_faces(faces, shapes[i], p_vertices, p_indices);
- } break;
- case PhysicsServer3D::SHAPE_HEIGHTMAP: {
- Dictionary dict = data;
- ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
- int heightmap_depth = dict["depth"];
- int heightmap_width = dict["width"];
-
- if (heightmap_depth >= 2 && heightmap_width >= 2) {
- const Vector<real_t> &map_data = dict["heights"];
-
- Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
- Vector2 start = heightmap_gridsize * -0.5;
-
- Vector<Vector3> vertex_array;
- vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
- int map_data_current_index = 0;
-
- for (int d = 0; d < heightmap_depth; d++) {
- for (int w = 0; w < heightmap_width; w++) {
- if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
- float top_left_height = map_data[map_data_current_index];
- float top_right_height = map_data[map_data_current_index + 1];
- float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
- float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
-
- Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
- Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
- Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
- Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
-
- vertex_array.push_back(top_right);
- vertex_array.push_back(bottom_left);
- vertex_array.push_back(top_left);
- vertex_array.push_back(top_right);
- vertex_array.push_back(bottom_right);
- vertex_array.push_back(bottom_left);
- }
- map_data_current_index += 1;
- }
- }
- if (vertex_array.size() > 0) {
- _add_faces(vertex_array, shapes[i], p_vertices, p_indices);
- }
- }
- } break;
- default: {
- WARN_PRINT("Unsupported collision shape type.");
- } break;
- }
- }
- }
- }
-#endif
-
- if (p_recurse_children) {
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_navmesh_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
- }
- }
-}
-
NavigationMeshGenerator *NavigationMeshGenerator::get_singleton() {
return singleton;
}
@@ -500,285 +60,11 @@ void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_navigation_mesh) {
}
void NavigationMeshGenerator::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
- ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
- ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
- ERR_FAIL_COND_MSG(p_root_node == nullptr, "No parsing root node specified.");
- ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree.");
-
- Vector<float> vertices;
- Vector<int> indices;
-
- List<Node *> parse_nodes;
-
- if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
- parse_nodes.push_back(p_root_node);
- } else {
- p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes);
- }
-
- Transform3D navmesh_xform = Transform3D();
- if (Object::cast_to<Node3D>(p_root_node)) {
- navmesh_xform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse();
- }
- for (Node *E : parse_nodes) {
- NavigationMesh::ParsedGeometryType geometry_type = p_navigation_mesh->get_parsed_geometry_type();
- uint32_t collision_mask = p_navigation_mesh->get_collision_mask();
- bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
- _parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children);
- }
-
- p_source_geometry_data->set_vertices(vertices);
- p_source_geometry_data->set_indices(indices);
-
- if (p_callback.is_valid()) {
- Callable::CallError ce;
- Variant result;
- p_callback.callp(nullptr, 0, result, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- //
- }
- }
+ NavigationServer3D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback);
}
void NavigationMeshGenerator::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
- ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
- ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
- ERR_FAIL_COND_MSG(!p_source_geometry_data->has_data(), "NavigationMeshSourceGeometryData3D is empty. Parse source geometry first.");
-
- generator_mutex.lock();
- if (baking_navmeshes.has(p_navigation_mesh)) {
- generator_mutex.unlock();
- ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
- } else {
- baking_navmeshes.insert(p_navigation_mesh);
- generator_mutex.unlock();
- }
-
-#ifndef _3D_DISABLED
- const Vector<float> vertices = p_source_geometry_data->get_vertices();
- const Vector<int> indices = p_source_geometry_data->get_indices();
-
- if (vertices.size() < 3 || indices.size() < 3) {
- return;
- }
-
- rcHeightfield *hf = nullptr;
- rcCompactHeightfield *chf = nullptr;
- rcContourSet *cset = nullptr;
- rcPolyMesh *poly_mesh = nullptr;
- rcPolyMeshDetail *detail_mesh = nullptr;
- rcContext ctx;
-
- // added to keep track of steps, no functionality right now
- String bake_state = "";
-
- bake_state = "Setting up Configuration..."; // step #1
-
- const float *verts = vertices.ptr();
- const int nverts = vertices.size() / 3;
- const int *tris = indices.ptr();
- const int ntris = indices.size() / 3;
-
- float bmin[3], bmax[3];
- rcCalcBounds(verts, nverts, bmin, bmax);
-
- rcConfig cfg;
- memset(&cfg, 0, sizeof(cfg));
-
- cfg.cs = p_navigation_mesh->get_cell_size();
- cfg.ch = p_navigation_mesh->get_cell_height();
- cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope();
- cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch);
- cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch);
- cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs);
- cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size());
- cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error();
- cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size());
- cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size());
- cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon();
- cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
- cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
-
- if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
- WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) {
- WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) {
- WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) {
- WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) {
- WARN_PRINT("Property region_min_size is converted to int and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) {
- WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
- }
- if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) {
- WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision.");
- }
- if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) {
- WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
- }
-
- cfg.bmin[0] = bmin[0];
- cfg.bmin[1] = bmin[1];
- cfg.bmin[2] = bmin[2];
- cfg.bmax[0] = bmax[0];
- cfg.bmax[1] = bmax[1];
- cfg.bmax[2] = bmax[2];
-
- AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb();
- if (baking_aabb.has_volume()) {
- Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset();
- cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x;
- cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y;
- cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z;
- cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0];
- cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1];
- cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
- }
-
- bake_state = "Calculating grid size..."; // step #2
- rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
-
- // ~30000000 seems to be around sweetspot where Editor baking breaks
- if ((cfg.width * cfg.height) > 30000000) {
- WARN_PRINT("NavigationMesh baking process will likely fail."
- "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings."
- "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues."
- "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry.");
- }
-
- bake_state = "Creating heightfield..."; // step #3
- hf = rcAllocHeightfield();
-
- ERR_FAIL_COND(!hf);
- ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
-
- bake_state = "Marking walkable triangles..."; // step #4
- {
- Vector<unsigned char> tri_areas;
- tri_areas.resize(ntris);
-
- ERR_FAIL_COND(tri_areas.size() == 0);
-
- memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char));
- rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw());
-
- ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb));
- }
-
- if (p_navigation_mesh->get_filter_low_hanging_obstacles()) {
- rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf);
- }
- if (p_navigation_mesh->get_filter_ledge_spans()) {
- rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf);
- }
- if (p_navigation_mesh->get_filter_walkable_low_height_spans()) {
- rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
- }
-
- bake_state = "Constructing compact heightfield..."; // step #5
-
- chf = rcAllocCompactHeightfield();
-
- ERR_FAIL_COND(!chf);
- ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf));
-
- rcFreeHeightField(hf);
- hf = nullptr;
-
- bake_state = "Eroding walkable area..."; // step #6
-
- ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
-
- bake_state = "Partitioning..."; // step #7
-
- if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
- ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
- ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
- } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) {
- ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
- } else {
- ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea));
- }
-
- bake_state = "Creating contours..."; // step #8
-
- cset = rcAllocContourSet();
-
- ERR_FAIL_COND(!cset);
- ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
-
- bake_state = "Creating polymesh..."; // step #9
-
- poly_mesh = rcAllocPolyMesh();
- ERR_FAIL_COND(!poly_mesh);
- ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh));
-
- detail_mesh = rcAllocPolyMeshDetail();
- ERR_FAIL_COND(!detail_mesh);
- ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh));
-
- rcFreeCompactHeightfield(chf);
- chf = nullptr;
- rcFreeContourSet(cset);
- cset = nullptr;
-
- bake_state = "Converting to native navigation mesh..."; // step #10
-
- Vector<Vector3> nav_vertices;
-
- for (int i = 0; i < detail_mesh->nverts; i++) {
- const float *v = &detail_mesh->verts[i * 3];
- nav_vertices.push_back(Vector3(v[0], v[1], v[2]));
- }
- p_navigation_mesh->set_vertices(nav_vertices);
- p_navigation_mesh->clear_polygons();
-
- for (int i = 0; i < detail_mesh->nmeshes; i++) {
- const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4];
- const unsigned int detail_mesh_bverts = detail_mesh_m[0];
- const unsigned int detail_mesh_m_btris = detail_mesh_m[2];
- const unsigned int detail_mesh_ntris = detail_mesh_m[3];
- const unsigned char *detail_mesh_tris = &detail_mesh->tris[detail_mesh_m_btris * 4];
- for (unsigned int j = 0; j < detail_mesh_ntris; j++) {
- Vector<int> nav_indices;
- nav_indices.resize(3);
- // Polygon order in recast is opposite than godot's
- nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0]));
- nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2]));
- nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1]));
- p_navigation_mesh->add_polygon(nav_indices);
- }
- }
-
- bake_state = "Cleanup..."; // step #11
-
- rcFreePolyMesh(poly_mesh);
- poly_mesh = nullptr;
- rcFreePolyMeshDetail(detail_mesh);
- detail_mesh = nullptr;
-
- bake_state = "Baking finished."; // step #12
-#endif // _3D_DISABLED
-
- generator_mutex.lock();
- baking_navmeshes.erase(p_navigation_mesh);
- generator_mutex.unlock();
-
- if (p_callback.is_valid()) {
- Callable::CallError ce;
- Variant result;
- p_callback.callp(nullptr, 0, result, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- //
- }
- }
+ NavigationServer3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
}
void NavigationMeshGenerator::_bind_methods() {
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 4bf2b64f44..08fe9f9142 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -36,27 +36,16 @@
#include "scene/3d/navigation_region_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include <Recast.h>
-
class NavigationMeshSourceGeometryData3D;
class NavigationMeshGenerator : public Object {
GDCLASS(NavigationMeshGenerator, Object);
- Mutex generator_mutex;
static NavigationMeshGenerator *singleton;
- HashSet<Ref<NavigationMesh>> baking_navmeshes;
-
protected:
static void _bind_methods();
- static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
- static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _add_mesh_array(const Array &p_array, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
-
public:
static NavigationMeshGenerator *get_singleton();
diff --git a/modules/navigation/register_types.cpp b/modules/navigation/register_types.cpp
index 1401833d0e..1548ff4b9c 100644
--- a/modules/navigation/register_types.cpp
+++ b/modules/navigation/register_types.cpp
@@ -32,9 +32,11 @@
#include "godot_navigation_server.h"
+#ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED
#include "navigation_mesh_generator.h"
#endif
+#endif // DISABLE_DEPRECATED
#ifdef TOOLS_ENABLED
#include "editor/navigation_mesh_editor_plugin.h"
@@ -43,9 +45,11 @@
#include "core/config/engine.h"
#include "servers/navigation_server_3d.h"
+#ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED
NavigationMeshGenerator *_nav_mesh_generator = nullptr;
#endif
+#endif // DISABLE_DEPRECATED
NavigationServer3D *new_server() {
return memnew(GodotNavigationServer);
@@ -55,11 +59,13 @@ void initialize_navigation_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
NavigationServer3DManager::set_default_server(new_server);
+#ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED
_nav_mesh_generator = memnew(NavigationMeshGenerator);
GDREGISTER_CLASS(NavigationMeshGenerator);
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
#endif
+#endif // DISABLE_DEPRECATED
}
#ifdef TOOLS_ENABLED
@@ -74,9 +80,11 @@ void uninitialize_navigation_module(ModuleInitializationLevel p_level) {
return;
}
+#ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED
if (_nav_mesh_generator) {
memdelete(_nav_mesh_generator);
}
#endif
+#endif // DISABLE_DEPRECATED
}
diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml
index 4c6cdfbf12..1ab2552547 100644
--- a/modules/noise/doc_classes/FastNoiseLite.xml
+++ b/modules/noise/doc_classes/FastNoiseLite.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This class generates noise using the FastNoiseLite library, which is a collection of several noise algorithms including Cellular, Perlin, Value, and more.
- Most generated noise values are in the range of [code][-1,1][/code], however not always. Some of the cellular noise algorithms return results above [code]1[/code].
+ Most generated noise values are in the range of [code][-1, 1][/code], but not always. Some of the cellular noise algorithms return results above [code]1[/code].
</description>
<tutorials>
</tutorials>
diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml
index dd232af1cc..7d74c84f93 100644
--- a/modules/noise/doc_classes/Noise.xml
+++ b/modules/noise/doc_classes/Noise.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This class defines the interface for noise generation libraries to inherit from.
- A default get_seamless_noise() implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from get_image(), reverses the quadrants of the image, then uses the strips of extra width to blend over the seams.
+ A default [method get_seamless_image] implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from the [method get_image] method, reverses the quadrants of the image, then uses the strips of extra width to blend over the seams.
Inheriting noise classes can optionally override this function to provide a more optimal algorithm.
</description>
<tutorials>
diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml
index e25af794d4..3f4afc5141 100644
--- a/modules/noise/doc_classes/NoiseTexture2D.xml
+++ b/modules/noise/doc_classes/NoiseTexture2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A texture filled with noise generated by a [Noise] object.
+ A 2D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
- Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures.
+ Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures.
The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data:
[codeblock]
var texture = NoiseTexture2D.new()
diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml
index 0ada6942ad..e8e205bc68 100644
--- a/modules/noise/doc_classes/NoiseTexture3D.xml
+++ b/modules/noise/doc_classes/NoiseTexture3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture3D" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A texture filled with noise generated by a [Noise] object.
+ A 3D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
- Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size.
+ Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size.
The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image:
[codeblock]
var texture = NoiseTexture3D.new()
diff --git a/modules/noise/icons/NoiseTexture3D.svg b/modules/noise/icons/NoiseTexture3D.svg
new file mode 100644
index 0000000000..92da633dce
--- /dev/null
+++ b/modules/noise/icons/NoiseTexture3D.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13zM3 11h4l1.25-1.25V9H9l1.25-1.25v-2h-2L7 7v.75h-.75v-2h-2L3 7z" fill="#e0e0e0"/><path d="M3 7h2l1.25-1.25h-2zm2 2h2V7.75h-.75zm2-2h2l1.25-1.25H8z" fill="#000" fill-opacity=".4"/><path d="M5 7v2l1.25-1.25v-2zm2 2v2l1.25-1.25V9zm2 0V7l1.25-1.25v2z" fill="#000" fill-opacity=".2"/></svg>
diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp
index ed242e7faa..33e257a5c2 100644
--- a/modules/noise/noise_texture_3d.cpp
+++ b/modules/noise/noise_texture_3d.cpp
@@ -110,6 +110,7 @@ void NoiseTexture3D::_set_texture_data(const TypedArray<Image> &p_data) {
} else {
texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
}
+ format = data[0]->get_format();
}
emit_changed();
}
@@ -346,6 +347,5 @@ Vector<Ref<Image>> NoiseTexture3D::get_data() const {
}
Image::Format NoiseTexture3D::get_format() const {
- ERR_FAIL_COND_V(!texture.is_valid(), Image::FORMAT_L8);
- return RS::get_singleton()->texture_3d_get(texture)[0]->get_format();
+ return format;
}
diff --git a/modules/noise/noise_texture_3d.h b/modules/noise/noise_texture_3d.h
index 397711ca98..13125efe7f 100644
--- a/modules/noise/noise_texture_3d.h
+++ b/modules/noise/noise_texture_3d.h
@@ -60,6 +60,8 @@ private:
Ref<Gradient> color_ramp;
Ref<Noise> noise;
+ Image::Format format = Image::FORMAT_L8;
+
void _thread_done(const TypedArray<Image> &p_data);
static void _thread_function(void *p_ud);
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index f49dc390de..1bd10f1009 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -52,10 +52,11 @@ if env["builtin_openxr"]:
env_thirdparty = env_openxr.Clone()
env_thirdparty.disable_warnings()
+
env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+ if env["disable_exceptions"]:
+ env_thirdparty.AppendUnique(CPPDEFINES=["XRLOADER_DISABLE_EXCEPTION_HANDLING", ("JSON_USE_EXCEPTION", 0)])
- if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
- env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
# add in external jsoncpp dependency
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index 81ba9c56b8..4829f713d2 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -126,7 +126,7 @@ void OpenXRExtensionWrapperExtension::on_before_instance_created() {
}
void OpenXRExtensionWrapperExtension::on_instance_created(const XrInstance p_instance) {
- uint64_t instance = reinterpret_cast<uint64_t>(p_instance);
+ uint64_t instance = (uint64_t)p_instance;
GDVIRTUAL_CALL(_on_instance_created, instance);
}
@@ -135,7 +135,7 @@ void OpenXRExtensionWrapperExtension::on_instance_destroyed() {
}
void OpenXRExtensionWrapperExtension::on_session_created(const XrSession p_session) {
- uint64_t session = reinterpret_cast<uint64_t>(p_session);
+ uint64_t session = (uint64_t)p_session;
GDVIRTUAL_CALL(_on_session_created, session);
}
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 91684b55b9..9885190cb1 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -795,7 +795,7 @@ bool OpenXRAPI::create_swapchains() {
Also Godot only creates a swapchain for the main output.
OpenXR will require us to create swapchains as the render target for additional viewports if we want to use the layer system
- to optimize text rendering and background rendering as OpenXR may choose to re-use the results for reprojection while we're
+ to optimize text rendering and background rendering as OpenXR may choose to reuse the results for reprojection while we're
already rendering the next frame.
Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
@@ -1668,7 +1668,7 @@ bool OpenXRAPI::process() {
}
bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
- ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // this was not released when it should be, error out and re-use...
+ ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // This was not released when it should be, error out and reuse...
XrResult result;
XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 13d8a2c17a..2a502d081e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2113,12 +2113,10 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid)
name = vformat("unknown_%d", names[i].name_id);
} break;
}
+ String text;
unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1;
- // @todo After godot-cpp#1141 is fixed, use text.resize() and write directly to text.wptr() instead of using a temporary buffer.
- char32_t *buffer = memnew_arr(char32_t, text_size);
- hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)buffer);
- String text(buffer);
- memdelete_arr(buffer);
+ text.resize(text_size);
+ hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw());
if (!text.is_empty()) {
Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))];
id_string[name] = text;
@@ -4771,7 +4769,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {
- // Insert an additional space when cutting word bound for aesthetics.
+ // Insert an additional space when cutting word bound for esthetics.
if (cut_per_word && (ellipsis_pos > 0)) {
Glyph gl;
gl.count = 1;
diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp
index 2f48f1564c..951ca15503 100644
--- a/modules/text_server_adv/thorvg_svg_in_ot.cpp
+++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp
@@ -155,21 +155,10 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
}
String xml_code_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
-#ifndef GDEXTENSION
gl_state.xml_code = xml_code_str.utf8();
-#else
- CharString xml_code = xml_code_str.utf8();
- gl_state.xml_code_length = xml_code.length();
- gl_state.xml_code = memnew_arr(char, gl_state.xml_code_length);
- memcpy(gl_state.xml_code, xml_code.get_data(), gl_state.xml_code_length);
-#endif
picture = tvg::Picture::gen();
-#ifndef GDEXTENSION
result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false);
-#else
- result = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false);
-#endif
if (result != tvg::Result::Success) {
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
}
@@ -257,11 +246,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready.");
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
-#ifndef GDEXTENSION
tvg::Result res = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false);
-#else
- tvg::Result res = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false);
-#endif
if (res != tvg::Result::Success) {
ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
}
diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h
index 5e79c8e444..034fffb5e6 100644
--- a/modules/text_server_adv/thorvg_svg_in_ot.h
+++ b/modules/text_server_adv/thorvg_svg_in_ot.h
@@ -66,22 +66,8 @@ struct GL_State {
float y = 0;
float w = 0;
float h = 0;
-#ifndef GDEXTENSION
CharString xml_code;
-#else
- // @todo After godot-cpp#1142 is fixed, use CharString just like when compiled as a Godot module.
- char *xml_code = nullptr;
- int xml_code_length = 0;
-#endif
tvg::Matrix m;
-
-#ifdef GDEXTENSION
- ~GL_State() {
- if (xml_code) {
- memdelete_arr(xml_code);
- }
- }
-#endif
};
struct TVG_State {
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index dc0a7df81a..70da5e2782 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3563,7 +3563,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {
- // Insert an additional space when cutting word bound for aesthetics.
+ // Insert an additional space when cutting word bound for esthetics.
if (cut_per_word && (ellipsis_pos > 0)) {
Glyph gl;
gl.count = 1;
diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml
index 0fa2672044..bf596806b2 100644
--- a/modules/zip/doc_classes/ZIPReader.xml
+++ b/modules/zip/doc_classes/ZIPReader.xml
@@ -25,6 +25,15 @@
Closes the underlying resources used by this instance.
</description>
</method>
+ <method name="file_exists">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="case_sensitive" type="bool" default="true" />
+ <description>
+ Returns [code]true[/code] if the file exists in the loaded zip archive.
+ Must be called after [method open].
+ </description>
+ </method>
<method name="get_files">
<return type="PackedStringArray" />
<description>
diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp
index 2684875e1c..5752b829ef 100644
--- a/modules/zip/zip_reader.cpp
+++ b/modules/zip/zip_reader.cpp
@@ -118,6 +118,21 @@ PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) {
return data;
}
+bool ZIPReader::file_exists(String p_path, bool p_case_sensitive) {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "ZIPReader must be opened before use.");
+
+ int cs = p_case_sensitive ? 1 : 2;
+ if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) {
+ return false;
+ }
+ if (unzOpenCurrentFile(uzf) != UNZ_OK) {
+ return false;
+ }
+
+ unzCloseCurrentFile(uzf);
+ return true;
+}
+
ZIPReader::ZIPReader() {}
ZIPReader::~ZIPReader() {
@@ -131,4 +146,5 @@ void ZIPReader::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close);
ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files);
ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true)));
+ ClassDB::bind_method(D_METHOD("file_exists", "path", "case_sensitive"), &ZIPReader::file_exists, DEFVAL(Variant(true)));
}
diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h
index c074197eb4..0f78352e3f 100644
--- a/modules/zip/zip_reader.h
+++ b/modules/zip/zip_reader.h
@@ -51,6 +51,7 @@ public:
PackedStringArray get_files();
PackedByteArray read_file(String p_path, bool p_case_sensitive);
+ bool file_exists(String p_path, bool p_case_sensitive);
ZIPReader();
~ZIPReader();
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 2860898e5c..33c6565789 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -170,10 +170,6 @@ def configure(env: "Environment"):
env["RANLIB"] = compiler_path + "/llvm-ranlib"
env["AS"] = compiler_path + "/clang"
- # Disable exceptions on template builds
- if not env.editor_build:
- env.Append(CXXFLAGS=["-fno-exceptions"])
-
env.Append(
CCFLAGS=(
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index c0c57cafbe..d270980d72 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -110,6 +110,10 @@
<member name="package/show_in_android_tv" type="bool" setter="" getter="">
If [code]true[/code], this app will show in Android TV launcher UI.
</member>
+ <member name="package/show_in_app_library" type="bool" setter="" getter="">
+ If [code]true[/code], this app will show in the device's app library.
+ [b]Note:[/b] This is [code]true[/code] by default.
+ </member>
<member name="package/signed" type="bool" setter="" getter="">
If [code]true[/code], package signing is enabled.
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 9e46085b2a..21de46f4fc 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -47,6 +47,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
@@ -1838,6 +1839,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_app_library"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
@@ -2225,17 +2227,15 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
}
bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
-#ifdef MODULE_MONO_ENABLED
- // Don't check for additional errors, as this particular error cannot be resolved.
- r_error += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n";
- r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
- return false;
-#else
-
String err;
bool valid = false;
const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
+#ifdef MODULE_MONO_ENABLED
+ // Android export is still a work in progress, keep a message as a warning.
+ err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n";
+#endif
+
// Look for export templates (first official, and if defined custom templates).
if (!gradle_build_enabled) {
@@ -2365,7 +2365,6 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
return valid;
-#endif // !MODULE_MONO_ENABLED
}
bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
@@ -2386,10 +2385,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
}
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index d0d0c34bb4..0915009235 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -269,7 +269,12 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
manifest_activity_text += " <intent-filter>\n"
" <action android:name=\"android.intent.action.MAIN\" />\n"
- " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ " <category android:name=\"android.intent.category.DEFAULT\" />\n";
+
+ bool show_in_app_library = p_preset->get("package/show_in_app_library");
+ if (show_in_app_library) {
+ manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ }
bool uses_leanback_category = p_preset->get("package/show_in_android_tv");
if (uses_leanback_category) {
@@ -279,7 +284,6 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
bool uses_home_category = p_preset->get("package/show_as_launcher_app");
if (uses_home_category) {
manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n";
- manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n";
}
manifest_activity_text += " </intent-filter>\n";
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 7ff5c4d117..3aa4ca98fc 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -76,8 +76,13 @@ public:
virtual bool file_exists(const String &p_path) override; // return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 739c8a0925..0c3f8d7259 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -91,8 +91,13 @@ public:
static void setup(jobject p_file_access_handler);
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 542ab51660..56d403a263 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index c7b2c8ad67..cb89d6e1b0 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
index 8449c08b88..56397bb2c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -867,7 +867,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*/
public interface EGLConfigChooser {
/**
- * Choose a configuration from the list. Implementors typically
+ * Choose a configuration from the list. Implementers typically
* implement this method by calling
* {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
* EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 185d03fe39..1a25be0460 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -306,7 +306,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return;
}
- // Assign first available number. Re-use numbers where possible.
+ // Assign first available number. Reuse numbers where possible.
final int id = assignJoystickIdNumber(deviceId);
final Joystick joystick = new Joystick();
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index a01f4b55db..e11c0b7d91 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -30,7 +30,6 @@ def get_opts():
),
("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False),
- BoolVariable("ios_exceptions", "Enable exceptions", False),
("ios_triple", "Triple for ios toolchain", ""),
]
@@ -138,11 +137,6 @@ def configure(env: "Environment"):
env.Append(ASFLAGS=["-arch", "arm64"])
env.Append(CPPDEFINES=["NEED_LONG_INT"])
- if env["ios_exceptions"]:
- env.Append(CCFLAGS=["-fexceptions"])
- else:
- env.Append(CCFLAGS=["-fno-exceptions"])
-
# Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation
env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 3d63b7bb55..b6320fb22b 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -1984,10 +1985,8 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
}
}
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
if (!err.is_empty()) {
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index c3cb0f411d..91c14e0e91 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -32,6 +32,7 @@
#ifdef DBUS_ENABLED
+#include "core/crypto/crypto_core.h"
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
@@ -43,12 +44,15 @@
#include <dbus/dbus.h>
#endif
+#include <unistd.h>
+
#define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
#define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
#define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
+#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
-static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
+bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
DBusMessageIter iter[3];
dbus_message_iter_init(p_reply_message, &iter[0]);
@@ -80,11 +84,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- unsupported = true;
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error opening D-Bus connection: " + error.message);
+ ERR_PRINT(vformat("Error opening D-Bus connection: %s", error.message));
}
+ dbus_error_free(&error);
+ unsupported = true;
return false;
}
@@ -100,11 +104,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- dbus_connection_unref(bus);
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error on D-Bus communication: " + error.message);
+ ERR_PRINT(vformat("Error on D-Bus communication: %s", error.message));
}
+ dbus_error_free(&error);
+ dbus_connection_unref(bus);
return false;
}
@@ -126,6 +130,317 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
return value;
}
+static const char *cs_empty = "";
+
+void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) {
+ CharString cs = p_string.utf8();
+ const char *cs_ptr = cs.ptr();
+ if (cs_ptr) {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr);
+ } else {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty);
+ }
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ DBusMessageIter arr_iter;
+ const char *filters_key = "filters";
+
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() == 2) {
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter array_struct_iter;
+ dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
+ append_dbus_string(&struct_iter, tokens[0]);
+
+ dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
+ {
+ const unsigned nil = 0;
+ dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
+ }
+ append_dbus_string(&array_struct_iter, tokens[1]);
+ dbus_message_iter_close_container(&array_iter, &array_struct_iter);
+ dbus_message_iter_close_container(&struct_iter, &array_iter);
+ dbus_message_iter_close_container(&arr_iter, &struct_iter);
+ }
+ }
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ if (p_as_byte_array) {
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter);
+ CharString cs = p_value.utf8();
+ const char *cs_ptr = cs.get_data();
+ do {
+ dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr);
+ } while (*cs_ptr++);
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ } else {
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter);
+ append_dbus_string(&var_iter, p_value);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter);
+ {
+ int val = p_value;
+ dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) {
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
+
+ dbus_uint32_t resp_code;
+ dbus_message_iter_get_basic(p_iter, &resp_code);
+ if (resp_code != 0) {
+ r_cancel = true;
+ } else {
+ r_cancel = false;
+ ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
+
+ DBusMessageIter dict_iter;
+ dbus_message_iter_recurse(p_iter, &dict_iter);
+ while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter iter;
+ dbus_message_iter_recurse(&dict_iter, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+ const char *key;
+ dbus_message_iter_get_basic(&iter, &key);
+ dbus_message_iter_next(&iter);
+
+ DBusMessageIter var_iter;
+ dbus_message_iter_recurse(&iter, &var_iter);
+ if (strcmp(key, "uris") == 0) {
+ if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter uri_iter;
+ dbus_message_iter_recurse(&var_iter, &uri_iter);
+ while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) {
+ const char *value;
+ dbus_message_iter_get_basic(&uri_iter, &value);
+ r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_decode());
+ if (!dbus_message_iter_next(&uri_iter)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!dbus_message_iter_next(&dict_iter)) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ if (unsupported) {
+ return FAILED;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ // Open connection and add signal handler.
+ FileDialogData fd;
+ fd.callback = p_callback;
+
+ CryptoCore::RandomGenerator rng;
+ ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
+ uint8_t uuid[64];
+ Error rng_err = rng.get_random_bytes(uuid, 64);
+ ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token.");
+
+ fd.connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to open DBus connection: %s", err.message));
+ dbus_error_free(&err);
+ unsupported = true;
+ return FAILED;
+ }
+
+ String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(fd.connection));
+ String token = String::hex_encode_buffer(uuid, 64);
+ String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token);
+
+ fd.path = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Generate FileChooser message.
+ const char *method = nullptr;
+ switch (p_mode) {
+ case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: {
+ method = "SaveFile";
+ } break;
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
+ method = "OpenFile";
+ } break;
+ }
+
+ DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
+ {
+ DBusMessageIter iter;
+ dbus_message_iter_init_append(message, &iter);
+
+ append_dbus_string(&iter, p_xid);
+ append_dbus_string(&iter, p_title);
+
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
+
+ append_dbus_dict_string(&arr_iter, "handle_token", token);
+ append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
+ append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
+ append_dbus_dict_filters(&arr_iter, p_filters);
+ append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
+ if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
+ append_dbus_dict_string(&arr_iter, "current_name", p_filename);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr_iter);
+ }
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(fd.connection, message, DBUS_TIMEOUT_INFINITE, &err);
+ dbus_message_unref(message);
+
+ if (!reply || dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to send DBus message: %s", err.message));
+ dbus_error_free(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Update signal path.
+ {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(reply, &iter)) {
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
+ const char *new_path = nullptr;
+ dbus_message_iter_get_basic(&iter, &new_path);
+ if (String::utf8(new_path) != path) {
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to remove DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ fd.path = String::utf8(new_path);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ dbus_message_unref(reply);
+
+ MutexLock lock(file_dialog_mutex);
+ file_dialogs.push_back(fd);
+
+ return OK;
+}
+
+void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
+ FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
+
+ while (!portal->file_dialog_thread_abort.is_set()) {
+ {
+ MutexLock lock(portal->file_dialog_mutex);
+ for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) {
+ bool remove = false;
+ {
+ FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
+ if (fd.connection) {
+ while (true) {
+ DBusMessage *msg = dbus_connection_pop_message(fd.connection);
+ if (!msg) {
+ break;
+ } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(msg, &iter)) {
+ bool cancel = false;
+ Vector<String> uris;
+ file_chooser_parse_response(&iter, cancel, uris);
+
+ if (fd.callback.is_valid()) {
+ Variant v_status = !cancel;
+ Variant v_files = uris;
+ Variant *v_args[2] = { &v_status, &v_files };
+ fd.callback.call_deferredp((const Variant **)&v_args, 2);
+ }
+ }
+ dbus_message_unref(msg);
+
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ remove = true;
+ break;
+ }
+ dbus_message_unref(msg);
+ }
+ dbus_connection_read_write(fd.connection, 0);
+ }
+ }
+ if (remove) {
+ portal->file_dialogs.remove_at(i);
+ }
+ }
+ }
+ usleep(50000);
+ }
+}
+
FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
@@ -153,6 +468,27 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
print_verbose("PortalDesktop: Unsupported DBus library version!");
unsupported = true;
}
+
+ if (!unsupported) {
+ file_dialog_thread_abort.clear();
+ file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this);
+ }
+}
+
+FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
+ file_dialog_thread_abort.set();
+ if (file_dialog_thread.is_started()) {
+ file_dialog_thread.wait_to_finish();
+ }
+ for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
+ if (fd.connection) {
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ }
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index 1520ab3a1f..a9b83b3844 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -33,20 +33,48 @@
#ifdef DBUS_ENABLED
-#include <stdint.h>
+#include "core/os/thread.h"
+#include "servers/display_server.h"
+
+struct DBusMessage;
+struct DBusConnection;
+struct DBusMessageIter;
class FreeDesktopPortalDesktop {
private:
bool unsupported = false;
+ static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value);
// Read a setting from org.freekdesktop.portal.Settings
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
+ static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
+ static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters);
+ static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
+ static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
+ static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls);
+
+ struct FileDialogData {
+ DBusConnection *connection = nullptr;
+ Callable callback;
+ String path;
+ };
+
+ Mutex file_dialog_mutex;
+ Vector<FileDialogData> file_dialogs;
+ Thread file_dialog_thread;
+ SafeFlag file_dialog_thread_abort;
+
+ static void _thread_file_dialog_monitor(void *p_ud);
+
public:
FreeDesktopPortalDesktop();
+ ~FreeDesktopPortalDesktop();
bool is_supported() { return !unsupported; }
+ Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
+
// Retrieve the system's preferred color scheme.
// 0: No preference or unknown.
// 1: Prefer dark appearance.
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5d636240b7..827c567785 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -82,31 +82,9 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
-#ifdef UDEV_ENABLED
-// This function is derived from SDL:
-// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
-static bool detect_sandbox() {
- if (access("/.flatpak-info", F_OK) == 0) {
- return true;
- }
-
- // For Snap, we check multiple variables because they might be set for
- // unrelated reasons. This is the same thing WebKitGTK does.
- if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
- return true;
- }
-
- if (access("/run/host/container-manager", F_OK) == 0) {
- return true;
- }
-
- return false;
-}
-#endif // UDEV_ENABLED
-
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
- if (detect_sandbox()) {
+ if (OS::get_singleton()->is_sandboxed()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 14d02a73c8..d22d398a67 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -164,6 +164,27 @@ String OS_LinuxBSD::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string."));
}
+bool OS_LinuxBSD::is_sandboxed() const {
+ // This function is derived from SDL:
+ // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (has_environment("SNAP") && has_environment("SNAP_NAME") && has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
void OS_LinuxBSD::finalize() {
if (main_loop) {
memdelete(main_loop);
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 007b90b82b..6917ea5ae7 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -123,6 +123,8 @@ public:
virtual String get_unique_id() const override;
virtual String get_processor_name() const override;
+ virtual bool is_sandboxed() const override;
+
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 2643cd3b1a..f38a9dd278 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -122,6 +122,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_HIDPI:
case FEATURE_ICON:
+#ifdef DBUS_ENABLED
+ case FEATURE_NATIVE_DIALOG:
+#endif
//case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
@@ -360,6 +363,17 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
+Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ WindowID window_id = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
+ return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
+}
+
#endif
void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
@@ -379,7 +393,11 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
if (show_cursor && !previously_shown) {
WindowID window_id = get_window_at_screen_position(mouse_get_position());
- if (window_id != INVALID_WINDOW_ID) {
+ if (window_id != INVALID_WINDOW_ID && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
}
@@ -744,7 +762,7 @@ int DisplayServerX11::get_screen_count() const {
// Using Xinerama Extension
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
XFree(xsi);
} else {
@@ -756,7 +774,7 @@ int DisplayServerX11::get_screen_count() const {
int DisplayServerX11::get_primary_screen() const {
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
return 0;
} else {
return XDefaultScreen(x11_display);
@@ -809,7 +827,7 @@ Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
// Using Xinerama Extension.
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
@@ -1244,7 +1262,7 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
XImage *image = nullptr;
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int xin_count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count);
if (p_screen < xin_count) {
@@ -1449,6 +1467,11 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+ if (window_mouseover_id == p_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
+ _send_window_event(windows[p_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+
window_set_rect_changed_callback(Callable(), p_id);
window_set_window_event_callback(Callable(), p_id);
window_set_input_event_callback(Callable(), p_id);
@@ -1586,6 +1609,7 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_COND(!xshaped_ext_ok);
const Vector<Vector2> region_path = windows[p_window].mpath;
@@ -2102,9 +2126,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
const WindowData &wd = windows[p_window];
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
- if (property == None) {
+ // Using EWMH instead of ICCCM, might work better for Wayland users.
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", True);
+ Atom hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", True);
+ if (property == None || hidden == None) {
return false;
}
@@ -2112,7 +2137,7 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = nullptr;
+ Atom *atoms = nullptr;
int result = XGetWindowProperty(
x11_display,
@@ -2121,20 +2146,21 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
0,
32,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
- &data);
+ (unsigned char **)&atoms);
- if (result == Success && data) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState) {
- XFree(data);
- return true;
+ if (result == Success && atoms) {
+ for (unsigned int i = 0; i < len; i++) {
+ if (atoms[i] == hidden) {
+ XFree(atoms);
+ return true;
+ }
}
- XFree(data);
+ XFree(atoms);
}
return false;
@@ -3957,7 +3983,7 @@ bool DisplayServerX11::mouse_process_popups() {
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
@@ -4291,7 +4317,8 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id == window_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
@@ -4303,7 +4330,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
@@ -5461,13 +5492,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
if (initialize_xinerama(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xinerama dynamically.");
+ xinerama_ext_ok = false;
}
if (initialize_xrandr(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xrandr dynamically.");
+ xrandr_ext_ok = false;
}
if (initialize_xrender(dylibloader_verbose) != 0) {
@@ -5537,42 +5566,36 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
- {
+ if (xshaped_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XShapeQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xshape %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xshape library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xshaped_ext_ok = false;
+ print_verbose("Unsupported Xshape library version.");
}
}
- {
+ if (xinerama_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XineramaQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xinerama %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xinerama library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xinerama_ext_ok = false;
+ print_verbose("Unsupported Xinerama library version.");
}
}
- {
+ if (xrandr_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XRRQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xrandr %d.%d detected.", version_major, version_minor));
if (rc != 1 || (version_major == 1 && version_minor < 3) || (version_major < 1)) {
- ERR_PRINT("Unsupported Xrandr library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xrandr_ext_ok = false;
+ print_verbose("Unsupported Xrandr library version.");
}
}
@@ -5638,7 +5661,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
if (!xrandr_handle) {
fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
}
- } else {
+ }
+
+ if (xrandr_handle) {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 70703d42c3..71beddce76 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -225,6 +225,7 @@ class DisplayServerX11 : public DisplayServer {
List<WindowID> popup_list;
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
@@ -322,7 +323,9 @@ class DisplayServerX11 : public DisplayServer {
xrr_get_monitors_t xrr_get_monitors = nullptr;
xrr_free_monitors_t xrr_free_monitors = nullptr;
void *xrandr_handle = nullptr;
- Bool xrandr_ext_ok;
+ bool xrandr_ext_ok = true;
+ bool xinerama_ext_ok = true;
+ bool xshaped_ext_ok = true;
struct Property {
unsigned char *data;
@@ -390,6 +393,8 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
+
+ virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
#endif
virtual void mouse_set_mode(MouseMode p_mode) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 59e20fec45..d3a25506a8 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -74,17 +74,17 @@ private:
};
struct GLDisplay {
- GLDisplay() { context = nullptr; }
+ GLDisplay() {}
~GLDisplay();
GLManager_X11_Private *context = nullptr;
- ::Display *x11_display;
- XVisualInfo x_vi;
+ ::Display *x11_display = nullptr;
+ XVisualInfo x_vi = {};
};
// just for convenience, window and display struct
struct XWinDisp {
::Window x11_window;
- ::Display *x11_display;
+ ::Display *x11_display = nullptr;
} _x_windisp;
LocalVector<GLWindow> _windows;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index e5e0e53bfb..69f6008043 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -171,6 +171,7 @@ private:
int current_layout = 0;
bool keyboard_layout_dirty = true;
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
float display_max_scale = 1.f;
@@ -240,6 +241,8 @@ public:
bool get_is_resizing() const;
void reparent_check(WindowID p_window);
WindowID _get_focused_window_or_popup() const;
+ void mouse_enter_window(WindowID p_window);
+ void mouse_exit_window(WindowID p_window);
void window_update(WindowID p_window);
void window_destroy(WindowID p_window);
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index d64bb5211e..e79d6acc3f 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -366,6 +366,25 @@ DisplayServer::WindowID DisplayServerMacOS::_get_focused_window_or_popup() const
return last_focused_window;
}
+void DisplayServerMacOS::mouse_enter_window(WindowID p_window) {
+ if (window_mouseover_id != p_window) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = p_window;
+ if (p_window != INVALID_WINDOW_ID) {
+ send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_ENTER);
+ }
+ }
+}
+
+void DisplayServerMacOS::mouse_exit_window(WindowID p_window) {
+ if (window_mouseover_id == p_window && p_window != INVALID_WINDOW_ID) {
+ send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = INVALID_WINDOW_ID;
+}
+
void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event) {
((DisplayServerMacOS *)(get_singleton()))->_dispatch_input_event(p_event);
}
@@ -2069,9 +2088,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
if (show_cursor && !previously_shown) {
window_id = get_window_at_screen_position(mouse_get_position());
- if (window_id != INVALID_WINDOW_ID) {
- send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
- }
+ mouse_enter_window(window_id);
}
if (p_mode == MOUSE_MODE_CAPTURED) {
@@ -4026,7 +4043,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) {
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 6e9c0a0fe7..c24ff4cb73 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -63,7 +63,7 @@
Array of the additional command line arguments passed to the code signing tool.
</member>
<member name="codesign/entitlements/address_book" type="bool" setter="" getter="">
- Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
+ Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
</member>
<member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter="">
Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url].
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index e788ff1eec..6586fe7f82 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
@@ -2124,16 +2125,12 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE
// Check the texture formats, which vary depending on the target architecture.
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
- const String bc_error = test_bc();
- if (!bc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
valid = false;
- err += bc_error;
}
} else if (architecture == "arm64") {
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
} else {
ERR_PRINT("Invalid architecture");
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 3467bf90a1..231be83a03 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -505,9 +505,8 @@
return;
}
- DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
- ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_EXIT);
+ ds->mouse_exit_window(window_id);
}
}
@@ -517,9 +516,8 @@
return;
}
- DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
- ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_ENTER);
+ ds->mouse_enter_window(window_id);
}
ds->cursor_update_shape();
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 1c6dbb1981..46355b4ae8 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -67,6 +67,7 @@
ds->window_set_transient(window_id, DisplayServerMacOS::INVALID_WINDOW_ID);
}
+ ds->mouse_exit_window(window_id);
ds->window_destroy(window_id);
}
diff --git a/platform/web/SCsub b/platform/web/SCsub
index e44e59bfb9..1af0642554 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -70,6 +70,9 @@ if env["dlink_enabled"]:
sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.)
sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi"
+ sys_env["CCFLAGS"].remove("-fvisibility=hidden")
+ sys_env["LINKFLAGS"].remove("-fvisibility=hidden")
+
# The main emscripten runtime, with exported standard libraries.
sys = sys_env.Program(build_targets, ["web_runtime.cpp"])
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 2685cbcd63..4015c8ff16 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -97,12 +97,9 @@ def configure(env: "Environment"):
if env["use_assertions"]:
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
- if env.editor_build:
- if env["initial_memory"] < 64:
- print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
- env["initial_memory"] = 64
- else:
- env.Append(CPPFLAGS=["-fno-exceptions"])
+ if env.editor_build and env["initial_memory"] < 64:
+ print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
+ env["initial_memory"] = 64
env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@@ -211,6 +208,8 @@ def configure(env: "Environment"):
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
+ env.Append(CCFLAGS=["-fvisibility=hidden"])
+ env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
# Reduce code size by generating less support code (e.g. skip NodeJS support).
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 38e2714d9f..993abd2cee 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -406,10 +407,8 @@ bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorEx
// Validate the project configuration.
if (p_preset->get("vram_texture_compression/for_mobile")) {
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
}
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 1993d66310..cc86c81096 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -88,7 +88,7 @@ const GodotAudio = {
GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
callback(GodotAudio.input);
} catch (e) {
- GodotRuntime.error('Failed creaating input.', e);
+ GodotRuntime.error('Failed creating input.', e);
}
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js
index 746f858923..c60e6899f2 100644
--- a/platform/web/js/libs/library_godot_display.js
+++ b/platform/web/js/libs/library_godot_display.js
@@ -289,11 +289,11 @@ const GodotDisplayScreen = {
const isFullscreen = GodotDisplayScreen.isFullscreen();
const wantsFullWindow = GodotConfig.canvas_resize_policy === 2;
const noResize = GodotConfig.canvas_resize_policy === 0;
- const wwidth = GodotDisplayScreen.desired_size[0];
- const wheight = GodotDisplayScreen.desired_size[1];
+ const dWidth = GodotDisplayScreen.desired_size[0];
+ const dHeight = GodotDisplayScreen.desired_size[1];
const canvas = GodotConfig.canvas;
- let width = wwidth;
- let height = wheight;
+ let width = dWidth;
+ let height = dHeight;
if (noResize) {
// Don't resize canvas, just update GL if needed.
if (canvas.width !== width || canvas.height !== height) {
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index bec1fd2cb6..9548939695 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -355,6 +355,9 @@ def configure_msvc(env, vcvars_msvc_config):
else:
env.AppendUnique(CCFLAGS=["/MD"])
+ # MSVC incremental linking is broken and _increases_ link time (GH-77968).
+ env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
+
if env["arch"] == "x86_32":
env["x86_libtheora_opt_vc"] = True
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b1dccdcefe..5863a75324 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -278,7 +278,12 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
pfd->SetFileTypes(filters.size(), filters.ptr());
pfd->SetFileTypeIndex(0);
- hr = pfd->Show(nullptr);
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ hr = pfd->Show(windows[window_id].hWnd);
if (SUCCEEDED(hr)) {
Vector<String> file_names;
@@ -2735,7 +2740,7 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 07c6a8d6e4..c4be1821bd 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -168,10 +168,10 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
if (p_preset->get("application/modify_resources")) {
- _rcedit_add_data(p_preset, p_path, true);
+ _rcedit_add_data(p_preset, p_path, false);
String wrapper_path = p_path.get_basename() + ".console.exe";
if (FileAccess::exists(wrapper_path)) {
- _rcedit_add_data(p_preset, wrapper_path, false);
+ _rcedit_add_data(p_preset, wrapper_path, true);
}
}
return OK;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index df93631ef0..7c94e38e14 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1714,7 +1714,7 @@ String OS_Windows::get_system_ca_certificates() {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance;
- CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
#ifdef WASAPI_ENABLED
AudioDriverManager::add_driver(&driver_wasapi);
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 2ea7a8d6ae..3aa2a71a2c 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -510,7 +510,7 @@ StringName Area2D::get_audio_bus_name() const {
return audio_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area2D::_validate_property(PropertyInfo &p_property) const {
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 343dd82b87..dac31058bd 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -116,10 +116,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
//check if any area is diverting sound into a bus
Ref<World2D> world_2d = get_world_2d();
- ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master"));
+ ERR_FAIL_COND_V(world_2d.is_null(), SceneStringNames::get_singleton()->Master);
PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
- ERR_FAIL_COND_V(space_state == nullptr, SNAME("Master"));
+ ERR_FAIL_COND_V(space_state == nullptr, SceneStringNames::get_singleton()->Master);
PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS];
PhysicsDirectSpaceState2D::PointParameters point_params;
@@ -316,7 +316,7 @@ StringName AudioStreamPlayer2D::get_bus() const {
return default_bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer2D::set_autoplay(bool p_enable) {
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 9b23fd3943..a4677bef36 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -32,6 +32,7 @@
#define AUDIO_STREAM_PLAYER_2D_H
#include "scene/2d/node_2d.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
#include "servers/audio_server.h"
@@ -66,7 +67,7 @@ private:
float volume_db = 0.0;
float pitch_scale = 1.0;
bool autoplay = false;
- StringName default_bus = SNAME("Master");
+ StringName default_bus = SceneStringNames::get_singleton()->Master;
int max_polyphony = 1;
void _set_playing(bool p_enable);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index e7003ab98b..78987738a5 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -33,20 +33,21 @@
#include "core/config/project_settings.h"
#include "scene/main/window.h"
+bool Camera2D::_is_editing_in_editor() const {
+#ifdef TOOLS_ENABLED
+ return is_part_of_edited_scene();
+#else
+ return false;
+#endif // TOOLS_ENABLED
+}
+
void Camera2D::_update_scroll() {
- if (!is_inside_tree()) {
+ if (!is_inside_tree() || !viewport) {
return;
}
- if (Engine::get_singleton()->is_editor_hint()) {
+ if (_is_editing_in_editor()) {
queue_redraw();
- // Only set viewport transform when not bound to the main viewport.
- if (get_tree()->get_edited_scene_root() && get_viewport() == get_tree()->get_edited_scene_root()->get_viewport()) {
- return;
- }
- }
-
- if (!viewport) {
return;
}
@@ -65,7 +66,7 @@ void Camera2D::_update_scroll() {
}
void Camera2D::_update_process_callback() {
- if (Engine::get_singleton()->is_editor_hint()) {
+ if (_is_editing_in_editor()) {
set_process_internal(false);
set_physics_process_internal(false);
} else if (process_callback == CAMERA2D_PROCESS_IDLE) {
@@ -106,7 +107,7 @@ Transform2D Camera2D::get_camera_transform() {
if (!first) {
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
- if (drag_horizontal_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_horizontal_offset_changed) {
+ if (drag_horizontal_enabled && !_is_editing_in_editor() && !drag_horizontal_offset_changed) {
camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT]));
camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT]));
} else {
@@ -119,7 +120,7 @@ Transform2D Camera2D::get_camera_transform() {
drag_horizontal_offset_changed = false;
}
- if (drag_vertical_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_vertical_offset_changed) {
+ if (drag_vertical_enabled && !_is_editing_in_editor() && !drag_vertical_offset_changed) {
camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP]));
camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM]));
@@ -158,7 +159,7 @@ Transform2D Camera2D::get_camera_transform() {
}
}
- if (position_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) {
+ if (position_smoothing_enabled && !_is_editing_in_editor()) {
real_t c = position_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time());
smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;
ret_camera_pos = smoothed_camera_pos;
@@ -175,7 +176,7 @@ Transform2D Camera2D::get_camera_transform() {
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2());
if (!ignore_rotation) {
- if (rotation_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) {
+ if (rotation_smoothing_enabled && !_is_editing_in_editor()) {
real_t step = rotation_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time());
camera_angle = Math::lerp_angle(camera_angle, get_global_rotation(), step);
} else {
@@ -250,7 +251,7 @@ void Camera2D::_notification(int p_what) {
add_to_group(group_name);
add_to_group(canvas_group_name);
- if (!Engine::get_singleton()->is_editor_hint() && enabled && !viewport->get_camera_2d()) {
+ if (!_is_editing_in_editor() && enabled && !viewport->get_camera_2d()) {
make_current();
}
@@ -272,7 +273,7 @@ void Camera2D::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_DRAW: {
- if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint()) {
+ if (!is_inside_tree() || !_is_editing_in_editor()) {
break;
}
@@ -398,7 +399,11 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) {
void Camera2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- if (enabled && is_inside_tree() && !viewport->get_camera_2d()) {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (enabled && !viewport->get_camera_2d()) {
make_current();
} else if (!enabled && is_current()) {
clear_current();
@@ -414,27 +419,27 @@ Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
}
void Camera2D::_make_current(Object *p_which) {
- if (!viewport || (custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
+ if (!is_inside_tree() || !viewport) {
return;
}
+ if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) {
+ return;
+ }
+
+ queue_redraw();
+
if (p_which == this) {
- if (is_inside_tree()) {
- viewport->_camera_2d_set(this);
- queue_redraw();
- }
+ viewport->_camera_2d_set(this);
} else {
- if (is_inside_tree()) {
- if (viewport->get_camera_2d() == this) {
- viewport->_camera_2d_set(nullptr);
- }
- queue_redraw();
+ if (viewport->get_camera_2d() == this) {
+ viewport->_camera_2d_set(nullptr);
}
}
}
void Camera2D::_update_process_internal_for_smoothing() {
- bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint());
+ bool is_not_in_scene_or_editor = !(is_inside_tree() && _is_editing_in_editor());
bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0;
bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor;
@@ -453,13 +458,22 @@ void Camera2D::make_current() {
void Camera2D::clear_current() {
ERR_FAIL_COND(!is_current());
- if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) && viewport->is_inside_tree()) {
+
+ if (!viewport || !viewport->is_inside_tree()) {
+ return;
+ }
+
+ if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
viewport->assign_next_enabled_camera_2d(group_name);
}
}
bool Camera2D::is_current() const {
- if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
+ if (!viewport) {
+ return false;
+ }
+
+ if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
return viewport->get_camera_2d() == this;
}
return false;
@@ -567,8 +581,7 @@ Point2 Camera2D::get_camera_screen_center() const {
}
Size2 Camera2D::_get_camera_screen_size() const {
- // special case if the camera2D is in the root viewport
- if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) {
+ if (_is_editing_in_editor()) {
return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
}
return get_viewport_rect().size;
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index 808529b0fb..5693d05ee5 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -85,6 +85,7 @@ protected:
bool drag_vertical_offset_changed = false;
Point2 camera_screen_center;
+ bool _is_editing_in_editor() const;
void _update_process_callback();
void _update_scroll();
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 7dd2e75f09..2c5c6a1a16 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -30,32 +30,67 @@
#include "canvas_modulate.h"
+void CanvasModulate::_on_in_canvas_visibility_changed(bool p_new_visibility) {
+ RID canvas = get_canvas();
+ StringName group_name = "_canvas_modulate_" + itos(canvas.get_id());
+
+ ERR_FAIL_COND_MSG(p_new_visibility == is_in_group(group_name), vformat("CanvasModulate becoming %s in the canvas already %s in the modulate group. Buggy logic, please report.", p_new_visibility ? "visible" : "invisible", p_new_visibility ? "was" : "was not"));
+
+ if (p_new_visibility) {
+ bool has_active_canvas_modulate = get_tree()->has_group(group_name); // Group would be removed if empty; otherwise one CanvasModulate within must be active.
+ add_to_group(group_name);
+ if (!has_active_canvas_modulate) {
+ is_active = true;
+ RS::get_singleton()->canvas_set_modulate(canvas, color);
+ }
+ } else {
+ remove_from_group(group_name);
+ if (is_active) {
+ is_active = false;
+ CanvasModulate *new_active = Object::cast_to<CanvasModulate>(get_tree()->get_first_node_in_group(group_name));
+ if (new_active) {
+ new_active->is_active = true;
+ RS::get_singleton()->canvas_set_modulate(canvas, new_active->color);
+ } else {
+ RS::get_singleton()->canvas_set_modulate(canvas, Color(1, 1, 1, 1));
+ }
+ }
+ }
+
+ update_configuration_warnings();
+}
+
void CanvasModulate::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_CANVAS: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ is_in_canvas = true;
+ bool visible_in_tree = is_visible_in_tree();
+ if (visible_in_tree) {
+ _on_in_canvas_visibility_changed(true);
}
+ was_visible_in_tree = visible_in_tree;
} break;
case NOTIFICATION_EXIT_CANVAS: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ is_in_canvas = false;
+ if (was_visible_in_tree) {
+ _on_in_canvas_visibility_changed(false);
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- } else {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ if (!is_in_canvas) {
+ return;
}
- update_configuration_warnings();
+ bool visible_in_tree = is_visible_in_tree();
+ if (visible_in_tree == was_visible_in_tree) {
+ return;
+ }
+
+ _on_in_canvas_visibility_changed(visible_in_tree);
+
+ was_visible_in_tree = visible_in_tree;
} break;
}
}
@@ -69,7 +104,7 @@ void CanvasModulate::_bind_methods() {
void CanvasModulate::set_color(const Color &p_color) {
color = p_color;
- if (is_visible_in_tree()) {
+ if (is_active) {
RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
}
}
@@ -81,12 +116,12 @@ Color CanvasModulate::get_color() const {
PackedStringArray CanvasModulate::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
- if (is_visible_in_tree() && is_inside_tree()) {
+ if (is_in_canvas && is_visible_in_tree()) {
List<Node *> nodes;
get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes);
if (nodes.size() > 1) {
- warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
+ warnings.push_back(RTR("Only one visible CanvasModulate is allowed per canvas.\nWhen there are more than one, only one of them will be active. Which one is undefined."));
}
}
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index 3b11cf71f1..08ded52e23 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -38,6 +38,14 @@ class CanvasModulate : public Node2D {
Color color = Color(1, 1, 1, 1);
+ // CanvasModulate is in canvas-specific modulate group when both in canvas and visible in tree.
+ // Exactly one CanvasModulate in each such non-empty group is active.
+ bool is_in_canvas = false;
+ bool was_visible_in_tree = false; // Relevant only when in canvas.
+ bool is_active = false;
+
+ void _on_in_canvas_visibility_changed(bool p_new_visibility);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 1836cc20b6..09effe6596 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1839,6 +1839,17 @@ void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vec
ERR_FAIL_COND(!Q);
TileMapQuadrant &q = Q->value;
+ // Find node in scenes and remove it.
+ HashMap<Vector2i, String>::Iterator entry = q.scenes.find(pk);
+ if (entry != q.scenes.end()) {
+ String scene_name = entry->value;
+ Node *scene = tile_map_node->get_node_or_null(scene_name);
+ if (scene) {
+ scene->queue_free();
+ instantiated_scenes.erase(Vector2i(pk.x, pk.y));
+ }
+ }
+
q.cells.erase(pk);
// Remove or make the quadrant dirty.
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 29d151b726..beb6892435 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -573,7 +573,7 @@ StringName Area3D::get_audio_bus_name() const {
return audio_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area3D::set_use_reverb_bus(bool p_enable) {
@@ -594,7 +594,7 @@ StringName Area3D::get_reverb_bus_name() const {
return reverb_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area3D::set_reverb_amount(float p_amount) {
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 454c8c2960..86602d3192 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -33,6 +33,7 @@
#include "core/templates/vset.h"
#include "scene/3d/collision_object_3d.h"
+#include "scene/scene_string_names.h"
class Area3D : public CollisionObject3D {
GDCLASS(Area3D, CollisionObject3D);
@@ -134,10 +135,10 @@ private:
void _clear_monitoring();
bool audio_bus_override = false;
- StringName audio_bus = "Master";
+ StringName audio_bus = SceneStringNames::get_singleton()->Master;
bool use_reverb_bus = false;
- StringName reverb_bus = "Master";
+ StringName reverb_bus = SceneStringNames::get_singleton()->Master;
float reverb_amount = 0.0;
float reverb_uniformity = 0.0;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index ac626d0d2a..8147521a01 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -35,6 +35,7 @@
#include "scene/3d/audio_listener_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/main/viewport.h"
+#include "scene/scene_string_names.h"
// Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004)
// Speaker-Placement Correction Amplitude Panning (SPCAP)
@@ -635,7 +636,7 @@ StringName AudioStreamPlayer3D::get_bus() const {
return bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer3D::set_autoplay(bool p_enable) {
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index b4df06a83e..a666dca658 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -552,7 +552,7 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc
// Luckily, because we are using tetrahedrons, we can resort to
// less precise but still working ways to generate the separating plane
// this will most likely look bad when interpolating, but at least it will not crash.
- // and the arctifact will most likely also be very small, so too difficult to notice.
+ // and the artifact will most likely also be very small, so too difficult to notice.
//find the longest axis
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 8d66f7ebeb..e03640f6cb 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -179,10 +179,6 @@ void NavigationRegion3D::_notification(int p_what) {
}
void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navigation_mesh) {
- if (p_navigation_mesh == navigation_mesh) {
- return;
- }
-
if (navigation_mesh.is_valid()) {
navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
@@ -240,62 +236,29 @@ RID NavigationRegion3D::get_navigation_map() const {
return RID();
}
-struct BakeThreadsArgs {
- NavigationRegion3D *nav_region = nullptr;
- Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
-};
-
-void _bake_navigation_mesh(void *p_user_data) {
- BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
-
- if (args->nav_region->get_navigation_mesh().is_valid()) {
- Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh()->duplicate();
- Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
-
- NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
- if (!Thread::is_main_thread()) {
- args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
- } else {
- args->nav_region->_bake_finished(nav_mesh);
- }
- memdelete(args);
- } else {
- ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
- if (!Thread::is_main_thread()) {
- args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
- } else {
- args->nav_region->_bake_finished(Ref<NavigationMesh>());
- }
- memdelete(args);
- }
-}
-
void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
- ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
source_geometry_data.instantiate();
NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
- BakeThreadsArgs *args = memnew(BakeThreadsArgs);
- args->nav_region = this;
- args->source_geometry_data = source_geometry_data;
-
if (p_on_thread) {
- bake_thread.start(_bake_navigation_mesh, args);
+ NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
} else {
- _bake_navigation_mesh(args);
+ NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
}
}
-void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
- set_navigation_mesh(p_nav_mesh);
- if (bake_thread.is_started()) {
- bake_thread.wait_to_finish();
+void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) {
+ if (!Thread::is_main_thread()) {
+ call_deferred(SNAME("_bake_finished"), p_navigation_mesh);
+ return;
}
+
+ set_navigation_mesh(p_navigation_mesh);
emit_signal(SNAME("bake_finished"));
}
@@ -456,10 +419,6 @@ NavigationRegion3D::NavigationRegion3D() {
}
NavigationRegion3D::~NavigationRegion3D() {
- if (bake_thread.is_started()) {
- bake_thread.wait_to_finish();
- }
-
if (navigation_mesh.is_valid()) {
navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index e41d07f4cf..02fe5524b2 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -49,8 +49,6 @@ class NavigationRegion3D : public Node3D {
Transform3D current_global_transform;
- Thread bake_thread;
-
void _navigation_mesh_changed();
#ifdef DEBUG_ENABLED
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 715d8a5bc1..8da1ef8e1d 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -150,7 +150,7 @@ double AnimationNodeAnimation::_process(double p_time, bool p_seek, bool p_is_ex
// Emit start & finish signal. Internally, the detections are the same for backward.
// We should use call_deferred since the track keys are still being prosessed.
- if (state->tree) {
+ if (state->tree && !p_test_only) {
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
if (p_seek && !p_is_external_seeking && cur_time == 0) {
state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index b32b04655d..bf01898402 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -546,8 +546,9 @@ void PropertyTweener::start() {
return;
}
- if (do_continue) {
+ if (do_continue && Math::is_zero_approx(delay)) {
initial_val = target_instance->get_indexed(property);
+ do_continue = false;
}
if (relative) {
@@ -572,6 +573,10 @@ bool PropertyTweener::step(double &r_delta) {
if (elapsed_time < delay) {
r_delta = 0;
return true;
+ } else if (do_continue && !Math::is_zero_approx(delay)) {
+ initial_val = target_instance->get_indexed(property);
+ delta_val = Animation::subtract_variant(final_val, initial_val);
+ do_continue = false;
}
double time = MIN(elapsed_time - delay, duration);
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 6c37d6f81d..ae68bd719b 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -200,7 +200,7 @@ StringName AudioStreamPlayer::get_bus() const {
return bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer::set_autoplay(bool p_enable) {
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index d1f6fca2ee..c0c25aa983 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -33,6 +33,7 @@
#include "core/templates/safe_refcount.h"
#include "scene/main/node.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
class AudioStreamPlayer : public Node {
@@ -54,7 +55,7 @@ private:
float pitch_scale = 1.0;
float volume_db = 0.0;
bool autoplay = false;
- StringName bus = SNAME("Master");
+ StringName bus = SceneStringNames::get_singleton()->Master;
int max_polyphony = 1;
MixTarget mix_target = MIX_TARGET_STEREO;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 430569432a..738778b516 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -118,6 +118,7 @@ void Button::_notification(int p_what) {
Ref<StyleBox> style = theme_cache.normal;
bool rtl = is_layout_rtl();
+ const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING;
switch (get_draw_mode()) {
case DRAW_NORMAL: {
@@ -283,7 +284,7 @@ void Button::_notification(int p_what) {
Size2 _size = get_size() - style->get_offset() * 2;
int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
_size.width -= icon_text_separation + icon_ofs_region;
- if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
+ if (!is_clipped && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
@@ -335,9 +336,9 @@ void Button::_notification(int p_what) {
text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation;
}
- text_buf->set_width((clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? text_clip : -1);
+ text_buf->set_width(is_clipped ? text_clip : -1);
- int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
+ int text_width = MAX(1, is_clipped ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 68241337c9..443a639ff2 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -3128,6 +3128,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
}
String target_lower = option.display.to_lower();
+ int long_option = target_lower.size() > 50;
const char32_t *string_to_complete_char_lower = &string_to_complete_lower[0];
const char32_t *target_char_lower = &target_lower[0];
@@ -3142,27 +3143,34 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
for (int i = 1; *string_to_complete_char_lower && (all_possible_subsequence_matches.size() > 0); i++, string_to_complete_char_lower++) {
// find all occurrences of ssq_lower to avoid looking everywhere each time
Vector<int> all_ocurence;
- for (int j = i; j < target_lower.length(); j++) {
- if (target_lower[j] == *string_to_complete_char_lower) {
- all_ocurence.push_back(j);
+ if (long_option) {
+ all_ocurence.push_back(target_lower.find_char(*string_to_complete_char_lower));
+ } else {
+ for (int j = i; j < target_lower.length(); j++) {
+ if (target_lower[j] == *string_to_complete_char_lower) {
+ all_ocurence.push_back(j);
+ }
}
}
Vector<Vector<Pair<int, int>>> next_subsequence_matches;
- for (Vector<Pair<int, int>> &subsequence_matches : all_possible_subsequence_matches) {
- Pair<int, int> match_last_segment = subsequence_matches[subsequence_matches.size() - 1];
+ for (Vector<Pair<int, int>> &subsequence_match : all_possible_subsequence_matches) {
+ Pair<int, int> match_last_segment = subsequence_match[subsequence_match.size() - 1];
int next_index = match_last_segment.first + match_last_segment.second;
// get the last index from current sequence
// and look for next char starting from that index
if (target_lower[next_index] == *string_to_complete_char_lower) {
- Vector<Pair<int, int>> new_matches = subsequence_matches;
- new_matches.write[new_matches.size() - 1].second++;
- next_subsequence_matches.push_back(new_matches);
+ Vector<Pair<int, int>> new_match = subsequence_match;
+ new_match.write[new_match.size() - 1].second++;
+ next_subsequence_matches.push_back(new_match);
+ if (long_option) {
+ continue;
+ }
}
for (int index : all_ocurence) {
if (index > next_index) {
- Vector<Pair<int, int>> new_matches = subsequence_matches;
- new_matches.push_back({ index, 1 });
- next_subsequence_matches.push_back(new_matches);
+ Vector<Pair<int, int>> new_match = subsequence_match;
+ new_match.push_back({ index, 1 });
+ next_subsequence_matches.push_back(new_match);
}
}
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 2a0f85a1be..3c19766ca7 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -71,16 +71,14 @@ void ColorPicker::_notification(int p_what) {
for (int i = 0; i < SLIDER_COUNT; i++) {
labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
- set_offset((Side)i, get_offset((Side)i) + theme_cache.content_margin);
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
- set_offset((Side)0, get_offset((Side)0) + theme_cache.content_margin);
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
- mode_btns[i]->add_theme_style_override("pressed", theme_cache.mode_button_pressed);
- mode_btns[i]->add_theme_style_override("normal", theme_cache.mode_button_normal);
- mode_btns[i]->add_theme_style_override("hover", theme_cache.mode_button_hover);
+ mode_btns[i]->add_theme_style_override(SNAME("pressed"), theme_cache.mode_button_pressed);
+ mode_btns[i]->add_theme_style_override(SNAME("normal"), theme_cache.mode_button_normal);
+ mode_btns[i]->add_theme_style_override(SNAME("hover"), theme_cache.mode_button_hover);
}
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_RECTANGLE), theme_cache.shape_rect);
@@ -88,6 +86,11 @@ void ColorPicker::_notification(int p_what) {
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_VHS_CIRCLE), theme_cache.shape_circle);
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle);
+ internal_margin->add_theme_constant_override(SNAME("margin_bottom"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_left"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_right"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_top"), theme_cache.content_margin);
+
_reset_sliders_theme();
if (Engine::get_singleton()->is_editor_hint()) {
@@ -100,13 +103,6 @@ void ColorPicker::_notification(int p_what) {
_update_controls();
} break;
- case NOTIFICATION_VISIBILITY_CHANGED: {
- Popup *p = Object::cast_to<Popup>(get_parent());
- if (p && is_visible_in_tree()) {
- p->set_size(Size2(get_combined_minimum_size().width + theme_cache.content_margin * 2, get_combined_minimum_size().height + theme_cache.content_margin * 2));
- }
- } break;
-
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (picker_window != nullptr && picker_window->is_visible()) {
picker_window->hide();
@@ -1450,7 +1446,7 @@ void ColorPicker::_pick_button_pressed() {
picker_window = memnew(Popup);
picker_window->set_size(Vector2i(1, 1));
picker_window->connect("visibility_changed", callable_mp(this, &ColorPicker::_pick_finished));
- add_child(picker_window);
+ add_child(picker_window, false, INTERNAL_MODE_FRONT);
}
picker_window->popup();
}
@@ -1479,7 +1475,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_window = memnew(Popup);
picker_window->hide();
picker_window->set_transient(true);
- add_child(picker_window);
+ add_child(picker_window, false, INTERNAL_MODE_FRONT);
picker_texture_rect = memnew(TextureRect);
picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
@@ -1715,8 +1711,14 @@ void ColorPicker::_bind_methods() {
}
ColorPicker::ColorPicker() {
+ internal_margin = memnew(MarginContainer);
+ add_child(internal_margin, false, INTERNAL_MODE_FRONT);
+
+ VBoxContainer *real_vbox = memnew(VBoxContainer);
+ internal_margin->add_child(real_vbox);
+
HBoxContainer *hb_edit = memnew(HBoxContainer);
- add_child(hb_edit, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(hb_edit);
hb_edit->set_v_size_flags(SIZE_SHRINK_BEGIN);
uv_edit = memnew(Control);
@@ -1728,7 +1730,7 @@ ColorPicker::ColorPicker() {
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit));
sample_hbc = memnew(HBoxContainer);
- add_child(sample_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(sample_hbc);
btn_pick = memnew(Button);
sample_hbc->add_child(btn_pick);
@@ -1771,7 +1773,7 @@ ColorPicker::ColorPicker() {
add_mode(new ColorModeOKHSL(this));
mode_hbc = memnew(HBoxContainer);
- add_child(mode_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(mode_hbc);
mode_group.instantiate();
@@ -1806,11 +1808,11 @@ ColorPicker::ColorPicker() {
mode_popup->set_item_checked(MODE_MAX + 1, true);
mode_popup->connect("id_pressed", callable_mp(this, &ColorPicker::_set_mode_popup_value));
VBoxContainer *vbl = memnew(VBoxContainer);
- add_child(vbl, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(vbl);
VBoxContainer *vbr = memnew(VBoxContainer);
- add_child(vbr, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(vbr);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
slider_gc = memnew(GridContainer);
@@ -1900,9 +1902,9 @@ ColorPicker::ColorPicker() {
btn_preset->set_focus_mode(FOCUS_NONE);
btn_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
btn_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_preset, preset_container));
- add_child(btn_preset, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(btn_preset);
- add_child(preset_container, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(preset_container);
recent_preset_hbc = memnew(HBoxContainer);
recent_preset_hbc->set_v_size_flags(SIZE_SHRINK_BEGIN);
@@ -1917,9 +1919,9 @@ ColorPicker::ColorPicker() {
btn_recent_preset->set_focus_mode(FOCUS_NONE);
btn_recent_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
btn_recent_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_recent_preset, recent_preset_hbc));
- add_child(btn_recent_preset, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(btn_recent_preset);
- add_child(recent_preset_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(recent_preset_hbc);
set_pick_color(Color(1, 1, 1));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 1a4b1b6ee1..680b3f7d96 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -130,6 +130,7 @@ private:
Ref<StyleBoxFlat> picker_preview_style_box;
Color picker_color;
+ MarginContainer *internal_margin = nullptr;
Control *uv_edit = nullptr;
Control *w_edit = nullptr;
AspectRatioContainer *wheel_edit = nullptr;
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 4f94f0d9f2..6e75be268d 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -38,8 +38,7 @@
// AcceptDialog
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
- Ref<InputEventKey> key = p_event;
- if (close_on_escape && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) {
+ if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_cancel_pressed();
}
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 614e9b9695..d86cc4a5f9 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -227,7 +227,6 @@ private:
void _zoom_plus();
void _update_zoom_label();
- PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to);
void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom);
void _graph_node_selected(Node *p_gn);
@@ -243,8 +242,6 @@ private:
virtual void gui_input(const Ref<InputEvent> &p_ev) override;
void _top_layer_input(const Ref<InputEvent> &p_ev);
- bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
- bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
@@ -272,6 +269,9 @@ protected:
void _notification(int p_what);
+ virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+
GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2)
GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2)
GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2)
@@ -287,6 +287,8 @@ public:
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections();
void force_connection_drag_end();
+
+ virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to);
virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index b273f709f2..cff07c6e1c 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -675,11 +675,18 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) {
int i = closest;
+ if (items[i].disabled) {
+ // Don't emit any signal or do any action with clicked item when disabled.
+ return;
+ }
+
if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_or_control_pressed()) {
deselect(i);
emit_signal(SNAME("multi_selected"), i, false);
} else if (select_mode == SELECT_MULTI && mb->is_shift_pressed() && current >= 0 && current < items.size() && current != i) {
+ // Range selection.
+
int from = current;
int to = i;
if (i < current) {
@@ -687,6 +694,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
for (int j = from; j <= to; j++) {
if (!CAN_SELECT(j)) {
+ // Item is not selectable during a range selection, so skip it.
continue;
}
bool selected = !items[j].selected;
@@ -698,12 +706,17 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
} else {
- if (!mb->is_double_click() && !mb->is_command_or_control_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
+ if (!mb->is_double_click() &&
+ !mb->is_command_or_control_pressed() &&
+ select_mode == SELECT_MULTI &&
+ items[i].selectable &&
+ items[i].selected &&
+ mb->get_button_index() == MouseButton::LEFT) {
defer_select_single = i;
return;
}
- if (!items[i].selected || allow_reselect) {
+ if (items[i].selectable && (!items[i].selected || allow_reselect)) {
select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed());
if (select_mode == SELECT_SINGLE) {
@@ -722,7 +735,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
} else if (closest != -1) {
- emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ if (!items[closest].disabled) {
+ emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ }
} else {
// Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index());
@@ -886,7 +901,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = "";
} else if (p_event->is_action("ui_select", true) && select_mode == SELECT_MULTI) {
if (current >= 0 && current < items.size()) {
- if (items[current].selectable && !items[current].disabled && !items[current].selected) {
+ if (CAN_SELECT(current) && !items[current].selected) {
select(current, false);
emit_signal(SNAME("multi_selected"), current, true);
} else if (items[current].selected) {
@@ -897,7 +912,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
} else if (p_event->is_action("ui_accept", true)) {
search_string = ""; //any mousepress cancels
- if (current >= 0 && current < items.size()) {
+ if (current >= 0 && current < items.size() && !items[current].disabled) {
emit_signal(SNAME("item_activated"), current);
}
} else {
diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp
index 85068ac862..0dd258d92c 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -152,7 +152,7 @@ void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) {
return;
}
- if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) {
+ if (p_event->is_pressed() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) {
if (!get_parent() || !is_visible_in_tree()) {
return;
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 4e80d7a2d6..868383b141 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -40,7 +40,7 @@ void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) {
return;
}
- if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
+ if (p_event->is_pressed() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
accept_event();
return;
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 8138a66f64..a260385a46 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -30,10 +30,26 @@
#include "option_button.h"
+#include "core/os/keyboard.h"
#include "core/string/print_string.h"
static const int NONE_SELECTED = -1;
+void OptionButton::shortcut_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
+ if (disable_shortcuts) {
+ return;
+ }
+
+ if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
+ accept_event();
+ return;
+ }
+
+ Button::shortcut_input(p_event);
+}
+
Size2 OptionButton::get_minimum_size() const {
Size2 minsize;
if (fit_to_longest_item) {
@@ -78,6 +94,7 @@ void OptionButton::_update_theme_item_cache() {
void OptionButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
+ _refresh_size_cache();
if (has_theme_icon(SNAME("arrow"))) {
if (is_layout_rtl()) {
_set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width());
@@ -446,7 +463,7 @@ void OptionButton::_refresh_size_cache() {
cache_refresh_pending = false;
if (fit_to_longest_item) {
- _cached_size = Vector2();
+ _cached_size = theme_cache.normal->get_minimum_size();
for (int i = 0; i < get_item_count(); i++) {
_cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i)));
}
@@ -573,6 +590,7 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_fit_to_longest_item"), &OptionButton::is_fit_to_longest_item);
ClassDB::bind_method(D_METHOD("set_allow_reselect", "allow"), &OptionButton::set_allow_reselect);
ClassDB::bind_method(D_METHOD("get_allow_reselect"), &OptionButton::get_allow_reselect);
+ ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &OptionButton::set_disable_shortcuts);
// "selected" property must come after "item_count", otherwise GH-10213 occurs.
ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
@@ -583,9 +601,14 @@ void OptionButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index")));
}
+void OptionButton::set_disable_shortcuts(bool p_disabled) {
+ disable_shortcuts = p_disabled;
+}
+
OptionButton::OptionButton(const String &p_text) :
Button(p_text) {
set_toggle_mode(true);
+ set_process_shortcut_input(true);
set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
@@ -595,7 +618,6 @@ OptionButton::OptionButton(const String &p_text) :
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false));
- _refresh_size_cache();
}
OptionButton::~OptionButton() {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 7dcb3319c6..e29f14ad54 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -37,6 +37,7 @@
class OptionButton : public Button {
GDCLASS(OptionButton, Button);
+ bool disable_shortcuts = false;
PopupMenu *popup = nullptr;
int current = -1;
bool fit_to_longest_item = true;
@@ -79,6 +80,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
// ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes,
@@ -129,6 +131,8 @@ public:
PopupMenu *get_popup() const;
void show_popup();
+ void set_disable_shortcuts(bool p_disabled);
+
OptionButton(const String &p_text = String());
~OptionButton();
};
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index c0a2dc81d0..6915f3d242 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -35,8 +35,7 @@
#include "scene/gui/panel.h"
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
- Ref<InputEventKey> key = p_event;
- if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) {
+ if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_close_pressed();
}
}
diff --git a/scene/gui/popup_menu.compat.inc b/scene/gui/popup_menu.compat.inc
new file mode 100644
index 0000000000..ef74a17228
--- /dev/null
+++ b/scene/gui/popup_menu.compat.inc
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* popup_menu.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 PopupMenu::_add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+ return add_shortcut(p_shortcut, p_id, p_global, false);
+}
+
+void PopupMenu::_add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+ return add_icon_shortcut(p_icon, p_shortcut, p_id, p_global, false);
+}
+
+void PopupMenu::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::_add_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_compatibility_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::_add_icon_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false));
+}
+
+#endif
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 40db8deaac..4bba33f18e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "popup_menu.h"
+#include "popup_menu.compat.inc"
#include "core/config/project_settings.h"
#include "core/input/input.h"
@@ -1164,18 +1165,19 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
_menu_changed();
}
-#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \
+#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \
ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = atr(item.text); \
item.id = p_id == -1 ? items.size() : p_id; \
item.shortcut = p_shortcut; \
- item.shortcut_is_global = p_global;
+ item.shortcut_is_global = p_global; \
+ item.allow_echo = p_allow_echo;
-void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo);
items.push_back(item);
_shape_item(items.size() - 1);
@@ -1185,9 +1187,9 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
_menu_changed();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo);
item.icon = p_icon;
items.push_back(item);
@@ -1200,7 +1202,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); // Echo for check shortcuts doesn't make sense.
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
@@ -1213,7 +1215,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
@@ -1227,7 +1229,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
@@ -1240,7 +1242,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
@@ -1838,7 +1840,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
}
for (int i = 0; i < items.size(); i++) {
- if (is_item_disabled(i) || items[i].shortcut_is_disabled) {
+ if (is_item_disabled(i) || items[i].shortcut_is_disabled || (!items[i].allow_echo && p_event->is_echo())) {
continue;
}
@@ -2213,8 +2215,8 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 5ad9cd4303..ef754315f0 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -74,6 +74,7 @@ class PopupMenu : public Popup {
Ref<Shortcut> shortcut;
bool shortcut_is_global = false;
bool shortcut_is_disabled = false;
+ bool allow_echo = false;
// Returns (0,0) if icon is null.
Size2 get_icon_size() const {
@@ -199,6 +200,12 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void _add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ static void _bind_compatibility_methods();
+#endif
+
public:
// ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes,
// this value should be updated to reflect the new size.
@@ -215,8 +222,8 @@ public:
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, Key p_accel = Key::NONE);
- void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false);
+ void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false);
void add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 3a0fb6d89c..1e7e376fc8 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2745,6 +2745,9 @@ void RichTextLabel::_thread_function(void *p_userdata) {
void RichTextLabel::_thread_end() {
set_physics_process_internal(false);
+ if (!scroll_visible) {
+ vscroll->hide();
+ }
if (is_visible_in_tree()) {
queue_redraw();
}
@@ -2814,7 +2817,6 @@ _FORCE_INLINE_ float RichTextLabel::_update_scroll_exceeds(float p_total_height,
} else {
scroll_visible = false;
scroll_w = 0;
- vscroll->hide();
}
main->first_resized_line.store(0);
@@ -2862,6 +2864,9 @@ bool RichTextLabel::_validate_line_caches() {
if (main->first_resized_line.load() == (int)main->lines.size()) {
vscroll->set_value(old_scroll);
validating.store(false);
+ if (!scroll_visible) {
+ vscroll->hide();
+ }
return true;
}
@@ -2881,6 +2886,9 @@ bool RichTextLabel::_validate_line_caches() {
update_minimum_size();
}
validating.store(false);
+ if (!scroll_visible) {
+ vscroll->hide();
+ }
return true;
}
validating.store(false);
@@ -2896,6 +2904,9 @@ bool RichTextLabel::_validate_line_caches() {
updating.store(true);
_process_line_caches();
updating.store(false);
+ if (!scroll_visible) {
+ vscroll->hide();
+ }
queue_redraw();
return true;
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 46bf5641bc..0d0917a9d8 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -256,7 +256,7 @@ private:
ListType list_type = LIST_DOTS;
bool capitalize = false;
int level = 0;
- String bullet = String::utf8("•");
+ String bullet = U"•";
ItemList() { type = ITEM_LIST; }
};
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index fcf9302953..06b3882d25 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -428,6 +428,14 @@ void ScrollBar::_notification(int p_what) {
}
} break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ incr_active = false;
+ decr_active = false;
+ drag.active = false;
+ }
+ } break;
+
case NOTIFICATION_MOUSE_EXIT: {
highlight = HIGHLIGHT_NONE;
queue_redraw();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 433ae656ba..3c1be2d5fe 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1922,7 +1922,7 @@ void Tree::update_column(int p_col) {
columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
}
- columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.font, theme_cache.font_size, columns[p_col].language);
+ columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.tb_font, theme_cache.tb_font_size, columns[p_col].language);
columns.write[p_col].cached_minimum_width_dirty = true;
}
@@ -4108,7 +4108,7 @@ void Tree::update_scrollbars() {
}
int Tree::_get_title_button_height() const {
- ERR_FAIL_COND_V(theme_cache.font.is_null() || theme_cache.title_button.is_null(), 0);
+ ERR_FAIL_COND_V(theme_cache.tb_font.is_null() || theme_cache.title_button.is_null(), 0);
int h = 0;
if (show_column_titles) {
for (int i = 0; i < columns.size(); i++) {
@@ -4243,7 +4243,6 @@ void Tree::_notification(int p_what) {
int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT);
for (int i = 0; i < columns.size(); i++) {
Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button);
- Ref<Font> f = theme_cache.tb_font;
Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
if (cache.rtl) {
tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index f6e558fe57..ac09844128 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -451,7 +451,7 @@ StringName VideoStreamPlayer::get_bus() const {
return bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8d5133311a..3500b2201a 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1347,6 +1347,10 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
}
}
+Node::InternalMode Node::get_internal_mode() const {
+ return data.internal_mode;
+}
+
void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode) {
//add a child node quickly, without name validation
diff --git a/scene/main/node.h b/scene/main/node.h
index ed8f699d7b..feda200b24 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -386,6 +386,8 @@ public:
String get_description() const;
void set_name(const String &p_name);
+ InternalMode get_internal_mode() const;
+
void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
void remove_child(Node *p_child);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index db9c1efa68..d8c5e9c007 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1733,6 +1733,9 @@ SceneTree::SceneTree() {
const bool transparent_background = GLOBAL_DEF("rendering/viewport/transparent_background", false);
root->set_transparent_background(transparent_background);
+ const bool use_hdr_2d = GLOBAL_DEF_RST_BASIC("rendering/viewport/hdr_2d", false);
+ root->set_use_hdr_2d(use_hdr_2d);
+
const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), 0);
root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index c0e54e4e2a..41034466f9 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1085,7 +1085,7 @@ void Viewport::enable_canvas_transform_override(bool p_enable) {
}
}
-bool Viewport::is_canvas_transform_override_enbled() const {
+bool Viewport::is_canvas_transform_override_enabled() const {
ERR_READ_THREAD_GUARD_V(false);
return override_canvas_transform;
}
@@ -1177,6 +1177,17 @@ bool Viewport::has_transparent_background() const {
return transparent_bg;
}
+void Viewport::set_use_hdr_2d(bool p_enable) {
+ ERR_MAIN_THREAD_GUARD;
+ use_hdr_2d = p_enable;
+ RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable);
+}
+
+bool Viewport::is_using_hdr_2d() const {
+ ERR_READ_THREAD_GUARD_V(false);
+ return use_hdr_2d;
+}
+
void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
ERR_MAIN_THREAD_GUARD;
if (world_2d == p_world_2d) {
@@ -2184,6 +2195,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
return;
}
+ if (p_event->is_action_pressed("ui_cancel")) {
+ // Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
+ _gui_cancel_tooltip();
+ if (gui.tooltip_popup) {
+ // If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring.
+ // For instance, this prevents the node from being deselected when pressing Escape
+ // to hide a documentation tooltip in the inspector.
+ set_input_as_handled();
+ return;
+ }
+ }
+
if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
gui.key_focus->release_focus();
}
@@ -2416,6 +2439,7 @@ Window *Viewport::get_base_window() const {
return w;
}
+
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
if (get_base_window() == p_window) {
gui_release_focus();
@@ -2932,7 +2956,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
void Viewport::_update_mouse_over() {
// Update gui.mouse_over and gui.subwindow_over in all Viewports.
- // Send necessary mouse_enter/mouse_exit signals and the NOTIFICATION_VP_MOUSE_ENTER/NOTIFICATION_VP_MOUSE_EXIT notifications for every Viewport in the SceneTree.
+ // Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree.
if (is_attached_in_viewport()) {
// Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
@@ -2986,7 +3010,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
}
gui.subwindow_over = sw;
if (!sw->is_input_disabled()) {
- sw->notification(NOTIFICATION_VP_MOUSE_ENTER);
+ sw->_propagate_window_notification(sw, NOTIFICATION_WM_MOUSE_ENTER);
}
}
if (!sw->is_input_disabled()) {
@@ -3633,6 +3657,15 @@ bool Viewport::is_embedding_subwindows() const {
return gui.embed_subwindows_hint;
}
+TypedArray<Window> Viewport::get_embedded_subwindows() const {
+ TypedArray<Window> windows;
+ for (int i = 0; i < gui.sub_windows.size(); i++) {
+ windows.append(gui.sub_windows[i].window);
+ }
+
+ return windows;
+}
+
void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
int index = _sub_window_find(p_window);
ERR_FAIL_COND(index == -1);
@@ -4271,6 +4304,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect);
ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background);
ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background);
+ ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
+ ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
@@ -4359,6 +4394,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
+ ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows);
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
@@ -4435,6 +4471,8 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
+
#ifndef _3D_DISABLED
ADD_GROUP("Scaling 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 7cfad42119..346dc6af7e 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -243,6 +243,7 @@ private:
Rect2 last_vp_rect;
bool transparent_bg = false;
+ bool use_hdr_2d = false;
bool gen_mipmaps = false;
bool snap_controls_to_pixels = true;
@@ -467,7 +468,8 @@ private:
SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point);
void _update_mouse_over();
- void _update_mouse_over(Vector2 p_pos);
+ virtual void _update_mouse_over(Vector2 p_pos);
+ virtual void _mouse_leave_viewport();
virtual bool _can_consume_input_events() const { return true; }
uint64_t event_count = 0;
@@ -481,8 +483,6 @@ protected:
Size2i _get_size_2d_override() const;
bool _is_size_allocated() const;
- void _mouse_leave_viewport();
-
void _notification(int p_what);
void _process_picking();
static void _bind_methods();
@@ -507,7 +507,7 @@ public:
Ref<World2D> find_world_2d() const;
void enable_canvas_transform_override(bool p_enable);
- bool is_canvas_transform_override_enbled() const;
+ bool is_canvas_transform_override_enabled() const;
void set_canvas_transform_override(const Transform2D &p_transform);
Transform2D get_canvas_transform_override() const;
@@ -526,6 +526,9 @@ public:
void set_transparent_background(bool p_enable);
bool has_transparent_background() const;
+ void set_use_hdr_2d(bool p_enable);
+ bool is_using_hdr_2d() const;
+
Ref<ViewportTexture> get_texture() const;
void set_positional_shadow_atlas_size(int p_size);
@@ -650,6 +653,7 @@ public:
void set_embedding_subwindows(bool p_embed);
bool is_embedding_subwindows() const;
+ TypedArray<Window> get_embedded_subwindows() const;
void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect);
Rect2i subwindow_get_popup_safe_rect(Window *p_window) const;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index d0658c489d..1af279d94c 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -670,18 +670,29 @@ void Window::_propagate_window_notification(Node *p_node, int p_notification) {
void Window::_event_callback(DisplayServer::WindowEvent p_event) {
switch (p_event) {
case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: {
- _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
Window *root = get_tree()->get_root();
- DEV_ASSERT(!root->gui.windowmanager_window_over); // Entering a window while a window is hovered should never happen.
+ if (root->gui.windowmanager_window_over) {
+#ifdef DEV_ENABLED
+ WARN_PRINT_ONCE("Entering a window while a window is hovered should never happen in DisplayServer.");
+#endif // DEV_ENABLED
+ root->gui.windowmanager_window_over->_event_callback(DisplayServer::WINDOW_EVENT_MOUSE_EXIT);
+ }
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
root->gui.windowmanager_window_over = this;
- notification(NOTIFICATION_VP_MOUSE_ENTER);
+ mouse_in_window = true;
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape
}
} break;
case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: {
Window *root = get_tree()->get_root();
- DEV_ASSERT(root->gui.windowmanager_window_over); // Exiting a window, while no window is hovered should never happen.
+ if (!root->gui.windowmanager_window_over) {
+#ifdef DEV_ENABLED
+ WARN_PRINT_ONCE("Exiting a window while no window is hovered should never happen in DisplayServer.");
+#endif // DEV_ENABLED
+ return;
+ }
+ mouse_in_window = false;
root->gui.windowmanager_window_over->_mouse_leave_viewport();
root->gui.windowmanager_window_over = nullptr;
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
@@ -991,6 +1002,17 @@ void Window::_update_viewport_size() {
float font_oversampling = 1.0;
window_transform = Transform2D();
+ if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
+ // We always want to make sure that the content scale factor is a whole
+ // number, else there will be pixel wobble no matter what.
+ content_scale_factor = Math::floor(content_scale_factor);
+
+ // A content scale factor of zero is pretty useless.
+ if (content_scale_factor < 1) {
+ content_scale_factor = 1;
+ }
+ }
+
if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) {
font_oversampling = content_scale_factor;
final_size = size;
@@ -1044,13 +1066,26 @@ void Window::_update_viewport_size() {
screen_size = screen_size.floor();
viewport_size = viewport_size.floor();
+ if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
+ Size2i screen_scale = (screen_size / viewport_size).floor();
+ int scale_factor = MIN(screen_scale.x, screen_scale.y);
+
+ if (scale_factor < 1) {
+ scale_factor = 1;
+ }
+
+ screen_size = viewport_size * scale_factor;
+ }
+
Size2 margin;
Size2 offset;
- if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) {
+ if (screen_size.x < video_mode.x) {
margin.x = Math::round((video_mode.x - screen_size.x) / 2.0);
offset.x = Math::round(margin.x * viewport_size.y / screen_size.y);
- } else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) {
+ }
+
+ if (screen_size.y < video_mode.y) {
margin.y = Math::round((video_mode.y - screen_size.y) / 2.0);
offset.y = Math::round(margin.y * viewport_size.x / screen_size.x);
}
@@ -1327,6 +1362,15 @@ Window::ContentScaleAspect Window::get_content_scale_aspect() const {
return content_scale_aspect;
}
+void Window::set_content_scale_stretch(ContentScaleStretch p_stretch) {
+ content_scale_stretch = p_stretch;
+ _update_viewport_size();
+}
+
+Window::ContentScaleStretch Window::get_content_scale_stretch() const {
+ return content_scale_stretch;
+}
+
void Window::set_content_scale_factor(real_t p_factor) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_COND(p_factor <= 0);
@@ -2509,6 +2553,41 @@ bool Window::is_attached_in_viewport() const {
return get_embedder();
}
+void Window::_update_mouse_over(Vector2 p_pos) {
+ if (!mouse_in_window) {
+ if (is_embedded()) {
+ mouse_in_window = true;
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
+ } else {
+ // Prevent update based on delayed InputEvents from DisplayServer.
+ return;
+ }
+ }
+
+ bool new_in = get_visible_rect().has_point(p_pos);
+ if (new_in == gui.mouse_in_viewport) {
+ if (new_in) {
+ Viewport::_update_mouse_over(p_pos);
+ }
+ return;
+ }
+
+ if (new_in) {
+ notification(NOTIFICATION_VP_MOUSE_ENTER);
+ Viewport::_update_mouse_over(p_pos);
+ } else {
+ Viewport::_mouse_leave_viewport();
+ }
+}
+
+void Window::_mouse_leave_viewport() {
+ Viewport::_mouse_leave_viewport();
+ if (is_embedded()) {
+ mouse_in_window = false;
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
+ }
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
@@ -2583,6 +2662,9 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect);
ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect);
+ ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch);
+ ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch);
+
ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor);
ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor);
@@ -2701,7 +2783,8 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor"), "set_content_scale_factor", "get_content_scale_factor");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor");
ADD_GROUP("Localization", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
@@ -2753,6 +2836,9 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT);
BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND);
+ BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_FRACTIONAL);
+ BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER);
+
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
diff --git a/scene/main/window.h b/scene/main/window.h
index 18ddd89662..c387ffa92a 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -78,6 +78,11 @@ public:
CONTENT_SCALE_ASPECT_EXPAND,
};
+ enum ContentScaleStretch {
+ CONTENT_SCALE_STRETCH_FRACTIONAL,
+ CONTENT_SCALE_STRETCH_INTEGER,
+ };
+
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
@@ -135,6 +140,7 @@ private:
Size2i content_scale_size;
ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED;
ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE;
+ ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL;
real_t content_scale_factor = 1.0;
void _make_window();
@@ -197,6 +203,10 @@ private:
void _event_callback(DisplayServer::WindowEvent p_event);
virtual bool _can_consume_input_events() const override;
+ bool mouse_in_window = false;
+ void _update_mouse_over(Vector2 p_pos) override;
+ void _mouse_leave_viewport() override;
+
Ref<Shortcut> debugger_stop_shortcut;
protected:
@@ -299,6 +309,9 @@ public:
void set_content_scale_aspect(ContentScaleAspect p_aspect);
ContentScaleAspect get_content_scale_aspect() const;
+ void set_content_scale_stretch(ContentScaleStretch p_stretch);
+ ContentScaleStretch get_content_scale_stretch() const;
+
void set_content_scale_factor(real_t p_factor);
real_t get_content_scale_factor() const;
@@ -420,6 +433,7 @@ VARIANT_ENUM_CAST(Window::Mode);
VARIANT_ENUM_CAST(Window::Flags);
VARIANT_ENUM_CAST(Window::ContentScaleMode);
VARIANT_ENUM_CAST(Window::ContentScaleAspect);
+VARIANT_ENUM_CAST(Window::ContentScaleStretch);
VARIANT_ENUM_CAST(Window::LayoutDirection);
VARIANT_ENUM_CAST(Window::WindowInitialPosition);
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index b6a1737acb..eef46a6798 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -183,7 +183,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
- theme->set_constant("h_separation", "Button", Math::round(2 * scale));
+ theme->set_constant("h_separation", "Button", Math::round(4 * scale));
theme->set_constant("icon_max_width", "Button", 0);
// MenuBar
@@ -259,7 +259,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "OptionButton", Math::round(2 * scale));
+ theme->set_constant("h_separation", "OptionButton", Math::round(4 * scale));
theme->set_constant("arrow_margin", "OptionButton", Math::round(4 * scale));
theme->set_constant("outline_size", "OptionButton", 0);
theme->set_constant("modulate_arrow", "OptionButton", false);
@@ -282,7 +282,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "MenuButton", Math::round(3 * scale));
+ theme->set_constant("h_separation", "MenuButton", Math::round(4 * scale));
theme->set_constant("outline_size", "MenuButton", 0);
// CheckBox
@@ -762,6 +762,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
theme->set_font_size("font_size", "Tree", -1);
+ theme->set_font_size("title_button_font_size", "Tree", -1);
theme->set_color("title_button_color", "Tree", control_font_color);
theme->set_color("font_color", "Tree", control_font_low_color);
@@ -788,7 +789,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("children_hl_line_width", "Tree", 1);
theme->set_constant("parent_hl_line_margin", "Tree", 0);
theme->set_constant("draw_guides", "Tree", 1);
- theme->set_constant("scroll_border", "Tree", 4);
+ theme->set_constant("scroll_border", "Tree", Math::round(4 * scale));
theme->set_constant("scroll_speed", "Tree", 12);
theme->set_constant("outline_size", "Tree", 0);
theme->set_constant("icon_max_width", "Tree", 0);
@@ -803,9 +804,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("panel", "ItemList", make_flat_stylebox(style_normal_color));
theme->set_stylebox("focus", "ItemList", focus);
- theme->set_constant("h_separation", "ItemList", 4);
- theme->set_constant("v_separation", "ItemList", 2);
- theme->set_constant("icon_margin", "ItemList", 4);
+ theme->set_constant("h_separation", "ItemList", Math::round(4 * scale));
+ theme->set_constant("v_separation", "ItemList", Math::round(2 * scale));
+ theme->set_constant("icon_margin", "ItemList", Math::round(4 * scale));
theme->set_constant("line_separation", "ItemList", Math::round(2 * scale));
theme->set_font("font", "ItemList", Ref<Font>());
@@ -1005,7 +1006,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "ColorPickerButton", Math::round(2 * scale));
+ theme->set_constant("h_separation", "ColorPickerButton", Math::round(4 * scale));
theme->set_constant("outline_size", "ColorPickerButton", 0);
// ColorPresetButton
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index e48f744c72..abe1561310 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1446,7 +1446,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_emission_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_emission_energy", "get_volumetric_fog_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_volumetric_fog_length", "get_volumetric_fog_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_volumetric_fog_sky_affect", "get_volumetric_fog_sky_affect");
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 09a835db1b..df0bd8bc2e 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -97,6 +97,8 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &Font::get_supported_feature_list);
ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &Font::get_supported_variation_list);
ClassDB::bind_method(D_METHOD("get_face_count"), &Font::get_face_count);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks");
}
void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const {
@@ -1006,7 +1008,12 @@ void FontFile::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks");
+}
+
+void FontFile::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "fallbacks") {
+ p_property.usage &= ~PROPERTY_USAGE_EDITOR;
+ }
}
bool FontFile::_set(const StringName &p_name, const Variant &p_value) {
@@ -2634,7 +2641,6 @@ void FontVariation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks");
ADD_GROUP("Variation", "variation_");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype");
@@ -2924,7 +2930,6 @@ void SystemFont::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range"), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size"), "set_msdf_size", "get_msdf_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks");
}
void SystemFont::_update_rids() const {
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 5d600451a1..d8374c4447 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -210,6 +210,7 @@ class FontFile : public Font {
protected:
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 745c71626c..72d38ec8ce 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -683,11 +683,13 @@ void ParticleProcessMaterial::_update_shader() {
code += " pos.z = 0.0;\n";
}
code += " // apply linear acceleration\n";
- code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
+ code += " float linear_accel_rand = rand_from_seed(alt_seed);\n";
+ code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, linear_accel_rand) : vec3(0.0);\n";
code += " // apply radial acceleration\n";
code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
code += " vec3 diff = pos - org;\n";
- code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
+ code += " float radial_accel_rand = rand_from_seed(alt_seed);\n";
+ code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, radial_accel_rand) : vec3(0.0);\n";
code += " // apply tangential acceleration;\n";
code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed));\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 6430a1302d..a64ae07f05 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -2066,8 +2066,8 @@ void TorusMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1"), "set_rings", "get_rings");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_segments", "get_ring_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1,or_greater"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1,or_greater"), "set_ring_segments", "get_ring_segments");
}
void TorusMesh::set_inner_radius(const float p_inner_radius) {
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index f0971f1a34..cf8ece0d4c 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -193,6 +193,9 @@ SceneStringNames::SceneStringNames() {
path_pp = NodePath("..");
+ // Audio bus name.
+ Master = StaticCString::create("Master");
+
_default = StaticCString::create("default");
_window_group = StaticCString::create("_window_group");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index f31cf7b881..10b71e2a2a 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -204,6 +204,8 @@ public:
StringName output;
+ StringName Master;
+
StringName parameters_base_path;
StringName _window_group;
diff --git a/scu_builders.py b/scu_builders.py
index 5f7821655b..e76a58bd88 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -8,8 +8,8 @@ from os.path import normpath, basename
base_folder_path = str(Path(__file__).parent) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
_verbose = False
-_is_release_build = False
_scu_folders = set()
+_max_includes_per_scu = 1024
def clear_out_existing_files(output_folder, extension):
@@ -197,13 +197,14 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
# adjust number of output files according to whether DEV or release
num_output_files = 1
- if _is_release_build:
- # always have a maximum in release
- includes_per_scu = 8
- num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
+
+ if includes_per_scu == 0:
+ includes_per_scu = _max_includes_per_scu
else:
- if includes_per_scu > 0:
- num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
+ if includes_per_scu > _max_includes_per_scu:
+ includes_per_scu = _max_includes_per_scu
+
+ num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
lines_per_file = math.ceil(total_lines / float(num_output_files))
lines_per_file = max(lines_per_file, 1)
@@ -241,15 +242,15 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
)
-def generate_scu_files(verbose, is_release_build):
+def generate_scu_files(verbose, max_includes_per_scu):
print("=============================")
print("Single Compilation Unit Build")
print("=============================")
- print("Generating SCU build files")
global _verbose
_verbose = verbose
- global _is_release_build
- _is_release_build = is_release_build
+ global _max_includes_per_scu
+ _max_includes_per_scu = max_includes_per_scu
+ print("Generating SCU build files... (max includes per scu " + str(_max_includes_per_scu) + ")")
curr_folder = os.path.abspath("./")
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 02a7651631..983ca8892d 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -223,8 +223,8 @@ private:
HashSet<AudioStreamPlaybackRandomizer *> playbacks;
Vector<PoolEntry> audio_stream_pool;
- float random_pitch_scale = 1.1f;
- float random_volume_offset_db = 5.0f;
+ float random_pitch_scale = 1.0f;
+ float random_volume_offset_db = 0.0f;
Ref<AudioStreamPlayback> instance_playback_random();
Ref<AudioStreamPlayback> instance_playback_no_repeats();
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 49991e41d3..2308fcca9e 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -40,6 +40,7 @@
#include "core/string/string_name.h"
#include "core/templates/pair.h"
#include "scene/resources/audio_stream_wav.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio/effects/audio_effect_compressor.h"
@@ -747,7 +748,7 @@ void AudioServer::set_bus_count(int p_count) {
buses[i]->bypass = false;
buses[i]->volume_db = 0;
if (i > 0) {
- buses[i]->send = "Master";
+ buses[i]->send = SceneStringNames::get_singleton()->Master;
}
bus_map[attempt] = buses[i];
@@ -1582,7 +1583,7 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) {
for (int i = 0; i < p_bus_layout->buses.size(); i++) {
Bus *bus = memnew(Bus);
if (i == 0) {
- bus->name = "Master";
+ bus->name = SceneStringNames::get_singleton()->Master;
} else {
bus->name = p_bus_layout->buses[i].name;
bus->send = p_bus_layout->buses[i].send;
@@ -1891,5 +1892,5 @@ void AudioBusLayout::_get_property_list(List<PropertyInfo> *p_list) const {
AudioBusLayout::AudioBusLayout() {
buses.resize(1);
- buses.write[0].name = "Master";
+ buses.write[0].name = SceneStringNames::get_singleton()->Master;
}
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 04facdb8d9..75036b935b 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -155,6 +155,7 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
+ ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
@@ -204,6 +205,9 @@ NavigationServer3D::NavigationServer3D() {
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
+ GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
+ GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
+
#ifdef DEBUG_ENABLED
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 391730e18f..39f147357a 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -34,7 +34,7 @@
#include "core/object/class_db.h"
#include "core/templates/rid.h"
-#include "scene/3d/navigation_region_3d.h"
+#include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation/navigation_path_query_parameters_3d.h"
#include "servers/navigation/navigation_path_query_result_3d.h"
@@ -301,14 +301,17 @@ public:
/// so this must be called in the main thread.
/// Note: This function is not thread safe.
virtual void process(real_t delta_time) = 0;
+ virtual void init() = 0;
+ virtual void finish() = 0;
/// Returns a customized navigation path using a query parameters object
virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
- virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
- virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+ virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
+ virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+ virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
NavigationServer3D();
~NavigationServer3D() override;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index b2d452f67a..b1ec214bb0 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -145,11 +145,14 @@ public:
void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
- void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
- void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+ void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
+ void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+ void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void free(RID p_object) override {}
void set_active(bool p_active) override {}
void process(real_t delta_time) override {}
+ void init() override {}
+ void finish() override {}
NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); }
int get_process_info(ProcessInfo p_info) const override { return 0; }
void set_debug_enabled(bool p_enabled) {}
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 71a1801de9..d9bde5903c 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -177,6 +177,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) override {}
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override {}
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override { return RS::VIEWPORT_MSAA_DISABLED; }
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {}
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; }
virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {}
virtual bool render_target_is_clear_requested(RID p_render_target) override { return false; }
diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
index 971f8b7f6f..cc5031823e 100644
--- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
+++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
@@ -359,11 +359,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -393,11 +392,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
// Pass 2
@@ -418,11 +416,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
if (bokeh.push_constant.half_size) {
@@ -438,11 +435,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -471,11 +467,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
if (bokeh.push_constant.half_size) {
@@ -491,11 +486,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
} else {
CopyEffects::get_singleton()->copy_raster(p_buffers.secondary_texture, p_buffers.base_fb);
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index eba1c145e3..834653f5c2 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -696,11 +696,9 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
-
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -803,12 +801,11 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure }));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_auto_exposure), 1);
}
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
blur_mode = BLUR_MODE_GAUSSIAN_GLOW;
@@ -820,12 +817,11 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
blur_raster.push_constant.flags = base_flags;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -1055,11 +1051,10 @@ void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_fra
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -1136,11 +1131,10 @@ void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebu
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CubemapFilterRasterPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -1214,11 +1208,10 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -1269,8 +1262,7 @@ void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_b
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_reflection), 1);
}
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
RD::get_singleton()->draw_command_end_label();
diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp
index 7462282932..3aa5f5706e 100644
--- a/servers/rendering/renderer_rd/effects/luminance.cpp
+++ b/servers/rendering/renderer_rd/effects/luminance.cpp
@@ -191,11 +191,10 @@ void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_
RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1);
}
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
} else {
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp
index b9346c0201..d123f24865 100644
--- a/servers/rendering/renderer_rd/effects/ss_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp
@@ -1429,7 +1429,7 @@ void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffe
push_constant.camera_z_far = p_projections[v].get_z_far();
push_constant.camera_z_near = p_projections[v].get_z_near();
push_constant.orthogonal = p_projections[v].is_orthogonal();
- push_constant.filter = false; //enabling causes arctifacts
+ push_constant.filter = false; // Enabling causes artifacts.
push_constant.screen_size[0] = p_ssr_buffers.size.x;
push_constant.screen_size[1] = p_ssr_buffers.size.y;
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
index 821960bb3b..48c6511408 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -89,12 +89,12 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
- tonemap.push_constant.use_bcs = p_settings.use_bcs;
+ tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
- tonemap.push_constant.use_glow = p_settings.use_glow;
+ tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
tonemap.push_constant.glow_intensity = p_settings.glow_intensity;
tonemap.push_constant.glow_map_strength = p_settings.glow_map_strength;
tonemap.push_constant.glow_levels[0] = p_settings.glow_levels[0]; // clean this up to just pass by pointer or something
@@ -114,19 +114,21 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
- tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
+ tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
- tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
+ tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
- tonemap.push_constant.use_fxaa = p_settings.use_fxaa;
- tonemap.push_constant.use_debanding = p_settings.use_debanding;
+ tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
+ tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
+ tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
+
if (p_settings.view_count > 1) {
// Use MULTIVIEW versions
mode += 6;
@@ -170,10 +172,9 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
@@ -185,13 +186,13 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
- tonemap.push_constant.use_bcs = p_settings.use_bcs;
+ tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
- tonemap.push_constant.use_glow = p_settings.use_glow;
+ tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS;
if (p_settings.view_count > 1) {
@@ -200,16 +201,18 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
- tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
+ tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
- tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
+ tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
- tonemap.push_constant.use_debanding = p_settings.use_debanding;
+ tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
+ tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
+
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
@@ -250,8 +253,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
- RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
- RD::get_singleton()->draw_list_draw(p_subpass_draw_list, true);
+ RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u);
}
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index afd2f8e401..a1a99f931f 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -59,14 +59,23 @@ private:
TONEMAP_MODE_MAX
};
+ enum {
+ TONEMAP_FLAG_USE_BCS = (1 << 0),
+ TONEMAP_FLAG_USE_GLOW = (1 << 1),
+ TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2),
+ TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3),
+ TONEMAP_FLAG_USE_FXAA = (1 << 4),
+ TONEMAP_FLAG_USE_DEBANDING = (1 << 5),
+ TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 6),
+ };
+
struct TonemapPushConstant {
float bcs[3]; // 12 - 12
- uint32_t use_bcs; // 4 - 16
+ uint32_t flags; // 4 - 16
- uint32_t use_glow; // 4 - 20
- uint32_t use_auto_exposure; // 4 - 24
- uint32_t use_color_correction; // 4 - 28
- uint32_t tonemapper; // 4 - 32
+ float pixel_size[2]; // 8 - 24
+ uint32_t tonemapper; // 4 - 28
+ uint32_t pad; // 4 - 32
uint32_t glow_texture_size[2]; // 8 - 40
float glow_intensity; // 4 - 44
@@ -79,10 +88,6 @@ private:
float white; // 4 - 88
float auto_exposure_scale; // 4 - 92
float luminance_multiplier; // 4 - 96
-
- float pixel_size[2]; // 8 - 104
- uint32_t use_fxaa; // 4 - 108
- uint32_t use_debanding; // 4 - 112
};
/* tonemap actually writes to a framebuffer, which is
@@ -141,6 +146,8 @@ public:
bool use_debanding = false;
Vector2i texture_size;
uint32_t view_count = 1;
+
+ bool convert_to_srgb = false;
};
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp
index 6ec8612029..63c99facdd 100644
--- a/servers/rendering/renderer_rd/effects/vrs.cpp
+++ b/servers/rendering/renderer_rd/effects/vrs.cpp
@@ -85,9 +85,8 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>());
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
- RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
// RD::get_singleton()->draw_list_set_push_constant(draw_list, &vrs_shader.push_constant, sizeof(VRSPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp
index 98d826b1f9..8402cc7444 100644
--- a/servers/rendering/renderer_rd/environment/fog.cpp
+++ b/servers/rendering/renderer_rd/environment/fog.cpp
@@ -143,7 +143,7 @@ Vector3 Fog::fog_volume_get_size(RID p_fog_volume) const {
bool Fog::FogMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
uniform_set_updated = true;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true, true);
}
Fog::FogMaterialData::~FogMaterialData() {
diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 964d4d9adf..991ccf984e 100644
--- a/servers/rendering/renderer_rd/environment/gi.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -2215,7 +2215,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_
RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size);
RD::get_singleton()->compute_list_add_barrier(compute_list);
- //run one pass of fullsize jumpflood to fix up half size arctifacts
+ //run one pass of fullsize jumpflood to fix up half size artifacts
push_constant.half_size = false;
push_constant.step_size = 1;
@@ -2977,7 +2977,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID
push_constant.cell_offset = mipmaps[i].cell_offset;
push_constant.cell_count = mipmaps[i].cell_count;
- int64_t wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1;
+ int64_t wg_todo = (mipmaps[i].cell_count + wg_size - 1) / wg_size;
while (wg_todo) {
int64_t wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIPushConstant));
@@ -2998,7 +2998,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID
push_constant.cell_offset = mipmaps[i].cell_offset;
push_constant.cell_count = mipmaps[i].cell_count;
- int64_t wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1;
+ int64_t wg_todo = (mipmaps[i].cell_count + wg_size - 1) / wg_size;
while (wg_todo) {
int64_t wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIPushConstant));
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index ebf3c5f619..f94c701ada 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -185,7 +185,7 @@ bool SkyRD::SkyMaterialData::update_parameters(const HashMap<StringName, Variant
uniform_set_updated = true;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true, true);
}
SkyRD::SkyMaterialData::~SkyMaterialData() {
@@ -249,11 +249,9 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC
}
}
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant));
- RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
}
////////////////////////////////////////////////////////////////////////////////
@@ -941,23 +939,6 @@ void sky() {
sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
}
-
- { //create index array for copy shaders
- Vector<uint8_t> pv;
- pv.resize(6 * 4);
- {
- uint8_t *w = pv.ptrw();
- int *p32 = (int *)w;
- p32[0] = 0;
- p32[1] = 1;
- p32[2] = 2;
- p32[3] = 0;
- p32[4] = 2;
- p32[5] = 3;
- }
- index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
- index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6);
- }
}
void SkyRD::set_texture_format(RD::DataFormat p_texture_format) {
@@ -990,8 +971,6 @@ SkyRD::~SkyRD() {
if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_only_texture_uniform_set)) {
RD::get_singleton()->free(sky_scene_state.fog_only_texture_uniform_set);
}
-
- RD::get_singleton()->free(index_buffer); //array gets freed as dependency
}
void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, uint32_t p_view_count, const Projection *p_view_projections, const Vector3 *p_view_eye_offsets, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) {
diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h
index 7aee65fd67..ee2d81757c 100644
--- a/servers/rendering/renderer_rd/environment/sky.h
+++ b/servers/rendering/renderer_rd/environment/sky.h
@@ -70,9 +70,6 @@ public:
private:
RD::DataFormat texture_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- RID index_buffer;
- RID index_array;
-
enum SkyTextureSetVersion {
SKY_TEXTURE_SET_BACKGROUND,
SKY_TEXTURE_SET_HALF_RES,
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 7bca1dc3be..c98752fb39 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -433,10 +433,11 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
RID index_array_rd;
//skeleton and blend shape
+ bool pipeline_motion_vectors = pipeline_color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
if (surf->owner->mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format);
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format);
} else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format);
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format);
}
index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
@@ -1010,13 +1011,6 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DEPTH_TEXTURE) {
scene_state.used_depth_texture = true;
}
-
- if (p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) {
- if ((flags & (INSTANCE_DATA_FLAG_MULTIMESH | INSTANCE_DATA_FLAG_PARTICLES)) == INSTANCE_DATA_FLAG_MULTIMESH && RendererRD::MeshStorage::get_singleton()->_multimesh_enable_motion_vectors(inst->data->base)) {
- inst->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(inst->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET);
- }
- }
-
} else if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) {
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) {
rl->add_element(surf);
@@ -1435,7 +1429,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
// Note: when rendering stereoscopic (multiview) we are using our combined frustum projection to create
// our cluster data. We use reprojection in the shader to adjust for our left/right eye.
// This only works as we don't filter our cluster by depth buffer.
- // If we ever make this optimisation we should make it optional and only use it in mono.
+ // If we ever make this optimization we should make it optional and only use it in mono.
// What we win by filtering out a few lights, we loose by having to do the work double for stereo.
current_cluster_builder->begin(p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, !p_render_data->reflection_probe.is_valid());
}
@@ -1769,7 +1763,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
case RS::ENV_BG_CANVAS: {
if (!is_reflection_probe) {
RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target());
- copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true);
+ bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target());
+ copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear);
}
keep_color = true;
} break;
@@ -1847,7 +1842,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());
- bool finish_depth = using_ssao || using_sdfgi || using_voxelgi;
+ 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);
_render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear);
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 298a6c0752..12f8f6a366 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -436,7 +436,7 @@ void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) {
bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER);
}
SceneShaderForwardClustered::MaterialData::~MaterialData() {
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 2053c3a9a6..90d770399e 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -722,16 +722,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
using_subpass_post_process = false;
}
- // We do this last because our get_color_fbs creates and caches the framebuffer if we need it.
- RID four_subpasses = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES);
- if (using_subpass_post_process && four_subpasses.is_null()) {
- // can't do blit subpass because we don't have all subpasses
- using_subpass_post_process = false;
- }
-
if (using_subpass_post_process) {
// all as subpasses
- framebuffer = four_subpasses;
+ framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES);
} else if (using_subpass_transparent) {
// our tonemap pass is separate
framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_THREE_SUBPASSES);
@@ -803,7 +796,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (rb_data.is_valid()) {
RID dest_framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS);
RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target());
- copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true);
+ bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target());
+ copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear);
}
keep_color = true;
} break;
@@ -2145,9 +2139,9 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
//skeleton and blend shape
if (surf->owner->mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format);
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format);
} else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), vertex_array_rd, vertex_format);
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format);
}
index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index e4498ac533..32d2289f75 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -388,7 +388,7 @@ void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) {
bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER);
}
SceneShaderForwardMobile::MaterialData::~MaterialData() {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index eb33f296d0..d70bff8593 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -348,7 +348,7 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) {
////////////////////
-void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size) {
+void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) {
if (p_texture == RID()) {
p_texture = default_canvas_texture;
}
@@ -363,7 +363,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI
bool use_normal;
bool use_specular;
- bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, uniform_set, size, specular_shininess, use_normal, use_specular);
+ bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data);
//something odd happened
if (!success) {
_bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size);
@@ -421,6 +421,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
Color base_color = p_item->final_modulate;
+ bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_render_target);
for (int i = 0; i < 4; i++) {
push_constant.modulation[i] = 0;
@@ -441,6 +442,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.lights[3] = 0;
uint32_t base_flags = 0;
+ base_flags |= use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0;
uint16_t light_count = 0;
PipelineLightMode light_mode;
@@ -505,7 +507,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
//bind textures
- _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size);
+ _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF));
Rect2 src_rect;
Rect2 dst_rect;
@@ -566,10 +568,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.flags |= FLAGS_USE_LCD;
}
- push_constant.modulation[0] = rect->modulate.r * base_color.r;
- push_constant.modulation[1] = rect->modulate.g * base_color.g;
- push_constant.modulation[2] = rect->modulate.b * base_color.b;
- push_constant.modulation[3] = rect->modulate.a * base_color.a;
+ Color modulated = rect->modulate * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -618,10 +625,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
}
}
- push_constant.modulation[0] = np->color.r * base_color.r;
- push_constant.modulation[1] = np->color.g * base_color.g;
- push_constant.modulation[2] = np->color.b * base_color.b;
- push_constant.modulation[3] = np->color.a * base_color.a;
+ Color modulated = np->color * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -676,10 +688,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
_bind_canvas_texture(p_draw_list, polygon->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size);
- push_constant.modulation[0] = base_color.r;
- push_constant.modulation[1] = base_color.g;
- push_constant.modulation[2] = base_color.b;
- push_constant.modulation[3] = base_color.a;
+ Color color = base_color;
+ if (use_linear_colors) {
+ color = color.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = color.r;
+ push_constant.modulation[1] = color.g;
+ push_constant.modulation[2] = color.b;
+ push_constant.modulation[3] = color.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
@@ -718,6 +735,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y;
Color col = primitive->colors[j] * base_color;
+ if (use_linear_colors) {
+ col = col.srgb_to_linear();
+ }
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -732,6 +752,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y;
Color col = primitive->colors[j + 1] * base_color;
+ if (use_linear_colors) {
+ col = col.srgb_to_linear();
+ }
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -849,10 +872,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
- push_constant.modulation[0] = base_color.r * modulate.r;
- push_constant.modulation[1] = base_color.g * modulate.g;
- push_constant.modulation[2] = base_color.b * modulate.b;
- push_constant.modulation[3] = base_color.a * modulate.a;
+ Color modulated = modulate * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
@@ -872,9 +900,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID;
if (mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array, vertex_format);
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format);
} else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array, vertex_format);
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format);
}
RID pipeline = pipeline_variants->variants[light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format);
@@ -1113,8 +1141,9 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) {
pipeline_variants = &material_data->shader_data->pipeline_variants;
// Update uniform set.
- if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set.
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET);
+ RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target) ? material_data->uniform_set : material_data->uniform_set_srgb;
+ if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set.
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET);
material_data->set_as_used();
}
} else {
@@ -2235,12 +2264,14 @@ RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_
bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
-
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false);
+ bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false, RD::BARRIER_MASK_ALL_BARRIERS);
+ bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false, RD::BARRIER_MASK_ALL_BARRIERS);
+ return uniform_set_changed || uniform_set_srgb_changed;
}
RendererCanvasRenderRD::CanvasMaterialData::~CanvasMaterialData() {
free_parameters_uniform_set(uniform_set);
+ free_parameters_uniform_set(uniform_set_srgb);
}
RendererRD::MaterialStorage::MaterialData *RendererCanvasRenderRD::_create_material_func(CanvasShaderData *p_shader) {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 1116bed6e4..4c8cbd1c9f 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -75,6 +75,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
+ FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR = (1 << 11),
+
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
FLAGS_USING_PARTICLES = (1 << 13),
@@ -195,6 +197,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct CanvasMaterialData : public RendererRD::MaterialStorage::MaterialData {
CanvasShaderData *shader_data = nullptr;
RID uniform_set;
+ RID uniform_set_srgb;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
@@ -415,7 +418,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer);
- inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead.
+ inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead.
void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used);
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false);
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 870da3c321..4ccd2aa322 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -81,6 +81,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2;
blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
+ blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
@@ -171,7 +172,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
RID texture = texture_storage->texture_allocate();
texture_storage->texture_2d_initialize(texture, p_image);
- RID rd_texture = texture_storage->texture_get_rd_texture(texture);
+ RID rd_texture = texture_storage->texture_get_rd_texture(texture, false);
RD::SamplerState sampler_state;
sampler_state.min_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST;
@@ -237,6 +238,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
blit.push_constant.k2 = 0;
blit.push_constant.upscale = 1.0;
blit.push_constant.aspect_ratio = 1.0;
+ blit.push_constant.convert_to_srgb = false;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 14fb4e6340..705fb9e8e5 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -80,7 +80,7 @@ protected:
float upscale;
float aspect_ratio;
uint32_t layer;
- uint32_t pad1;
+ uint32_t convert_to_srgb;
};
struct Blit {
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 646bdc5436..9d4d266a7a 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -368,7 +368,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
buffers.base_texture = rb->get_internal_texture(i);
buffers.depth_texture = rb->get_depth_texture(i);
- // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum
+ // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum.
float z_near = p_render_data->scene_data->view_projection[i].get_z_near();
float z_far = p_render_data->scene_data->view_projection[i].get_z_far();
bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal);
@@ -391,7 +391,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
buffers.depth_texture = rb->get_depth_texture(i);
buffers.base_fb = FramebufferCacheRD::get_singleton()->get_cache(buffers.base_texture); // TODO move this into bokeh_dof_raster, we can do this internally
- // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum
+ // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum.
float z_near = p_render_data->scene_data->view_projection[i].get_z_near();
float z_far = p_render_data->scene_data->view_projection[i].get_z_far();
bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal);
@@ -552,6 +552,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier();
tonemap.view_count = rb->get_view_count();
+ tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
+
RID dest_fb;
if (fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR) {
// If we use FSR to upscale we need to write our result into an intermediate buffer.
@@ -647,6 +649,8 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier();
tonemap.view_count = rb->get_view_count();
+ tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(rb->get_render_target());
+
tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
RD::get_singleton()->draw_command_end_label();
@@ -697,7 +701,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren
copy_effects->copy_to_fb_rect(shadow_atlas_texture, dest_fb, Rect2i(Vector2(), size), false, true);
- // Visualise our view frustum to show coverage.
+ // Visualize our view frustum to show coverage.
for (int i = 0; i < p_render_data->render_shadow_count; i++) {
RID light = p_render_data->render_shadows[i].light;
RID base = light_storage->light_instance_get_base_light(light);
diff --git a/servers/rendering/renderer_rd/shaders/blit.glsl b/servers/rendering/renderer_rd/shaders/blit.glsl
index 14f190a49f..d451647bec 100644
--- a/servers/rendering/renderer_rd/shaders/blit.glsl
+++ b/servers/rendering/renderer_rd/shaders/blit.glsl
@@ -45,7 +45,7 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
- uint pad1;
+ bool convert_to_srgb;
}
data;
@@ -59,6 +59,13 @@ layout(binding = 0) uniform sampler2DArray src_rt;
layout(binding = 0) uniform sampler2D src_rt;
#endif
+vec3 linear_to_srgb(vec3 color) {
+ // If going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
void main() {
#ifdef APPLY_LENS_DISTORTION
vec2 coords = uv * 2.0 - 1.0;
@@ -94,4 +101,8 @@ void main() {
#else
color = texture(src_rt, uv);
#endif
+
+ if (data.convert_to_srgb) {
+ color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
+ }
}
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 6270450c15..31c5aadc88 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -36,6 +36,12 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
#GLOBALS
+#ifdef USE_ATTRIBUTES
+vec3 srgb_to_linear(vec3 color) {
+ return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
+}
+#endif
+
void main() {
vec4 instance_custom = vec4(0.0);
#ifdef USE_PRIMITIVE
@@ -65,7 +71,11 @@ void main() {
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
- vec4 color = color_attrib * draw_data.modulation;
+ vec4 color = color_attrib;
+ if (bool(draw_data.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR)) {
+ color.rgb = srgb_to_linear(color.rgb);
+ }
+ color *= draw_data.modulation;
vec2 uv = uv_attrib;
uvec4 bones = bone_attrib;
@@ -563,9 +573,6 @@ void main() {
}
vec4 base_color = color;
- if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
- color = vec4(0.0); //invisible by default due to using light mask
- }
#ifdef MODE_LIGHT_ONLY
float light_only_alpha = 0.0;
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 2dd5abb75f..7ac7cf9c07 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -12,7 +12,7 @@
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
-#define FLAGS_USING_LIGHT_MASK (1 << 11)
+#define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11)
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
#define FLAGS_USING_PARTICLES (1 << 13)
diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
index 31aabbe9d2..91a3b582b9 100644
--- a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
@@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
-
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
index 1b487835d2..947aa793d9 100644
--- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
@@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
-
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
index 3a82861057..2b3d27b000 100644
--- a/servers/rendering/renderer_rd/shaders/effects/copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
@@ -57,7 +57,7 @@ layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffe
#elif defined(DST_IMAGE_8BIT)
layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#else
-layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
+layout(rgba16f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#endif
#ifdef MODE_GAUSSIAN_BLUR
diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
index b8c64d09f4..7b3c2f1c3b 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
@@ -31,9 +31,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex] * float(params.face_size);
- gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl
index 0990dc7c2f..961ec96f00 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl
@@ -35,9 +35,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
- gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl
index c29accd8a7..05a20c459b 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl
@@ -11,9 +11,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl
index 29ebd74a90..05a84db6b6 100644
--- a/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl
@@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
-
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
index a7da0812df..4df74b8626 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
@@ -36,7 +36,7 @@ void main() {
if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing
return;
}
- //do not filter, SSR will generate arctifacts if this is done
+ //do not filter, SSR will generate artifacts if this is done
float divisor = 0.0;
vec4 color;
diff --git a/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl b/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl
index c62144fdaf..db710b7cdd 100644
--- a/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl
@@ -27,16 +27,11 @@ layout(location = 0) out vec2 uv_interp;
#endif //USE_MULTIVIEW
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
-
-#ifdef USE_MULTIVIEW
- uv_interp = vec3(base_arr[gl_VertexIndex], ViewIndex);
-
- gl_Position = vec4(uv_interp.xy * 2.0 - 1.0, 0.0, 1.0);
-#else
- uv_interp = base_arr[gl_VertexIndex];
-
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
+#ifdef MULTIVIEW
+ uv_interp.z = ViewIndex;
#endif
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 52aee8b648..33ec991107 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -13,9 +13,9 @@
layout(location = 0) out vec2 uv_interp;
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp = base_arr[gl_VertexIndex];
- gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
#[fragment]
@@ -57,14 +57,21 @@ layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
#endif
+#define FLAG_USE_BCS (1 << 0)
+#define FLAG_USE_GLOW (1 << 1)
+#define FLAG_USE_AUTO_EXPOSURE (1 << 2)
+#define FLAG_USE_COLOR_CORRECTION (1 << 3)
+#define FLAG_USE_FXAA (1 << 4)
+#define FLAG_USE_DEBANDING (1 << 5)
+#define FLAG_CONVERT_TO_SRGB (1 << 6)
+
layout(push_constant, std430) uniform Params {
vec3 bcs;
- bool use_bcs;
+ uint flags;
- bool use_glow;
- bool use_auto_exposure;
- bool use_color_correction;
+ vec2 pixel_size;
uint tonemapper;
+ uint pad;
uvec2 glow_texture_size;
float glow_intensity;
@@ -77,10 +84,6 @@ layout(push_constant, std430) uniform Params {
float white;
float auto_exposure_scale;
float luminance_multiplier;
-
- vec2 pixel_size;
- bool use_fxaa;
- bool use_debanding;
}
params;
@@ -318,10 +321,12 @@ vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blendi
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
- //need color clamping
+ // Needs color clamping.
+ glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
return max((color + glow) - (color * glow), vec3(0.0));
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
- //need color clamping
+ // Needs color clamping.
+ glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
glow = glow * vec3(0.5f) + vec3(0.5f);
color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
@@ -439,7 +444,7 @@ void main() {
float exposure = params.exposure;
#ifndef SUBPASS
- if (params.use_auto_exposure) {
+ if (bool(params.flags & FLAG_USE_AUTO_EXPOSURE)) {
exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale);
}
#endif
@@ -448,12 +453,12 @@ void main() {
// Early Tonemap & SRGB Conversion
#ifndef SUBPASS
- if (params.use_fxaa) {
+ if (bool(params.flags & FLAG_USE_FXAA)) {
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
}
- if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
if (params.glow_map_strength > 0.001) {
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
@@ -464,11 +469,12 @@ void main() {
color.rgb = apply_tonemapping(color.rgb, params.white);
- color.rgb = linear_to_srgb(color.rgb); // regular linear -> SRGB conversion
-
+ if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
+ color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
+ }
#ifndef SUBPASS
// Glow
- if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier;
if (params.glow_map_strength > 0.001) {
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
@@ -476,7 +482,9 @@ void main() {
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
- glow = linear_to_srgb(glow);
+ if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
+ glow = linear_to_srgb(glow);
+ }
color.rgb = apply_glow(color.rgb, glow);
}
@@ -484,15 +492,15 @@ void main() {
// Additional effects
- if (params.use_bcs) {
+ if (bool(params.flags & FLAG_USE_BCS)) {
color.rgb = apply_bcs(color.rgb, params.bcs);
}
- if (params.use_color_correction) {
+ if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
color.rgb = apply_color_correction(color.rgb);
}
- if (params.use_debanding) {
+ if (bool(params.flags & FLAG_USE_DEBANDING)) {
// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
// Otherwise, we're adding noise to an already-quantized image.
color.rgb += screen_space_dither(gl_FragCoord.xy);
diff --git a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl
index b450bb9fe9..d3d39a8b92 100644
--- a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl
@@ -20,13 +20,12 @@ layout(location = 0) out vec2 uv_interp;
#endif
void main() {
- vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- uv_interp.xy = base_arr[gl_VertexIndex];
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
+ gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
+ uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
#ifdef MULTIVIEW
uv_interp.z = ViewIndex;
#endif
-
- gl_Position = vec4(uv_interp.xy * 2.0 - 1.0, 0.0, 1.0);
}
#[fragment]
diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
index d605917acc..4e5b11aed8 100644
--- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
@@ -23,7 +23,7 @@ layout(push_constant, std430) uniform Params {
params;
void main() {
- vec2 base_arr[4] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(1.0, -1.0));
+ vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 1.0, 1.0);
}
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 9214a953aa..d1cfda515f 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
@@ -18,7 +18,11 @@ layout(location = 0) in vec3 vertex_attrib;
layout(location = 1) in vec2 normal_attrib;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
+#define TANGENT_USED
+#endif
+
+#ifdef TANGENT_USED
layout(location = 2) in vec2 tangent_attrib;
#endif
@@ -58,6 +62,18 @@ layout(location = 10) in uvec4 bone_attrib;
layout(location = 11) in vec4 weight_attrib;
#endif
+#ifdef MOTION_VECTORS
+layout(location = 12) in vec3 previous_vertex_attrib;
+
+#ifdef NORMAL_USED
+layout(location = 13) in vec2 previous_normal_attrib;
+#endif
+
+#ifdef TANGENT_USED
+layout(location = 14) in vec2 previous_tangent_attrib;
+#endif
+#endif // MOTION_VECTORS
+
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
@@ -85,7 +101,7 @@ layout(location = 3) out vec2 uv_interp;
layout(location = 4) out vec2 uv2_interp;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
layout(location = 5) out vec3 tangent_interp;
layout(location = 6) out vec3 binormal_interp;
#endif
@@ -161,7 +177,14 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec
}
#endif
-void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) {
+void vertex_shader(vec3 vertex_input,
+#ifdef NORMAL_USED
+ in vec2 normal_input,
+#endif
+#ifdef TANGENT_USED
+ in vec2 tangent_input,
+#endif
+ in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) {
vec4 instance_custom = vec4(0.0);
#if defined(COLOR_USED)
color_interp = color_attrib;
@@ -289,15 +312,15 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime
model_normal_matrix = model_normal_matrix * mat3(matrix);
}
- vec3 vertex = vertex_attrib;
+ vec3 vertex = vertex_input;
#ifdef NORMAL_USED
- vec3 normal = oct_to_vec3(normal_attrib * 2.0 - 1.0);
+ vec3 normal = oct_to_vec3(normal_input * 2.0 - 1.0);
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
- vec2 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0;
- vec3 tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0));
- float binormalf = sign(signed_tangent_attrib.y);
+#ifdef TANGENT_USED
+ vec2 signed_tangent_input = tangent_input * 2.0 - 1.0;
+ vec3 tangent = oct_to_vec3(vec2(signed_tangent_input.x, abs(signed_tangent_input.y) * 2.0 - 1.0));
+ float binormalf = sign(signed_tangent_input.y);
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
@@ -333,7 +356,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime
normal = model_normal_matrix * normal;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
tangent = model_normal_matrix * tangent;
binormal = model_normal_matrix * binormal;
@@ -377,7 +400,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
binormal = modelview_normal * binormal;
tangent = modelview_normal * tangent;
@@ -391,7 +414,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime
normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz;
tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz;
#endif
@@ -403,7 +426,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multime
normal_interp = normal;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
tangent_interp = tangent;
binormal_interp = binormal;
#endif
@@ -472,16 +495,33 @@ void main() {
instance_index_interp = instance_index;
mat4 model_matrix = instances.data[instance_index].transform;
-#if defined(MOTION_VECTORS)
+
+#ifdef MOTION_VECTORS
+ // Previous vertex.
global_time = scene_data_block.prev_data.time;
- vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
- global_time = scene_data_block.data.time;
- vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
+ vertex_shader(previous_vertex_attrib,
+#ifdef NORMAL_USED
+ previous_normal_attrib,
+#endif
+#ifdef TANGENT_USED
+ previous_tangent_attrib,
+#endif
+ instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
#else
- global_time = scene_data_block.data.time;
+ // Unused output.
vec4 screen_position;
- vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
#endif
+
+ // Current vertex.
+ global_time = scene_data_block.data.time;
+ vertex_shader(vertex_attrib,
+#ifdef NORMAL_USED
+ normal_attrib,
+#endif
+#ifdef TANGENT_USED
+ tangent_attrib,
+#endif
+ instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
}
#[fragment]
@@ -535,7 +575,11 @@ layout(location = 3) in vec2 uv_interp;
layout(location = 4) in vec2 uv2_interp;
#endif
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
+#define TANGENT_USED
+#endif
+
+#ifdef TANGENT_USED
layout(location = 5) in vec3 tangent_interp;
layout(location = 6) in vec3 binormal_interp;
#endif
@@ -771,7 +815,7 @@ void fragment_shader(in SceneData scene_data) {
float alpha = float(instances.data[instance_index].flags >> INSTANCE_FLAGS_FADE_SHIFT) / float(255.0);
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#ifdef TANGENT_USED
vec3 binormal = normalize(binormal_interp);
vec3 tangent = normalize(tangent_interp);
#else
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index ba8d901772..aa90ad6876 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -584,7 +584,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
{
vec4 clamp_rect = omni_lights.data[idx].atlas_rect;
- //redo shadowmapping, but shrink the model a bit to avoid arctifacts
+ //redo shadowmapping, but shrink the model a bit to avoid artifacts
vec4 splane = (omni_lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * omni_lights.data[idx].transmittance_bias, 1.0));
float shadow_len = length(splane.xyz);
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index d055f01009..fda341bbc9 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -751,7 +751,7 @@ MaterialStorage::MaterialData::~MaterialData() {
}
}
-void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) {
+void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material) {
TextureStorage *texture_storage = TextureStorage::get_singleton();
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -917,7 +917,7 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va
if (tex) {
rd_texture = (srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture;
#ifdef TOOLS_ENABLED
- if (tex->detect_3d_callback && p_use_linear_color) {
+ if (tex->detect_3d_callback && p_3d_material) {
tex->detect_3d_callback(tex->detect_3d_callback_ud);
}
if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) {
@@ -986,7 +986,7 @@ void MaterialStorage::MaterialData::free_parameters_uniform_set(RID p_uniform_se
}
}
-bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier) {
+bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier) {
if ((uint32_t)ubo_data.size() != p_ubo_size) {
p_uniform_dirty = true;
if (uniform_buffer.is_valid()) {
@@ -1033,7 +1033,7 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<
}
if (p_textures_dirty && tex_uniform_count) {
- update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color);
+ update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color, p_3d_material);
}
if (p_ubo_size == 0 && (p_texture_uniforms.size() == 0)) {
@@ -1821,14 +1821,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture
if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) {
//textire
if (!p_load_textures) {
- value = RID();
continue;
}
String path = value;
- Ref<Resource> resource = ResourceLoader::load(path);
- ERR_CONTINUE(resource.is_null());
- value = resource;
+ if (path.is_empty()) {
+ value = RID();
+ } else {
+ Ref<Resource> resource = ResourceLoader::load(path);
+ value = resource;
+ }
}
if (global_shader_uniforms.variables.has(name)) {
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index b6da3df783..ae97f43a3c 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -78,7 +78,7 @@ public:
struct MaterialData {
Vector<RendererRD::TextureStorage::RenderTarget *> render_target_cache;
void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color);
- void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color);
+ void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material);
void set_as_used();
virtual void set_render_priority(int p_priority) = 0;
@@ -87,7 +87,7 @@ public:
virtual ~MaterialData();
//to be used internally by update_parameters, in the most common configuration of material parameters
- bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
+ bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &r_uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
void free_parameters_uniform_set(RID p_uniform_set);
private:
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 67b5cdd291..439d0702f5 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "mesh_storage.h"
-#include "../../rendering_server_globals.h"
using namespace RendererRD;
@@ -854,8 +853,11 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) {
}
memfree(surface.versions);
}
- if (surface.vertex_buffer.is_valid()) {
- RD::get_singleton()->free(surface.vertex_buffer);
+
+ for (uint32_t i = 0; i < 2; i++) {
+ if (surface.vertex_buffer[i].is_valid()) {
+ RD::get_singleton()->free(surface.vertex_buffer[i]);
+ }
}
}
mi->surfaces.clear();
@@ -881,35 +883,38 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3
MeshInstance::Surface s;
if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) {
- //surface warrants transform
- s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true);
-
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.append_id(s.vertex_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- if (mi->blend_weights_buffer.is_valid()) {
- u.append_id(mi->blend_weights_buffer);
- } else {
- u.append_id(default_rd_storage_buffer);
- }
- uniforms.push_back(u);
- }
- s.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE);
+ _mesh_instance_add_surface_buffer(mi, mesh, &s, p_surface, 0);
}
mi->surfaces.push_back(s);
mi->dirty = true;
}
+void MeshStorage::_mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh, MeshInstance::Surface *s, uint32_t p_surface, uint32_t p_buffer_index) {
+ s->vertex_buffer[p_buffer_index] = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true);
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.append_id(s->vertex_buffer[p_buffer_index]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ if (mi->blend_weights_buffer.is_valid()) {
+ u.append_id(mi->blend_weights_buffer);
+ } else {
+ u.append_id(default_rd_storage_buffer);
+ }
+ uniforms.push_back(u);
+ }
+ s->uniform_set[p_buffer_index] = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE);
+}
+
void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
@@ -956,6 +961,8 @@ void MeshStorage::update_mesh_instances() {
}
//process skeletons and blend shapes
+ uint64_t frame = RSG::rasterizer->get_frame_number();
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
while (dirty_mesh_instance_arrays.first()) {
@@ -964,7 +971,29 @@ void MeshStorage::update_mesh_instances() {
Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton);
for (uint32_t i = 0; i < mi->surfaces.size(); i++) {
- if (mi->surfaces[i].uniform_set == RID() || mi->mesh->surfaces[i]->uniform_set == RID()) {
+ if (mi->surfaces[i].uniform_set[0].is_null() || mi->mesh->surfaces[i]->uniform_set.is_null()) {
+ // Skip over mesh instances that don't require their own uniform buffers.
+ continue;
+ }
+
+ mi->surfaces[i].previous_buffer = mi->surfaces[i].current_buffer;
+
+ if (uses_motion_vectors && (frame - mi->surfaces[i].last_change) == 1) {
+ // Previous buffer's data can only be one frame old to be able to use motion vectors.
+ uint32_t new_buffer_index = mi->surfaces[i].current_buffer ^ 1;
+
+ if (mi->surfaces[i].uniform_set[new_buffer_index].is_null()) {
+ // Create the new vertex buffer on demand where the result for the current frame will be stored.
+ _mesh_instance_add_surface_buffer(mi, mi->mesh, &mi->surfaces[i], i, new_buffer_index);
+ }
+
+ mi->surfaces[i].current_buffer = new_buffer_index;
+ }
+
+ mi->surfaces[i].last_change = frame;
+
+ RID mi_surface_uniform_set = mi->surfaces[i].uniform_set[mi->surfaces[i].current_buffer];
+ if (mi_surface_uniform_set.is_null()) {
continue;
}
@@ -972,7 +1001,7 @@ void MeshStorage::update_mesh_instances() {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, skeleton_shader.pipeline[array_is_2d ? SkeletonShader::SHADER_MODE_2D : SkeletonShader::SHADER_MODE_3D]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->surfaces[i].uniform_set, SkeletonShader::UNIFORM_SET_INSTANCE);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi_surface_uniform_set, SkeletonShader::UNIFORM_SET_INSTANCE);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->mesh->surfaces[i]->uniform_set, SkeletonShader::UNIFORM_SET_SURFACE);
if (sk && sk->uniform_set_mi.is_valid()) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sk->uniform_set_mi, SkeletonShader::UNIFORM_SET_SKELETON);
@@ -1032,7 +1061,7 @@ void MeshStorage::update_mesh_instances() {
RD::get_singleton()->compute_list_end();
}
-void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis) {
+void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis) {
Vector<RD::VertexAttribute> attributes;
Vector<RID> buffers;
@@ -1105,7 +1134,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
if (mis) {
- buffer = mis->vertex_buffer;
+ buffer = mis->vertex_buffer[mis->current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1117,7 +1146,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
stride += sizeof(uint16_t) * 2;
if (mis) {
- buffer = mis->vertex_buffer;
+ buffer = mis->vertex_buffer[mis->current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1128,7 +1157,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
stride += sizeof(uint16_t) * 2;
if (mis) {
- buffer = mis->vertex_buffer;
+ buffer = mis->vertex_buffer[mis->current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1193,6 +1222,32 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
attributes.push_back(vd);
buffers.push_back(buffer);
+
+ if (p_input_motion_vectors) {
+ // Since the previous vertex, normal and tangent can't be part of the vertex format but they are required when motion
+ // vectors are enabled, we opt to push a copy of the vertex attribute with a different location and buffer (if it's
+ // part of an instance that has one).
+ switch (i) {
+ case RS::ARRAY_VERTEX: {
+ vd.location = ATTRIBUTE_LOCATION_PREV_VERTEX;
+ } break;
+ case RS::ARRAY_NORMAL: {
+ vd.location = ATTRIBUTE_LOCATION_PREV_NORMAL;
+ } break;
+ case RS::ARRAY_TANGENT: {
+ vd.location = ATTRIBUTE_LOCATION_PREV_TANGENT;
+ } break;
+ }
+
+ if (int(vd.location) != i) {
+ if (mis && buffer != mesh_default_rd_buffers[i]) {
+ buffer = mis->vertex_buffer[mis->previous_buffer];
+ }
+
+ attributes.push_back(vd);
+ buffers.push_back(buffer);
+ }
+ }
}
//update final stride
@@ -1202,7 +1257,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
int loc = attributes[i].location;
- if (loc < RS::ARRAY_COLOR) {
+ if ((loc < RS::ARRAY_COLOR) || ((loc >= ATTRIBUTE_LOCATION_PREV_VERTEX) && (loc <= ATTRIBUTE_LOCATION_PREV_TANGENT))) {
attributes.write[i].stride = stride;
} else if (loc < RS::ARRAY_BONES) {
attributes.write[i].stride = attribute_stride;
@@ -1212,6 +1267,9 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
v.input_mask = p_input_mask;
+ v.current_buffer = mis ? mis->current_buffer : 0;
+ v.previous_buffer = mis ? mis->previous_buffer : 0;
+ v.input_motion_vectors = p_input_motion_vectors;
v.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers);
}
@@ -1289,12 +1347,9 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}
-bool MeshStorage::_multimesh_enable_motion_vectors(RID p_multimesh) {
- MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
- ERR_FAIL_COND_V(!multimesh, false);
-
+void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) {
if (multimesh->motion_vectors_enabled) {
- return false;
+ return;
}
multimesh->motion_vectors_enabled = true;
@@ -1307,22 +1362,30 @@ bool MeshStorage::_multimesh_enable_motion_vectors(RID p_multimesh) {
multimesh->data_cache.append_array(multimesh->data_cache);
}
- if (multimesh->buffer_set) {
+ uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float);
+ uint32_t new_buffer_size = buffer_size * 2;
+ RID new_buffer = RD::get_singleton()->storage_buffer_create(new_buffer_size);
+
+ if (multimesh->buffer_set && multimesh->data_cache.is_empty()) {
+ // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU.
RD::get_singleton()->barrier();
- Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer);
- if (!multimesh->data_cache.is_empty()) {
- memcpy(buffer_data.ptrw(), multimesh->data_cache.ptr(), buffer_data.size());
- }
+ RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER);
+ RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size);
+ } else if (!multimesh->data_cache.is_empty()) {
+ // Simply upload the data cached in the CPU, which should already be doubled in size.
+ ERR_FAIL_COND(multimesh->data_cache.size() * sizeof(float) != size_t(new_buffer_size));
+ RD::get_singleton()->buffer_update(new_buffer, 0, new_buffer_size, multimesh->data_cache.ptr());
+ }
+ if (multimesh->buffer.is_valid()) {
RD::get_singleton()->free(multimesh->buffer);
- uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float) * 2;
- multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size);
- RD::get_singleton()->buffer_update(multimesh->buffer, 0, buffer_data.size(), buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER);
- RD::get_singleton()->buffer_update(multimesh->buffer, buffer_data.size(), buffer_data.size(), buffer_data.ptr());
- multimesh->uniform_set_3d = RID(); // Cleared by dependency
- return true;
}
- return false; // Update the transforms uniform set cache
+
+ multimesh->buffer = new_buffer;
+ multimesh->uniform_set_3d = RID(); // Cleared by dependency.
+
+ // Invalidate any references to the buffer that was released and the uniform set that was pointing to it.
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}
void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) {
@@ -1531,6 +1594,12 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index,
ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D);
_multimesh_make_local(multimesh);
+
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
+ if (uses_motion_vectors) {
+ _multimesh_enable_motion_vectors(multimesh);
+ }
+
_multimesh_update_motion_vectors_data_cache(multimesh);
{
@@ -1749,6 +1818,11 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
ERR_FAIL_COND(!multimesh);
ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache));
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
+ if (uses_motion_vectors) {
+ _multimesh_enable_motion_vectors(multimesh);
+ }
+
if (multimesh->motion_vectors_enabled) {
uint32_t frame = RSG::rasterizer->get_frame_number();
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 4d46a62a76..99ba69f98a 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -31,6 +31,7 @@
#ifndef MESH_STORAGE_RD_H
#define MESH_STORAGE_RD_H
+#include "../../rendering_server_globals.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
@@ -90,6 +91,9 @@ private:
struct Version {
uint32_t input_mask = 0;
+ uint32_t current_buffer = 0;
+ uint32_t previous_buffer = 0;
+ bool input_motion_vectors = false;
RD::VertexFormatID vertex_format = 0;
RID vertex_array;
};
@@ -162,8 +166,11 @@ private:
Mesh *mesh = nullptr;
RID skeleton;
struct Surface {
- RID vertex_buffer;
- RID uniform_set;
+ RID vertex_buffer[2];
+ RID uniform_set[2];
+ uint32_t current_buffer = 0;
+ uint32_t previous_buffer = 0;
+ uint64_t last_change = 0;
Mesh::Surface::Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
@@ -183,10 +190,11 @@ private:
weight_update_list(this), array_update_list(this) {}
};
- void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis = nullptr);
+ void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr);
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
+ void _mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh, MeshInstance::Surface *s, uint32_t p_surface, uint32_t p_buffer_index);
mutable RID_Owner<MeshInstance> mesh_instance_owner;
@@ -234,6 +242,7 @@ private:
MultiMesh *multimesh_dirty_list = nullptr;
_FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const;
+ _FORCE_INLINE_ void _multimesh_enable_motion_vectors(MultiMesh *multimesh);
_FORCE_INLINE_ void _multimesh_update_motion_vectors_data_cache(MultiMesh *multimesh);
_FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb);
_FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb);
@@ -310,6 +319,12 @@ private:
Skeleton *skeleton_dirty_list = nullptr;
+ enum AttributeLocation {
+ ATTRIBUTE_LOCATION_PREV_VERTEX = 12,
+ ATTRIBUTE_LOCATION_PREV_NORMAL = 13,
+ ATTRIBUTE_LOCATION_PREV_TANGENT = 14
+ };
+
public:
static MeshStorage *get_singleton();
@@ -436,7 +451,7 @@ public:
}
}
- _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
+ _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
s->version_lock.lock();
@@ -444,9 +459,11 @@ public:
//there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way
for (uint32_t i = 0; i < s->version_count; i++) {
- if (s->versions[i].input_mask != p_input_mask) {
+ if (s->versions[i].input_mask != p_input_mask || s->versions[i].input_motion_vectors != p_input_motion_vectors) {
+ // Find the version that matches the inputs required.
continue;
}
+
//we have this version, hooray
r_vertex_format = s->versions[i].vertex_format;
r_vertex_array_rd = s->versions[i].vertex_array;
@@ -458,7 +475,7 @@ public:
s->version_count++;
s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count);
- _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask);
+ _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask, p_input_motion_vectors);
r_vertex_format = s->versions[version].vertex_format;
r_vertex_array_rd = s->versions[version].vertex_array;
@@ -466,7 +483,7 @@ public:
s->version_lock.unlock();
}
- _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
+ _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
ERR_FAIL_COND(!mi);
Mesh *mesh = mi->mesh;
@@ -474,15 +491,26 @@ public:
MeshInstance::Surface *mis = &mi->surfaces[p_surface_index];
Mesh::Surface *s = mesh->surfaces[p_surface_index];
+ uint32_t current_buffer = mis->current_buffer;
+
+ // Using the previous buffer is only allowed if the surface was updated this frame and motion vectors are required.
+ uint32_t previous_buffer = p_input_motion_vectors && (RSG::rasterizer->get_frame_number() == mis->last_change) ? mis->previous_buffer : current_buffer;
s->version_lock.lock();
//there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way
for (uint32_t i = 0; i < mis->version_count; i++) {
- if (mis->versions[i].input_mask != p_input_mask) {
+ if (mis->versions[i].input_mask != p_input_mask || mis->versions[i].input_motion_vectors != p_input_motion_vectors) {
+ // Find the version that matches the inputs required.
continue;
}
+
+ if (mis->versions[i].current_buffer != current_buffer || mis->versions[i].previous_buffer != previous_buffer) {
+ // Find the version that corresponds to the correct buffers that should be used.
+ continue;
+ }
+
//we have this version, hooray
r_vertex_format = mis->versions[i].vertex_format;
r_vertex_array_rd = mis->versions[i].vertex_array;
@@ -494,7 +522,7 @@ public:
mis->version_count++;
mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count);
- _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis);
+ _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, p_input_motion_vectors, mis);
r_vertex_format = mis->versions[version].vertex_format;
r_vertex_array_rd = mis->versions[version].vertex_array;
@@ -593,7 +621,6 @@ public:
virtual AABB multimesh_get_aabb(RID p_multimesh) const override;
void _update_dirty_multimeshes();
- bool _multimesh_enable_motion_vectors(RID p_multimesh);
void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset);
_FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const {
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 8f647be5c9..0a91672544 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -1629,7 +1629,7 @@ MaterialStorage::ShaderData *ParticlesStorage::_create_particles_shader_func() {
}
bool ParticlesStorage::ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true, false);
}
ParticlesStorage::ParticleProcessMaterialData::~ParticleProcessMaterialData() {
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 a7b8f985d9..27c435eeba 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
@@ -234,6 +234,12 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
RendererRD::MaterialStorage::store_transform(prev_cam_transform, prev_ubo.inv_view_matrix);
RendererRD::MaterialStorage::store_transform(prev_cam_transform.affine_inverse(), prev_ubo.view_matrix);
+#ifdef REAL_T_IS_DOUBLE
+ RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.x, &prev_ubo.inv_view_matrix[12], &prev_ubo.inv_view_matrix[3]);
+ RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.y, &prev_ubo.inv_view_matrix[13], &prev_ubo.inv_view_matrix[7]);
+ RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.z, &prev_ubo.inv_view_matrix[14], &prev_ubo.inv_view_matrix[11]);
+#endif
+
for (uint32_t v = 0; v < view_count; v++) {
prev_projection = prev_correction * view_projection[v];
RendererRD::MaterialStorage::store_camera(prev_projection, prev_ubo.projection_matrix_view[v]);
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index d84f6e6850..286a9528fc 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -46,9 +46,11 @@ void TextureStorage::CanvasTexture::clear_sets() {
}
for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
- if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j])) {
- RD::get_singleton()->free(uniform_sets[i][j]);
- uniform_sets[i][j] = RID();
+ for (int k = 0; k < 2; k++) {
+ if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j][k])) {
+ RD::get_singleton()->free(uniform_sets[i][j][k]);
+ uniform_sets[i][j][k] = RID();
+ }
}
}
}
@@ -641,7 +643,7 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS:
ct->clear_sets();
}
-bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) {
+bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
CanvasTexture *ct = nullptr;
@@ -674,7 +676,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat;
ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, false);
- RID uniform_set = ct->uniform_sets[filter][repeat];
+ RID uniform_set = ct->uniform_sets[filter][repeat][int(p_use_srgb)];
if (!RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
//create and update
Vector<RD::Uniform> uniforms;
@@ -688,7 +690,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE));
ct->size_cache = Size2i(1, 1);
} else {
- u.append_id(t->rd_texture);
+ u.append_id(t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture);
ct->size_cache = Size2i(t->width_2d, t->height_2d);
if (t->render_target) {
t->render_target->was_used = true;
@@ -741,7 +743,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
}
uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, p_base_set);
- ct->uniform_sets[filter][repeat] = uniform_set;
+ ct->uniform_sets[filter][repeat][int(p_use_srgb)] = uniform_set;
ct->cleared_cache = false;
}
@@ -1268,7 +1270,35 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
#endif
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0);
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
- Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
+ Ref<Image> image;
+
+ // Expand RGB10_A2 into RGBAH. This is needed for capturing viewport data
+ // when using the mobile renderer with HDR mode on.
+ if (tex->rd_format == RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32) {
+ Vector<uint8_t> new_data;
+ new_data.resize(data.size() * 2);
+ uint16_t *ndp = (uint16_t *)new_data.ptr();
+
+ uint32_t *ptr = (uint32_t *)data.ptr();
+ uint32_t num_pixels = data.size() / 4;
+
+ for (uint32_t ofs = 0; ofs < num_pixels; ofs++) {
+ uint32_t px = ptr[ofs];
+ uint32_t r = (px & 0x3FF);
+ uint32_t g = ((px >> 10) & 0x3FF);
+ uint32_t b = ((px >> 20) & 0x3FF);
+ uint32_t a = ((px >> 30) & 0x3);
+
+ ndp[ofs * 4 + 0] = Math::make_half_float(float(r) / 1023.0);
+ ndp[ofs * 4 + 1] = Math::make_half_float(float(g) / 1023.0);
+ ndp[ofs * 4 + 2] = Math::make_half_float(float(b) / 1023.0);
+ ndp[ofs * 4 + 3] = Math::make_half_float(float(a) / 3.0);
+ }
+ image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, new_data);
+ } else {
+ image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
+ }
+
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
if (tex->format != tex->validated_format) {
image->convert(tex->format);
@@ -3020,10 +3050,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
if (rt->size.width == 0 || rt->size.height == 0) {
return;
}
- //until we implement support for HDR monitors (and render target is attached to screen), this is enough.
- rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8;
+ if (rt->use_hdr) {
+ rt->color_format = RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
+ rt->color_format_srgb = rt->color_format;
+ rt->image_format = rt->is_transparent ? Image::FORMAT_RGBAH : Image::FORMAT_RGBH;
+ } else {
+ rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+ rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8;
+ }
RD::TextureFormat rd_color_attachment_format;
RD::TextureView rd_view;
@@ -3106,6 +3141,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
tex->rd_format = rt->color_format;
tex->rd_format_srgb = rt->color_format_srgb;
tex->format = rt->image_format;
+ tex->validated_format = rt->use_hdr ? Image::FORMAT_RGBAH : Image::FORMAT_RGBA8;
Vector<RID> proxies = tex->proxies; //make a copy, since update may change it
for (int i = 0; i < proxies.size(); i++) {
@@ -3328,6 +3364,25 @@ RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) con
return rt->msaa;
}
+void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+
+ if (p_use_hdr == rt->use_hdr) {
+ return;
+ }
+
+ rt->use_hdr = p_use_hdr;
+ _update_render_target(rt);
+}
+
+bool TextureStorage::render_target_is_using_hdr(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, false);
+
+ return rt->use_hdr;
+}
+
RID TextureStorage::render_target_get_rd_framebuffer(RID p_render_target) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND_V(!rt, RID());
@@ -3404,7 +3459,7 @@ bool TextureStorage::render_target_is_clear_requested(RID p_render_target) {
Color TextureStorage::render_target_get_clear_request_color(RID p_render_target) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND_V(!rt, Color());
- return rt->clear_color;
+ return rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color;
}
void TextureStorage::render_target_disable_clear_request(RID p_render_target) {
@@ -3420,7 +3475,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
return;
}
Vector<Color> clear_colors;
- clear_colors.push_back(rt->clear_color);
+ clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color);
RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors);
RD::get_singleton()->draw_list_end();
rt->clear_requested = false;
@@ -3735,7 +3790,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
// TODO figure out stereo support here
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+ copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, !rt->use_hdr, true);
} else {
copy_effects->copy_to_fb_rect(rt->color, rt->backbuffer_fb, region, false, false, false, false, RID(), false, true);
}
@@ -3759,7 +3814,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
RID mipmap = rt->backbuffer_mipmaps[i];
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr);
} else {
copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
}
@@ -3789,9 +3844,9 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
}
}
- //single texture copy for backbuffer
+ // Single texture copy for backbuffer.
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
+ copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, !rt->use_hdr);
} else {
copy_effects->set_color_raster(rt->backbuffer_mipmap0, p_color, region);
}
@@ -3833,7 +3888,7 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
RID mipmap = rt->backbuffer_mipmaps[i];
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr);
} else {
copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 6df6faa40a..276c8c4ce2 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -96,7 +96,7 @@ private:
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
- RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
+ RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX][2];
Size2i size_cache = Size2i(1, 1);
bool use_normal_cache = false;
@@ -341,6 +341,7 @@ private:
Image::Format image_format = Image::FORMAT_L8;
bool is_transparent = false;
+ bool use_hdr = false;
bool sdf_enabled = false;
@@ -474,7 +475,7 @@ public:
virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
- bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular);
+ bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data);
/* Texture API */
@@ -717,6 +718,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) override;
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) override;
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 44b15ee91d..45bbcf51c4 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1970,7 +1970,6 @@ void RendererSceneCull::_update_instance_aabb(Instance *p_instance) {
}
}
- // <Zylann> This is why I didn't re-use Instance::aabb to implement custom AABBs
if (p_instance->extra_margin) {
new_aabb.grow_by(p_instance->extra_margin);
}
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 7a829826d2..5880bf3951 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -1163,6 +1163,17 @@ void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_m
_configure_3d_render_buffers(viewport);
}
+void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) {
+ Viewport *viewport = viewport_owner.get_or_null(p_viewport);
+ ERR_FAIL_COND(!viewport);
+
+ if (viewport->use_hdr_2d == p_use_hdr_2d) {
+ return;
+ }
+ viewport->use_hdr_2d = p_use_hdr_2d;
+ RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d);
+}
+
void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_COND(!viewport);
@@ -1183,6 +1194,7 @@ void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) {
return;
}
viewport->use_taa = p_use_taa;
+ num_viewports_with_motion_vectors += p_use_taa ? 1 : -1;
_configure_3d_render_buffers(viewport);
}
@@ -1367,6 +1379,10 @@ bool RendererViewport::free(RID p_rid) {
RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid);
}
+ if (viewport->use_taa) {
+ num_viewports_with_motion_vectors--;
+ }
+
viewport_owner.free(p_rid);
return true;
@@ -1422,6 +1438,10 @@ int RendererViewport::get_total_draw_calls_used() const {
return total_draw_calls_used;
}
+int RendererViewport::get_num_viewports_with_motion_vectors() const {
+ return num_viewports_with_motion_vectors;
+}
+
RendererViewport::RendererViewport() {
occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread");
}
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index c004d05b23..3bfb1afd51 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -116,6 +116,7 @@ public:
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
bool transparent_bg = false;
+ bool use_hdr_2d = false;
uint32_t canvas_cull_mask = 0xffffffff;
@@ -157,6 +158,7 @@ public:
update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE;
clear_mode = RS::VIEWPORT_CLEAR_ALWAYS;
transparent_bg = false;
+ use_hdr_2d = false;
viewport_to_screen = DisplayServer::INVALID_WINDOW_ID;
shadow_atlas_size = 0;
@@ -196,6 +198,8 @@ public:
int total_vertices_drawn = 0;
int total_draw_calls_used = 0;
+ int num_viewports_with_motion_vectors = 0;
+
private:
Vector<Viewport *> _sort_active_viewports();
void _viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count);
@@ -250,6 +254,7 @@ public:
void viewport_remove_canvas(RID p_viewport, RID p_canvas);
void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset);
void viewport_set_transparent_background(RID p_viewport, bool p_enabled);
+ void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d);
void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform);
void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer);
@@ -299,6 +304,7 @@ public:
int get_total_objects_drawn() const;
int get_total_primitives_drawn() const;
int get_total_draw_calls_used() const;
+ int get_num_viewports_with_motion_vectors() const;
// Workaround for setting this on thread.
void call_set_vsync_mode(DisplayServer::VSyncMode p_mode, DisplayServer::WindowID p_window);
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 4e1ff07be4..3da69b9c3c 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -538,15 +538,15 @@ Error RenderingDevice::_reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spir
if (r_reflection_data.uniforms[set][k].binding == (uint32_t)info.binding) {
// Already exists, verify that it's the same type.
ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].type != info.type, FAILED,
- "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type.");
+ "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type.");
// Also, verify that it's the same size.
ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].length != info.length, FAILED,
- "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size.");
+ "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size.");
// Also, verify that it has the same writability.
ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].writable != info.writable, FAILED,
- "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability.");
+ "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability.");
// Just append stage mask and return.
r_reflection_data.uniforms.write[set].write[k].stages_mask.set_flag(stage_flag);
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 30d9b1c7b7..1ade1b25c4 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -840,6 +840,7 @@ public:
virtual bool uniform_set_is_valid(RID p_uniform_set) = 0;
virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0;
+ virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0) = 0; // This causes stall, only use to retrieve large buffers for saving.
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index e9b40eb082..9ad2175332 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -629,6 +629,7 @@ public:
FUNC2(viewport_remove_canvas, RID, RID)
FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &)
FUNC2(viewport_set_transparent_background, RID, bool)
+ FUNC2(viewport_set_use_hdr_2d, RID, bool)
FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool)
FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool)
diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp
index 7af326f779..8d6ddf9121 100644
--- a/servers/rendering/shader_preprocessor.cpp
+++ b/servers/rendering/shader_preprocessor.cpp
@@ -420,11 +420,11 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}
+ Vector<String> args;
if (p_tokenizer->peek() == '(') {
// Macro has arguments.
p_tokenizer->get_token();
- Vector<String> args;
while (true) {
String name = p_tokenizer->get_identifier();
if (name.is_empty()) {
@@ -442,17 +442,24 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}
}
+ }
- Define *define = memnew(Define);
+ String body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
+ if (body.begins_with("##")) {
+ set_error(RTR("'##' must not appear at beginning of macro expansion."), line);
+ return;
+ }
+ if (body.ends_with("##")) {
+ set_error(RTR("'##' must not appear at end of macro expansion."), line);
+ return;
+ }
+
+ Define *define = memnew(Define);
+ if (!args.is_empty()) {
define->arguments = args;
- define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
- state->defines[label] = define;
- } else {
- // Simple substitution macro.
- Define *define = memnew(Define);
- define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
- state->defines[label] = define;
}
+ define->body = body;
+ state->defines[label] = define;
}
void ShaderPreprocessor::process_elif(Tokenizer *p_tokenizer) {
@@ -1074,9 +1081,13 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num
}
}
+ concatenate_macro_body(body);
+
result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length());
} else {
- result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length()));
+ concatenate_macro_body(body);
+
+ result = result.substr(0, index) + " " + body + " " + result.substr(index + key.length(), result.length() - (index + key.length()));
}
r_expanded = result;
@@ -1115,6 +1126,40 @@ bool ShaderPreprocessor::find_match(const String &p_string, const String &p_valu
return false;
}
+void ShaderPreprocessor::concatenate_macro_body(String &r_body) {
+ int index_start = r_body.find("##");
+ while (index_start > -1) {
+ int index_end = index_start + 2; // First character after ##.
+ // The macro was checked during creation so this should never happen.
+ ERR_FAIL_INDEX(index_end, r_body.size());
+
+ // If there more than two # in a row, then it's not a concatenation.
+ bool is_concat = true;
+ while (index_end <= r_body.length() && r_body[index_end] == '#') {
+ index_end++;
+ is_concat = false;
+ }
+ if (!is_concat) {
+ index_start = r_body.find("##", index_end);
+ continue;
+ }
+
+ // Skip whitespace after ##.
+ while (index_end < r_body.length() && is_char_space(r_body[index_end])) {
+ index_end++;
+ }
+
+ // Skip whitespace before ##.
+ while (index_start >= 1 && is_char_space(r_body[index_start - 1])) {
+ index_start--;
+ }
+
+ r_body = r_body.substr(0, index_start) + r_body.substr(index_end, r_body.length() - index_end);
+
+ index_start = r_body.find("##", index_start);
+ }
+}
+
String ShaderPreprocessor::next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives) {
const int line = p_tokenizer->get_line();
int nesting = 0;
diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h
index 406b663228..9448a97d68 100644
--- a/servers/rendering/shader_preprocessor.h
+++ b/servers/rendering/shader_preprocessor.h
@@ -205,6 +205,7 @@ private:
Error expand_macros(const String &p_string, int p_line, String &r_result);
bool expand_macros_once(const String &p_line, int p_line_number, const RBMap<String, Define *>::Element *p_define_pair, String &r_expanded);
bool find_match(const String &p_string, const String &p_value, int &r_index, int &r_index_start);
+ void concatenate_macro_body(String &r_body);
String next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives);
void add_to_output(const String &p_str);
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index c3a257595c..6332dd578e 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -151,6 +151,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) = 0;
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) = 0;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const = 0;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) = 0;
+ virtual bool render_target_is_using_hdr(RID p_render_target) const = 0;
virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) = 0;
virtual bool render_target_is_clear_requested(RID p_render_target) = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 48b38cf2b7..45ba0b3c08 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2231,6 +2231,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_positional_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &RenderingServer::viewport_set_positional_shadow_atlas_quadrant_subdivision);
ClassDB::bind_method(D_METHOD("viewport_set_msaa_3d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_3d);
ClassDB::bind_method(D_METHOD("viewport_set_msaa_2d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_2d);
+ ClassDB::bind_method(D_METHOD("viewport_set_use_hdr_2d", "viewport", "enabled"), &RenderingServer::viewport_set_use_hdr_2d);
ClassDB::bind_method(D_METHOD("viewport_set_screen_space_aa", "viewport", "mode"), &RenderingServer::viewport_set_screen_space_aa);
ClassDB::bind_method(D_METHOD("viewport_set_use_taa", "viewport", "enable"), &RenderingServer::viewport_set_use_taa);
ClassDB::bind_method(D_METHOD("viewport_set_use_debanding", "viewport", "enable"), &RenderingServer::viewport_set_use_debanding);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 618ceb3091..1528a957ce 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -863,6 +863,7 @@ public:
virtual void viewport_remove_canvas(RID p_viewport, RID p_canvas) = 0;
virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0;
virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0;
+ virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0;
virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0;
virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0;
diff --git a/tests/SCsub b/tests/SCsub
index c59ce69b92..d96a1142e4 100644
--- a/tests/SCsub
+++ b/tests/SCsub
@@ -13,10 +13,8 @@ env_tests = env.Clone()
if env_tests["platform"] == "windows":
env_tests.Append(CPPDEFINES=[("DOCTEST_THREAD_LOCAL", "")])
-# Increase number of addressable sections in object files
-# due to doctest's heavy use of templates and macros.
-if env_tests.msvc:
- env_tests.Append(CCFLAGS=["/bigobj"])
+if env["disable_exceptions"]:
+ env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS"])
env_tests.add_source_files(env.tests_sources, "*.cpp")
diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h
index 1deafccde1..9bd072f511 100644
--- a/tests/core/config/test_project_settings.h
+++ b/tests/core/config/test_project_settings.h
@@ -32,9 +32,17 @@
#define TEST_PROJECT_SETTINGS_H
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/variant/variant.h"
#include "tests/test_macros.h"
+class TestProjectSettingsInternalsAccessor {
+public:
+ static String &resource_path() {
+ return ProjectSettings::get_singleton()->resource_path;
+ };
+};
+
namespace TestProjectSettings {
TEST_CASE("[ProjectSettings] Get existing setting") {
@@ -97,6 +105,58 @@ TEST_CASE("[ProjectSettings] Set value should be returned when retrieved") {
CHECK(ProjectSettings::get_singleton()->has_setting("my_custom_setting"));
}
+TEST_CASE("[ProjectSettings] localize_path") {
+ String old_resource_path = TestProjectSettingsInternalsAccessor::resource_path();
+ TestProjectSettingsInternalsAccessor::resource_path() = DirAccess::create(DirAccess::ACCESS_FILESYSTEM)->get_current_dir();
+ String root_path = ProjectSettings::get_singleton()->get_resource_path();
+#ifdef WINDOWS_ENABLED
+ String root_path_win = ProjectSettings::get_singleton()->get_resource_path().replace("/", "\\");
+#endif
+
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("filename"), "res://filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/something/../filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/./filename"), "res://path/filename");
+#ifdef WINDOWS_ENABLED
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\something\\..\\filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename");
+#endif
+
+ // FIXME?: These checks pass, but that doesn't seems correct
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename");
+
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/something/../filename"), "/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/./filename"), "/testroot/path/filename");
+#ifdef WINDOWS_ENABLED
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/filename"), "C:/testroot/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/filename"), "C:/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/something/../filename"), "C:/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/./filename"), "C:/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\filename"), "C:/testroot/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\filename"), "C:/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\something\\..\\filename"), "C:/testroot/path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\.\\filename"), "C:/testroot/path/filename");
+#endif
+
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/filename"), "res://filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/something/../filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/./filename"), "res://path/filename");
+#ifdef WINDOWS_ENABLED
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\filename"), "res://filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\something\\..\\filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\.\\filename"), "res://path/filename");
+#endif
+
+ TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path;
+}
+
} // namespace TestProjectSettings
#endif // TEST_PROJECT_SETTINGS_H
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 92ab166ae8..1897971113 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -262,9 +262,7 @@ TEST_CASE("[Image] Modifying pixels of an image") {
for (const Rect2i &rect : rects) {
Ref<Image> img = memnew(Image(img_width, img_height, false, Image::FORMAT_RGBA8));
- CHECK_NOTHROW_MESSAGE(
- img->fill_rect(rect, Color(1, 1, 1, 1)),
- "fill_rect() shouldn't throw for any rect.");
+ img->fill_rect(rect, Color(1, 1, 1, 1));
for (int y = 0; y < img->get_height(); y++) {
for (int x = 0; x < img->get_width(); x++) {
if (rect.abs().has_point(Point2(x, y))) {
@@ -317,7 +315,7 @@ TEST_CASE("[Image] Modifying pixels of an image") {
// Pre-multiply Alpha then Convert from RGBA to L8, checking alpha
{
Ref<Image> gray_image = memnew(Image(3, 3, false, Image::FORMAT_RGBA8));
- CHECK_NOTHROW_MESSAGE(gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0)), "fill_rect() shouldn't throw for any rect.");
+ gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0));
gray_image->set_pixel(1, 1, Color(1, 1, 1, 1));
gray_image->set_pixel(1, 2, Color(0.5, 0.5, 0.5, 0.5));
gray_image->set_pixel(2, 1, Color(0.25, 0.05, 0.5, 1.0));
diff --git a/tests/core/io/test_json.h b/tests/core/io/test_json.h
index 73c7ac7a2e..bf2ed42740 100644
--- a/tests/core/io/test_json.h
+++ b/tests/core/io/test_json.h
@@ -186,20 +186,21 @@ TEST_CASE("[JSON] Parsing escape sequences") {
}
SUBCASE("Valid unicode escape sequences") {
- String json_string = "\"\\u0000\"";
+ String json_string = "\"\\u0020\"";
json.parse(json_string);
CHECK_MESSAGE(
json.get_error_line() == 0,
- vformat("Parsing valid unicode escape sequence with value `0000` as JSON should parse successfully."));
+ vformat("Parsing valid unicode escape sequence with value `0020` as JSON should parse successfully."));
String json_value = json.get_data();
CHECK_MESSAGE(
- json_value == "\0",
- vformat("Parsing valid unicode escape sequence with value `0000` as JSON should return the expected value."));
+ json_value == " ",
+ vformat("Parsing valid unicode escape sequence with value `0020` as JSON should return the expected value."));
}
SUBCASE("Invalid escape sequences") {
+ ERR_PRINT_OFF
for (char32_t i = 0; i < 128; i++) {
bool skip = false;
for (int j = 0; j < valid_escapes.size(); j++) {
@@ -228,6 +229,7 @@ TEST_CASE("[JSON] Parsing escape sequences") {
err == ERR_PARSE_ERROR,
vformat("Parsing invalid escape sequence with ASCII value `%d` as JSON should fail to parse with ERR_PARSE_ERROR.", i));
}
+ ERR_PRINT_ON
}
}
} // namespace TestJSON
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index 3f091fd2fe..5f7de11c71 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -409,9 +409,6 @@ void validate_method(const Context &p_context, const ExposedClass &p_class, cons
if (p_method.return_type.name != StringName()) {
const ExposedClass *return_class = p_context.find_exposed_class(p_method.return_type);
if (return_class) {
- TEST_COND(return_class->is_singleton,
- "Method return type is a singleton: '", p_class.name, ".", p_method.name, "'.");
-
if (p_class.api_type == ClassDB::API_CORE) {
TEST_COND(return_class->api_type == ClassDB::API_EDITOR,
"Method '", p_class.name, ".", p_method.name, "' has return type '", return_class->name,
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 93b237788d..659fb003d3 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -1372,8 +1372,8 @@ TEST_CASE("[String] Ensuring empty string into parse_utf8 passes empty string")
}
TEST_CASE("[String] Cyrillic to_lower()") {
- String upper = String::utf8("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ");
- String lower = String::utf8("абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
+ String upper = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
+ String lower = U"абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
String test = upper.to_lower();
@@ -1609,6 +1609,11 @@ TEST_CASE("[String] Repeat") {
CHECK(t == s);
}
+TEST_CASE("[String] Reverse") {
+ String s = "Abcd";
+ CHECK(s.reverse() == "dcbA");
+}
+
TEST_CASE("[String] SHA1/SHA256/MD5") {
String s = "Godot";
String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201";
@@ -1700,7 +1705,7 @@ TEST_CASE("[String] validate_identifier") {
String name_with_spaces = "Name with spaces";
CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces");
- String name_with_invalid_chars = String::utf8("Invalid characters:@*#&世界");
+ String name_with_invalid_chars = U"Invalid characters:@*#&世界";
CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______");
}
diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h
new file mode 100644
index 0000000000..2c20574309
--- /dev/null
+++ b/tests/core/string/test_translation_server.h
@@ -0,0 +1,136 @@
+/**************************************************************************/
+/* test_translation_server.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_TRANSLATION_SERVER_H
+#define TEST_TRANSLATION_SERVER_H
+
+#include "core/string/translation.h"
+
+#include "tests/test_macros.h"
+
+namespace TestTranslationServer {
+TEST_CASE("[TranslationServer] Translation operations") {
+ Ref<Translation> t = memnew(Translation);
+ t->set_locale("uk");
+ t->add_message("Good Morning", String::utf8("Добрий ранок"));
+
+ TranslationServer *ts = TranslationServer::get_singleton();
+
+ int l_count_before = ts->get_loaded_locales().size();
+ ts->add_translation(t);
+ int l_count_after = ts->get_loaded_locales().size();
+ // Newly created Translation object should be added to the list, so the counter should increase, too.
+ CHECK(l_count_after > l_count_before);
+
+ Ref<Translation> trans = ts->get_translation_object("uk");
+ CHECK(trans.is_valid());
+
+ ts->set_locale("uk");
+ CHECK(ts->translate("Good Morning") == String::utf8("Добрий ранок"));
+
+ ts->remove_translation(t);
+ trans = ts->get_translation_object("uk");
+ CHECK(trans.is_null());
+ // If no suitable Translation object has been found - the original message should be returned.
+ CHECK(ts->translate("Good Morning") == "Good Morning");
+}
+
+TEST_CASE("[TranslationServer] Locale operations") {
+ TranslationServer *ts = TranslationServer::get_singleton();
+
+ // Language variant test; we supplied the variant of Español and the result should be the same string.
+ String loc = "es_Hani_ES_tradnl";
+ String res = ts->standardize_locale(loc);
+
+ CHECK(res == loc);
+
+ // No such variant in variant_map; should return everything except the variant.
+ loc = "es_Hani_ES_missing";
+ res = ts->standardize_locale(loc);
+
+ CHECK(res == "es_Hani_ES");
+
+ // Non-ISO language name check (Windows issue).
+ loc = "iw_Hani_IL";
+ res = ts->standardize_locale(loc);
+
+ CHECK(res == "he_Hani_IL");
+
+ // Country rename check.
+ loc = "uk_Hani_UK";
+ res = ts->standardize_locale(loc);
+
+ CHECK(res == "uk_Hani_GB");
+
+ // Supplying a script name that is not in the list.
+ loc = "de_Wrong_DE";
+ res = ts->standardize_locale(loc);
+
+ CHECK(res == "de_DE");
+}
+
+TEST_CASE("[TranslationServer] Comparing locales") {
+ TranslationServer *ts = TranslationServer::get_singleton();
+
+ String locale_a = "es";
+ String locale_b = "es";
+
+ // Exact match check.
+ int res = ts->compare_locales(locale_a, locale_b);
+
+ CHECK(res == 10);
+
+ locale_a = "sr-Latn-CS";
+ locale_b = "sr-Latn-RS";
+
+ // Two elements from locales match.
+ res = ts->compare_locales(locale_a, locale_b);
+
+ CHECK(res == 2);
+
+ locale_a = "uz-Cyrl-UZ";
+ locale_b = "uz-Latn-UZ";
+
+ // Two elements match, but they are not sequentual.
+ res = ts->compare_locales(locale_a, locale_b);
+
+ CHECK(res == 2);
+
+ locale_a = "es-EC";
+ locale_b = "fr-LU";
+
+ // No match.
+ res = ts->compare_locales(locale_a, locale_b);
+
+ CHECK(res == 0);
+}
+} // namespace TestTranslationServer
+
+#endif // TEST_TRANSLATION_SERVER_H
diff --git a/tests/scene/test_packed_scene.h b/tests/scene/test_packed_scene.h
new file mode 100644
index 0000000000..3517aba31f
--- /dev/null
+++ b/tests/scene/test_packed_scene.h
@@ -0,0 +1,155 @@
+/**************************************************************************/
+/* test_packed_scene.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_PACKED_SCENE_H
+#define TEST_PACKED_SCENE_H
+
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+namespace TestPackedScene {
+
+TEST_CASE("[PackedScene] Pack Scene and Retrieve State") {
+ // Create a scene to pack.
+ Node *scene = memnew(Node);
+ scene->set_name("TestScene");
+
+ // Pack the scene.
+ PackedScene packed_scene;
+ const Error err = packed_scene.pack(scene);
+ CHECK(err == OK);
+
+ // Retrieve the packed state.
+ Ref<SceneState> state = packed_scene.get_state();
+ CHECK(state.is_valid());
+ CHECK(state->get_node_count() == 1);
+ CHECK(state->get_node_name(0) == "TestScene");
+
+ memdelete(scene);
+}
+
+TEST_CASE("[PackedScene] Clear Packed Scene") {
+ // Create a scene to pack.
+ Node *scene = memnew(Node);
+ scene->set_name("TestScene");
+
+ // Pack the scene.
+ PackedScene packed_scene;
+ packed_scene.pack(scene);
+
+ // Clear the packed scene.
+ packed_scene.clear();
+
+ // Check if it has been cleared.
+ Ref<SceneState> state = packed_scene.get_state();
+ CHECK_FALSE(state->get_node_count() == 1);
+
+ memdelete(scene);
+}
+
+TEST_CASE("[PackedScene] Can Instantiate Packed Scene") {
+ // Create a scene to pack.
+ Node *scene = memnew(Node);
+ scene->set_name("TestScene");
+
+ // Pack the scene.
+ PackedScene packed_scene;
+ packed_scene.pack(scene);
+
+ // Check if the packed scene can be instantiated.
+ const bool can_instantiate = packed_scene.can_instantiate();
+ CHECK(can_instantiate == true);
+
+ memdelete(scene);
+}
+
+TEST_CASE("[PackedScene] Instantiate Packed Scene") {
+ // Create a scene to pack.
+ Node *scene = memnew(Node);
+ scene->set_name("TestScene");
+
+ // Pack the scene.
+ PackedScene packed_scene;
+ packed_scene.pack(scene);
+
+ // Instantiate the packed scene.
+ Node *instance = packed_scene.instantiate();
+ CHECK(instance != nullptr);
+ CHECK(instance->get_name() == "TestScene");
+
+ memdelete(scene);
+ memdelete(instance);
+}
+
+TEST_CASE("[PackedScene] Instantiate Packed Scene With Children") {
+ // Create a scene to pack.
+ Node *scene = memnew(Node);
+ scene->set_name("TestScene");
+
+ // Add persisting child nodes to the scene.
+ Node *child1 = memnew(Node);
+ child1->set_name("Child1");
+ scene->add_child(child1);
+ child1->set_owner(scene);
+
+ Node *child2 = memnew(Node);
+ child2->set_name("Child2");
+ scene->add_child(child2);
+ child2->set_owner(scene);
+
+ // Add non persisting child node to the scene.
+ Node *child3 = memnew(Node);
+ child3->set_name("Child3");
+ scene->add_child(child3);
+
+ // Pack the scene.
+ PackedScene packed_scene;
+ packed_scene.pack(scene);
+
+ // Instantiate the packed scene.
+ Node *instance = packed_scene.instantiate();
+ CHECK(instance != nullptr);
+ CHECK(instance->get_name() == "TestScene");
+
+ // Validate the child nodes of the instantiated scene.
+ CHECK(instance->get_child_count() == 2);
+ CHECK(instance->get_child(0)->get_name() == "Child1");
+ CHECK(instance->get_child(1)->get_name() == "Child2");
+ CHECK(instance->get_child(0)->get_owner() == instance);
+ CHECK(instance->get_child(1)->get_owner() == instance);
+
+ memdelete(scene);
+ memdelete(instance);
+}
+
+} // namespace TestPackedScene
+
+#endif // TEST_PACKED_SCENE_H
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 345e617285..8cfb189370 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -407,7 +407,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_line() == 1);
SIGNAL_CHECK("caret_changed", empty_signal_args);
- // insert before should move caret and selecion, and works when not editable.
+ // Insert before should move caret and selection, and works when not editable.
text_edit->set_editable(false);
lines_edited_args.remove_at(0);
text_edit->insert_line_at(0, "new");
@@ -424,7 +424,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
- // can undo/redo as single action
+ // Can undo/redo as single action.
((Array)lines_edited_args[0])[0] = 1;
((Array)lines_edited_args[0])[1] = 0;
text_edit->undo();
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index f0fdf655c0..dd4786977e 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -31,10 +31,13 @@
#ifndef TEST_VIEWPORT_H
#define TEST_VIEWPORT_H
-#include "scene/2d/node_2d.h"
+#include "scene/2d/area_2d.h"
+#include "scene/2d/collision_shape_2d.h"
#include "scene/gui/control.h"
#include "scene/gui/subviewport_container.h"
+#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
+#include "scene/resources/rectangle_shape_2d.h"
#include "tests/test_macros.h"
@@ -223,7 +226,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
SUBCASE("[Viewport][GuiInputEvent] nullptr as argument doesn't lead to a crash.") {
ERR_PRINT_OFF;
- CHECK_NOTHROW(root->push_input(nullptr));
+ root->push_input(nullptr);
ERR_PRINT_ON;
}
@@ -756,6 +759,408 @@ TEST_CASE("[SceneTree][Viewport] Control mouse cursor shape") {
}
}
+class TestArea2D : public Area2D {
+ GDCLASS(TestArea2D, Area2D);
+
+ void _on_mouse_entered() {
+ enter_id = ++TestArea2D::counter; // > 0, if activated.
+ }
+
+ void _on_mouse_exited() {
+ exit_id = ++TestArea2D::counter; // > 0, if activated.
+ }
+
+ void _on_input_event(Node *p_vp, Ref<InputEvent> p_ev, int p_shape) {
+ last_input_event = p_ev;
+ }
+
+public:
+ static int counter;
+ int enter_id = 0;
+ int exit_id = 0;
+ Ref<InputEvent> last_input_event;
+
+ void init_signals() {
+ connect(SNAME("mouse_entered"), callable_mp(this, &TestArea2D::_on_mouse_entered));
+ connect(SNAME("mouse_exited"), callable_mp(this, &TestArea2D::_on_mouse_exited));
+ connect(SNAME("input_event"), callable_mp(this, &TestArea2D::_on_input_event));
+ }
+
+ void test_reset() {
+ enter_id = 0;
+ exit_id = 0;
+ last_input_event.unref();
+ }
+};
+
+int TestArea2D::counter = 0;
+
+TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
+ // FIXME: MOUSE_MODE_CAPTURED if-conditions are not testable, because DisplayServerMock doesn't support it.
+
+ struct PickingCollider {
+ TestArea2D *a;
+ CollisionShape2D *c;
+ Ref<RectangleShape2D> r;
+ };
+
+ SceneTree *tree = SceneTree::get_singleton();
+ Window *root = tree->get_root();
+ root->set_physics_object_picking(true);
+
+ Point2i on_background = Point2i(800, 800);
+ Point2i on_outside = Point2i(-1, -1);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ Vector<PickingCollider> v;
+ for (int i = 0; i < 4; i++) {
+ PickingCollider pc;
+ pc.a = memnew(TestArea2D);
+ pc.c = memnew(CollisionShape2D);
+ pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D));
+ pc.r->set_size(Size2(150, 150));
+ pc.c->set_shape(pc.r);
+ pc.a->add_child(pc.c);
+ pc.a->set_name("A" + itos(i));
+ pc.c->set_name("C" + itos(i));
+ v.push_back(pc);
+ SIGNAL_WATCH(pc.a, SNAME("mouse_entered"));
+ SIGNAL_WATCH(pc.a, SNAME("mouse_exited"));
+ }
+
+ Node2D *node_a = memnew(Node2D);
+ node_a->set_position(Point2i(0, 0));
+ v[0].a->set_position(Point2i(0, 0));
+ v[1].a->set_position(Point2i(0, 100));
+ node_a->add_child(v[0].a);
+ node_a->add_child(v[1].a);
+ Node2D *node_b = memnew(Node2D);
+ node_b->set_position(Point2i(100, 0));
+ v[2].a->set_position(Point2i(0, 0));
+ v[3].a->set_position(Point2i(0, 100));
+ node_b->add_child(v[2].a);
+ node_b->add_child(v[3].a);
+ root->add_child(node_a);
+ root->add_child(node_b);
+ Point2i on_all = Point2i(50, 50);
+ Point2i on_0 = Point2i(10, 10);
+ Point2i on_01 = Point2i(10, 50);
+ Point2i on_02 = Point2i(50, 10);
+
+ Array empty_signal_args_2;
+ empty_signal_args_2.push_back(Array());
+ empty_signal_args_2.push_back(Array());
+
+ Array empty_signal_args_4;
+ empty_signal_args_4.push_back(Array());
+ empty_signal_args_4.push_back(Array());
+ empty_signal_args_4.push_back(Array());
+ empty_signal_args_4.push_back(Array());
+
+ for (PickingCollider E : v) {
+ E.a->init_signals();
+ }
+
+ SUBCASE("[Viewport][Picking2D] Mouse Motion") {
+ SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ SIGNAL_CHECK(SNAME("mouse_entered"), empty_signal_args_4);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ for (PickingCollider E : v) {
+ CHECK(E.a->enter_id);
+ CHECK_FALSE(E.a->exit_id);
+ E.a->test_reset();
+ }
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_01, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ if (i < 2) {
+ CHECK_FALSE(v[i].a->exit_id);
+ } else {
+ CHECK(v[i].a->exit_id);
+ }
+ v[i].a->test_reset();
+ }
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ if (i < 2) {
+ CHECK(v[i].a->exit_id);
+ } else {
+ CHECK_FALSE(v[i].a->exit_id);
+ }
+ v[i].a->test_reset();
+ }
+ }
+
+ SUBCASE("[Viewport][Picking2D] Object moved / passive hovering") {
+ SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ for (int i = 0; i < v.size(); i++) {
+ CHECK(v[i].a->enter_id);
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+
+ node_b->set_position(Point2i(200, 0));
+ tree->physics_process(1);
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ if (i < 2) {
+ CHECK_FALSE(v[i].a->exit_id);
+ } else {
+ CHECK(v[i].a->exit_id);
+ }
+ v[i].a->test_reset();
+ }
+
+ node_b->set_position(Point2i(100, 0));
+ tree->physics_process(1);
+ for (int i = 0; i < v.size(); i++) {
+ if (i < 2) {
+ CHECK_FALSE(v[i].a->enter_id);
+ } else {
+ CHECK(v[i].a->enter_id);
+ }
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+ }
+
+ SUBCASE("[Viewport][Picking2D] No Processing") {
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ for (PickingCollider E : v) {
+ E.a->test_reset();
+ }
+
+ v[0].a->set_process_mode(Node::PROCESS_MODE_DISABLED);
+ v[0].c->set_process_mode(Node::PROCESS_MODE_DISABLED);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ CHECK_FALSE(v[0].a->enter_id);
+ CHECK_FALSE(v[0].a->exit_id);
+ CHECK(v[2].a->enter_id);
+ CHECK_FALSE(v[2].a->exit_id);
+ for (PickingCollider E : v) {
+ E.a->test_reset();
+ }
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+ CHECK_FALSE(v[0].a->enter_id);
+ CHECK_FALSE(v[0].a->exit_id);
+ CHECK_FALSE(v[2].a->enter_id);
+ CHECK(v[2].a->exit_id);
+
+ for (PickingCollider E : v) {
+ E.a->test_reset();
+ }
+ v[0].a->set_process_mode(Node::PROCESS_MODE_ALWAYS);
+ v[0].c->set_process_mode(Node::PROCESS_MODE_ALWAYS);
+ }
+
+ SUBCASE("[Viewport][Picking2D] Multiple events in series") {
+ SEND_GUI_MOUSE_MOTION_EVENT(on_0, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_0 + Point2i(10, 0), MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ if (i < 1) {
+ CHECK(v[i].a->enter_id);
+ } else {
+ CHECK_FALSE(v[i].a->enter_id);
+ }
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background + Point2i(10, 10), MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ if (i < 1) {
+ CHECK(v[i].a->exit_id);
+ } else {
+ CHECK_FALSE(v[i].a->exit_id);
+ }
+ v[i].a->test_reset();
+ }
+ }
+
+ SUBCASE("[Viewport][Picking2D] Disable Picking") {
+ SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
+
+ root->set_physics_object_picking(false);
+ CHECK_FALSE(root->get_physics_object_picking());
+
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ v[i].a->test_reset();
+ }
+
+ root->set_physics_object_picking(true);
+ CHECK(root->get_physics_object_picking());
+ }
+
+ SUBCASE("[Viewport][Picking2D] CollisionObject in CanvasLayer") {
+ CanvasLayer *node_c = memnew(CanvasLayer);
+ node_c->set_rotation(Math_PI);
+ node_c->set_offset(Point2i(100, 100));
+ root->add_child(node_c);
+
+ v[2].a->reparent(node_c, false);
+ v[3].a->reparent(node_c, false);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ if (i == 0 || i == 3) {
+ CHECK(v[i].a->enter_id);
+ } else {
+ CHECK_FALSE(v[i].a->enter_id);
+ }
+ v[i].a->test_reset();
+ }
+
+ v[2].a->reparent(node_b, false);
+ v[3].a->reparent(node_b, false);
+ root->remove_child(node_c);
+ memdelete(node_c);
+ }
+
+ SUBCASE("[Viewport][Picking2D] Picking Sort") {
+ root->set_physics_object_picking_sort(true);
+ CHECK(root->get_physics_object_picking_sort());
+
+ SUBCASE("[Viewport][Picking2D] Picking Sort Z-Index") {
+ node_a->set_z_index(10);
+ v[0].a->set_z_index(0);
+ v[1].a->set_z_index(2);
+ node_b->set_z_index(5);
+ v[2].a->set_z_index(8);
+ v[3].a->set_z_index(11);
+ v[3].a->set_z_as_relative(false);
+
+ TestArea2D::counter = 0;
+ SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ CHECK(v[0].a->enter_id == 4);
+ CHECK(v[1].a->enter_id == 2);
+ CHECK(v[2].a->enter_id == 1);
+ CHECK(v[3].a->enter_id == 3);
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+
+ TestArea2D::counter = 0;
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ CHECK(v[0].a->exit_id == 4);
+ CHECK(v[1].a->exit_id == 2);
+ CHECK(v[2].a->exit_id == 1);
+ CHECK(v[3].a->exit_id == 3);
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ v[i].a->set_z_as_relative(true);
+ v[i].a->set_z_index(0);
+ v[i].a->test_reset();
+ }
+
+ node_a->set_z_index(0);
+ node_b->set_z_index(0);
+ }
+
+ SUBCASE("[Viewport][Picking2D] Picking Sort Scene Tree Location") {
+ TestArea2D::counter = 0;
+ SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK(v[i].a->enter_id == 4 - i);
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+
+ TestArea2D::counter = 0;
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ CHECK(v[i].a->exit_id == 4 - i);
+ v[i].a->test_reset();
+ }
+ }
+
+ root->set_physics_object_picking_sort(false);
+ CHECK_FALSE(root->get_physics_object_picking_sort());
+ }
+
+ SUBCASE("[Viewport][Picking2D] Mouse Button") {
+ SEND_GUI_MOUSE_BUTTON_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ if (i == 0) {
+ CHECK(v[i].a->enter_id);
+ } else {
+ CHECK_FALSE(v[i].a->enter_id);
+ }
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ tree->physics_process(1);
+
+ for (int i = 0; i < v.size(); i++) {
+ CHECK_FALSE(v[i].a->enter_id);
+ CHECK_FALSE(v[i].a->exit_id);
+ v[i].a->test_reset();
+ }
+ }
+
+ SUBCASE("[Viewport][Picking2D] Screen Touch") {
+ SEND_GUI_TOUCH_EVENT(on_01, true, false);
+ tree->physics_process(1);
+ for (int i = 0; i < v.size(); i++) {
+ if (i < 2) {
+ Ref<InputEventScreenTouch> st = v[i].a->last_input_event;
+ CHECK(st.is_valid());
+ } else {
+ CHECK(v[i].a->last_input_event.is_null());
+ }
+ v[i].a->test_reset();
+ }
+ }
+
+ for (PickingCollider E : v) {
+ SIGNAL_UNWATCH(E.a, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(E.a, SNAME("mouse_exited"));
+ memdelete(E.c);
+ memdelete(E.a);
+ }
+}
+
TEST_CASE("[SceneTree][Viewport] Embedded Windows") {
Window *root = SceneTree::get_singleton()->get_root();
Window *w = memnew(Window);
diff --git a/tests/scene/test_window.h b/tests/scene/test_window.h
new file mode 100644
index 0000000000..e0c55101de
--- /dev/null
+++ b/tests/scene/test_window.h
@@ -0,0 +1,96 @@
+/**************************************************************************/
+/* test_window.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_WINDOW_H
+#define TEST_WINDOW_H
+
+#include "scene/gui/control.h"
+#include "scene/main/window.h"
+
+#include "tests/test_macros.h"
+
+namespace TestWindow {
+
+class NotificationControl : public Control {
+ GDCLASS(NotificationControl, Control);
+
+protected:
+ void _notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_MOUSE_ENTER: {
+ mouse_over = true;
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ mouse_over = false;
+ } break;
+ }
+ }
+
+public:
+ bool mouse_over = false;
+};
+
+TEST_CASE("[SceneTree][Window]") {
+ Window *root = SceneTree::get_singleton()->get_root();
+
+ SUBCASE("Control-mouse-over within Window-black bars should not happen") {
+ Window *w = memnew(Window);
+ root->add_child(w);
+ w->set_size(Size2i(400, 200));
+ w->set_position(Size2i(0, 0));
+ w->set_content_scale_size(Size2i(200, 200));
+ w->set_content_scale_mode(Window::CONTENT_SCALE_MODE_CANVAS_ITEMS);
+ w->set_content_scale_aspect(Window::CONTENT_SCALE_ASPECT_KEEP);
+ NotificationControl *c = memnew(NotificationControl);
+ w->add_child(c);
+ c->set_size(Size2i(100, 100));
+ c->set_position(Size2i(-50, -50));
+
+ CHECK_FALSE(c->mouse_over);
+ SEND_GUI_MOUSE_MOTION_EVENT(Point2i(110, 10), MouseButtonMask::NONE, Key::NONE);
+ CHECK(c->mouse_over);
+ SEND_GUI_MOUSE_MOTION_EVENT(Point2i(90, 10), MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(c->mouse_over); // GH-80011
+
+ /* TODO:
+ SEND_GUI_MOUSE_BUTTON_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK(Control was not pressed);
+ */
+
+ memdelete(c);
+ memdelete(w);
+ }
+}
+
+} // namespace TestWindow
+
+#endif // TEST_WINDOW_H
diff --git a/tests/servers/rendering/test_shader_preprocessor.h b/tests/servers/rendering/test_shader_preprocessor.h
new file mode 100644
index 0000000000..d65eb522e8
--- /dev/null
+++ b/tests/servers/rendering/test_shader_preprocessor.h
@@ -0,0 +1,333 @@
+/**************************************************************************/
+/* test_shader_preprocessor.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_SHADER_PREPROCESSOR_H
+#define TEST_SHADER_PREPROCESSOR_H
+
+#include "servers/rendering/shader_preprocessor.h"
+
+#include "tests/test_macros.h"
+
+#include <cctype>
+
+namespace TestShaderPreprocessor {
+
+void erase_all_empty(Vector<String> &p_vec) {
+ int idx = p_vec.find(" ");
+ while (idx >= 0) {
+ p_vec.remove_at(idx);
+ idx = p_vec.find(" ");
+ }
+}
+
+bool is_variable_char(unsigned char c) {
+ return std::isalnum(c) || c == '_';
+}
+
+bool is_operator_char(unsigned char c) {
+ return (c == '*') || (c == '+') || (c == '-') || (c == '/') || ((c >= '<') && (c <= '>'));
+}
+
+// Remove unnecessary spaces from a line.
+String remove_spaces(String &p_str) {
+ String res;
+ // Result is guaranteed to not be longer than the input.
+ res.resize(p_str.size());
+ int wp = 0;
+ char32_t last = 0;
+ bool has_removed = false;
+
+ for (int n = 0; n < p_str.size(); n++) {
+ // These test cases only use ASCII.
+ auto c = static_cast<unsigned char>(p_str[n]);
+ if (std::isblank(c)) {
+ has_removed = true;
+ } else {
+ if (has_removed) {
+ // Insert a space to avoid joining things that could potentially form a new token.
+ // E.g. "float x" or "- -".
+ if ((is_variable_char(c) && is_variable_char(last)) ||
+ (is_operator_char(c) && is_operator_char(last))) {
+ res[wp++] = ' ';
+ }
+ has_removed = false;
+ }
+ res[wp++] = c;
+ last = c;
+ }
+ }
+ res.resize(wp);
+ return res;
+}
+
+// The pre-processor changes indentation and inserts spaces when inserting macros.
+// Re-format the code, without changing its meaning, to make it easier to compare.
+String compact_spaces(String &p_str) {
+ Vector<String> lines = p_str.split("\n", false);
+ erase_all_empty(lines);
+ for (auto &line : lines) {
+ line = remove_spaces(line);
+ }
+ return String("\n").join(lines);
+}
+
+#define CHECK_SHADER_EQ(a, b) CHECK_EQ(compact_spaces(a), compact_spaces(b))
+#define CHECK_SHADER_NE(a, b) CHECK_NE(compact_spaces(a), compact_spaces(b))
+
+TEST_CASE("[ShaderPreprocessor] Simple defines") {
+ String code(
+ "#define X 1.0 // comment\n"
+ "#define Y mix\n"
+ "#define Z X\n"
+ "\n"
+ "#define func0 \\\n"
+ " vec3 my_fun(vec3 arg) {\\\n"
+ " return pow(arg, 2.2);\\\n"
+ " }\n"
+ "\n"
+ "func0\n"
+ "\n"
+ "fragment() {\n"
+ " ALBEDO = vec3(X);\n"
+ " float x = Y(0., Z, X);\n"
+ " #undef X\n"
+ " float X = x;\n"
+ " x = -Z;\n"
+ "}\n");
+ String expected(
+ "vec3 my_fun(vec3 arg) { return pow(arg, 2.2); }\n"
+ "\n"
+ "fragment() {\n"
+ " ALBEDO = vec3( 1.0 );\n"
+ " float x = mix(0., 1.0 , 1.0 );\n"
+ " float X = x;\n"
+ " x = -X;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Avoid merging adjacent tokens") {
+ String code(
+ "#define X -10\n"
+ "#define Y(s) s\n"
+ "\n"
+ "fragment() {\n"
+ " float v = 1.0-X-Y(-2);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float v = 1.0 - -10 - -2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Complex defines") {
+ String code(
+ "const float X = 2.0;\n"
+ "#define A(X) X*2.\n"
+ "#define X 1.0\n"
+ "#define Y Z(X, W)\n"
+ "#define Z max\n"
+ "#define C(X, Y) Z(A(Y), B(X))\n"
+ "#define W -X\n"
+ "#define B(X) X*3.\n"
+ "\n"
+ "fragment() {\n"
+ " float x = Y;\n"
+ " float y = C(5., 7.0);\n"
+ "}\n");
+ String expected(
+ "const float X = 2.0;\n"
+ "fragment() {\n"
+ " float x = max(1.0, - 1.0);\n"
+ " float y = max(7.0*2. , 5.*3.);\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Concatenation") {
+ String code(
+ "fragment() {\n"
+ " #define X 1 // this is fine ##\n"
+ " #define y 2\n"
+ " #define z 3##.## 1## 4 ## 59\n"
+ " #define Z(y) X ## y\n"
+ " #define Z2(y) y##X\n"
+ " #define W(y) X, y\n"
+ " #define A(x) fl## oat a = 1##x ##.3 ## x\n"
+ " #define C(x, y) x##.##y\n"
+ " #define J(x) x##=\n"
+ " float Z(y) = 1.2;\n"
+ " float Z(z) = 2.3;\n"
+ " float Z2(y) = z;\n"
+ " float Z2(z) = 2.3;\n"
+ " int b = max(W(3));\n"
+ " Xy J(+) b J(=) 3 ? 0.1 : 0.2;\n"
+ " A(9);\n"
+ " Xy = C(X, y);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float Xy = 1.2;\n"
+ " float Xz = 2.3;\n"
+ " float yX = 3.1459;\n"
+ " float zX = 2.3;\n"
+ " int b = max(1, 3);\n"
+ " Xy += b == 3 ? 0.1 : 0.2;\n"
+ " float a = 19.39;\n"
+ " Xy = 1.2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Nested concatenation") {
+ // Concatenation ## should not expand adjacent tokens if they are macros,
+ // but this is currently not implemented in Godot's shader preprocessor.
+ // To force expanding, an extra macro should be required (B in this case).
+
+ String code(
+ "fragment() {\n"
+ " vec2 X = vec2(0);\n"
+ " #define X 1\n"
+ " #define y 2\n"
+ " #define B(x, y) C(x, y)\n"
+ " #define C(x, y) x##.##y\n"
+ " C(X, y) = B(X, y);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " vec2 X = vec2(0);\n"
+ " X.y = 1.2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ // TODO: Reverse the check when/if this is changed.
+ CHECK_SHADER_NE(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Concatenation sorting network") {
+ String code(
+ "fragment() {\n"
+ " #define ARR(X) test##X\n"
+ " #define ACMP(a, b) ARR(a) > ARR(b)\n"
+ " #define ASWAP(a, b) tmp = ARR(b); ARR(b) = ARR(a); ARR(a) = tmp;\n"
+ " #define ACSWAP(a, b) if(ACMP(a, b)) { ASWAP(a, b) }\n"
+ " float test0 = 1.2;\n"
+ " float test1 = 0.34;\n"
+ " float test3 = 0.8;\n"
+ " float test4 = 2.9;\n"
+ " float tmp;\n"
+ " ACSWAP(0,2)\n"
+ " ACSWAP(1,3)\n"
+ " ACSWAP(0,1)\n"
+ " ACSWAP(2,3)\n"
+ " ACSWAP(1,2)\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float test0 = 1.2;\n"
+ " float test1 = 0.34;\n"
+ " float test3 = 0.8;\n"
+ " float test4 = 2.9;\n"
+ " float tmp;\n"
+ " if(test0 > test2) { tmp = test2; test2 = test0; test0 = tmp; }\n"
+ " if(test1 > test3) { tmp = test3; test3 = test1; test1 = tmp; }\n"
+ " if(test0 > test1) { tmp = test1; test1 = test0; test0 = tmp; }\n"
+ " if(test2 > test3) { tmp = test3; test3 = test2; test2 = tmp; }\n"
+ " if(test1 > test2) { tmp = test2; test2 = test1; test1 = tmp; }\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Undefined behaviour") {
+ // None of these are valid concatenation, nor valid shader code.
+ // Don't care about results, just make sure there's no crash.
+ const String filename("somefile.gdshader");
+ String result;
+ ShaderPreprocessor preprocessor;
+
+ preprocessor.preprocess("#define X ###\nX\n", filename, result);
+ preprocessor.preprocess("#define X ####\nX\n", filename, result);
+ preprocessor.preprocess("#define X #####\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 #### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ##### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X ### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X #### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X ##### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ###\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ####\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 #####\nX\n", filename, result);
+}
+
+TEST_CASE("[ShaderPreprocessor] Invalid concatenations") {
+ const String filename("somefile.gdshader");
+ String result;
+ ShaderPreprocessor preprocessor;
+
+ CHECK_NE(preprocessor.preprocess("#define X ##", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X 1 ##", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X ## 1", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) ## ", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) y ## ", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) ## y", filename, result), Error::OK);
+}
+
+} // namespace TestShaderPreprocessor
+
+#endif // TEST_SHADER_PREPROCESSOR_H
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 8ea69ec67c..a116559cb2 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -423,6 +423,7 @@ TEST_SUITE("[Navigation]") {
navigation_server->free(map);
}
+#ifndef DISABLE_DEPRECATED
// This test case uses only public APIs on purpose - other test cases use simplified baking.
// FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed.
TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") {
@@ -470,6 +471,7 @@ TEST_SUITE("[Navigation]") {
memdelete(mesh_instance);
memdelete(node_3d);
}
+#endif // DISABLE_DEPRECATED
// This test case uses only public APIs on purpose - other test cases use simplified baking.
TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") {
diff --git a/tests/test_macros.h b/tests/test_macros.h
index d39da7f8e8..bc85ec6ddc 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -177,6 +177,13 @@ int register_test_command(String p_command, TestFunc p_function);
_UPDATE_EVENT_MODIFERS(event, m_modifers); \
event->set_pressed(true);
+#define _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
+ Ref<InputEventScreenTouch> event; \
+ event.instantiate(); \
+ event->set_position(m_screen_pos); \
+ event->set_pressed(m_pressed); \
+ event->set_double_tap(m_double);
+
#define SEND_GUI_MOUSE_BUTTON_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \
{ \
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers); \
@@ -215,6 +222,13 @@ int register_test_command(String p_command, TestFunc p_function);
CoreGlobals::print_error_enabled = errors_enabled; \
}
+#define SEND_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
+ { \
+ _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
+ _SEND_DISPLAYSERVER_EVENT(event); \
+ MessageQueue::get_singleton()->flush(); \
+ }
+
// Utility class / macros for testing signals
//
// Use SIGNAL_WATCH(*object, "signal_name") to start watching
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 96ee146e77..f1e348345b 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -72,6 +72,7 @@
#include "tests/core/string/test_node_path.h"
#include "tests/core/string/test_string.h"
#include "tests/core/string/test_translation.h"
+#include "tests/core/string/test_translation_server.h"
#include "tests/core/templates/test_command_queue.h"
#include "tests/core/templates/test_hash_map.h"
#include "tests/core/templates/test_hash_set.h"
@@ -105,6 +106,7 @@
#include "tests/scene/test_navigation_region_2d.h"
#include "tests/scene/test_navigation_region_3d.h"
#include "tests/scene/test_node.h"
+#include "tests/scene/test_packed_scene.h"
#include "tests/scene/test_path_2d.h"
#include "tests/scene/test_path_3d.h"
#include "tests/scene/test_primitives.h"
@@ -113,6 +115,8 @@
#include "tests/scene/test_theme.h"
#include "tests/scene/test_viewport.h"
#include "tests/scene/test_visual_shader.h"
+#include "tests/scene/test_window.h"
+#include "tests/servers/rendering/test_shader_preprocessor.h"
#include "tests/servers/test_navigation_server_2d.h"
#include "tests/servers/test_navigation_server_3d.h"
#include "tests/servers/test_text_server.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 3a98f676b4..27705de997 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -315,7 +315,7 @@ Files extracted from upstream source:
## libpng
- Upstream: http://libpng.org/pub/png/libpng.html
-- Version: 1.6.39 (07b8803110da160b158ebfef872627da6c85cbdf, 2022)
+- Version: 1.6.40 (f135775ad4e5d4408d2e12ffcc71bb36e6b48551, 2023)
- License: libpng/zlib
Files extracted from upstream source:
@@ -356,7 +356,7 @@ Files extracted from upstream source:
## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/
-- Version: 1.3.0 (b557776962a3dcc985d83bd4ed94e1e2e50d0fa2, 2022)
+- Version: 1.3.1 (fd7bb21c0cb56e8a82e9bfa376164b842f433f3b, 2023)
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -370,7 +370,7 @@ Patch `godot-node-debug-fix.patch` workarounds shadowing of godot's Node class i
## mbedtls
- Upstream: https://github.com/Mbed-TLS/mbedtls
-- Version: 2.18.3 (981743de6fcdbe672e482b6fd724d31d0a0d2476, 2023)
+- Version: 2.28.4 (aeb97a18913a86f051afab11b2c92c6be0c2eb83, 2023)
- License: Apache 2.0
File extracted from upstream release tarball:
@@ -422,7 +422,7 @@ to solve some MSVC warnings. See the patches in the `patches` directory.
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp
-- Version: 2.2.4 (7d1d8bc3868b08ad003bad235eee57562b95b76d, 2022)
+- Version: 2.2.5 (58837ef586278d18cbebee50be758835ed4be79a, 2023)
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/libpng/LICENSE b/thirdparty/libpng/LICENSE
index c8ad24eecf..086d1c2fda 100644
--- a/thirdparty/libpng/LICENSE
+++ b/thirdparty/libpng/LICENSE
@@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
PNG Reference Library License version 2
---------------------------------------
- * Copyright (c) 1995-2022 The PNG Reference Library Authors.
- * Copyright (c) 2018-2022 Cosmin Truta.
+ * Copyright (c) 1995-2023 The PNG Reference Library Authors.
+ * Copyright (c) 2018-2023 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
diff --git a/thirdparty/libpng/png.c b/thirdparty/libpng/png.c
index 4f3e8bbd31..d6471b06cc 100644
--- a/thirdparty/libpng/png.c
+++ b/thirdparty/libpng/png.c
@@ -1,7 +1,7 @@
/* png.c - location for general purpose libpng functions
*
- * Copyright (c) 2018-2022 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -14,7 +14,7 @@
#include "pngpriv.h"
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_39 Your_png_h_is_not_version_1_6_39;
+typedef png_libpng_version_1_6_40 Your_png_h_is_not_version_1_6_40;
#ifdef __GNUC__
/* The version tests may need to be added to, but the problem warning has
@@ -815,8 +815,8 @@ png_get_copyright(png_const_structrp png_ptr)
return PNG_STRING_COPYRIGHT
#else
return PNG_STRING_NEWLINE \
- "libpng version 1.6.39" PNG_STRING_NEWLINE \
- "Copyright (c) 2018-2022 Cosmin Truta" PNG_STRING_NEWLINE \
+ "libpng version 1.6.40" PNG_STRING_NEWLINE \
+ "Copyright (c) 2018-2023 Cosmin Truta" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
diff --git a/thirdparty/libpng/png.h b/thirdparty/libpng/png.h
index f109cdf336..cfc4841099 100644
--- a/thirdparty/libpng/png.h
+++ b/thirdparty/libpng/png.h
@@ -1,9 +1,9 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.6.39 - November 20, 2022
+ * libpng version 1.6.40
*
- * Copyright (c) 2018-2022 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -15,7 +15,7 @@
* libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
* libpng versions 0.97, January 1998, through 1.6.35, July 2018:
* Glenn Randers-Pehrson
- * libpng versions 1.6.36, December 2018, through 1.6.39, November 2022:
+ * libpng versions 1.6.36, December 2018, through 1.6.40, June 2023:
* Cosmin Truta
* See also "Contributing Authors", below.
*/
@@ -27,8 +27,8 @@
* PNG Reference Library License version 2
* ---------------------------------------
*
- * * Copyright (c) 1995-2022 The PNG Reference Library Authors.
- * * Copyright (c) 2018-2022 Cosmin Truta.
+ * * Copyright (c) 1995-2023 The PNG Reference Library Authors.
+ * * Copyright (c) 2018-2023 Cosmin Truta.
* * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* * Copyright (c) 1996-1997 Andreas Dilger.
* * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -239,7 +239,7 @@
* ...
* 1.5.30 15 10530 15.so.15.30[.0]
* ...
- * 1.6.39 16 10639 16.so.16.39[.0]
+ * 1.6.40 16 10640 16.so.16.40[.0]
*
* Henceforth the source version will match the shared-library major and
* minor numbers; the shared-library major version number will be used for
@@ -278,8 +278,8 @@
*/
/* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.39"
-#define PNG_HEADER_VERSION_STRING " libpng version 1.6.39 - November 20, 2022\n"
+#define PNG_LIBPNG_VER_STRING "1.6.40"
+#define PNG_HEADER_VERSION_STRING " libpng version 1.6.40 - June 21, 2023\n"
#define PNG_LIBPNG_VER_SONUM 16
#define PNG_LIBPNG_VER_DLLNUM 16
@@ -287,7 +287,7 @@
/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
#define PNG_LIBPNG_VER_MAJOR 1
#define PNG_LIBPNG_VER_MINOR 6
-#define PNG_LIBPNG_VER_RELEASE 39
+#define PNG_LIBPNG_VER_RELEASE 40
/* This should be zero for a public release, or non-zero for a
* development version. [Deprecated]
@@ -318,7 +318,7 @@
* From version 1.0.1 it is:
* XXYYZZ, where XX=major, YY=minor, ZZ=release
*/
-#define PNG_LIBPNG_VER 10639 /* 1.6.39 */
+#define PNG_LIBPNG_VER 10640 /* 1.6.40 */
/* Library configuration: these options cannot be changed after
* the library has been built.
@@ -428,7 +428,7 @@ extern "C" {
/* This triggers a compiler error in png.c, if png.c and png.h
* do not agree upon the version number.
*/
-typedef char* png_libpng_version_1_6_39;
+typedef char* png_libpng_version_1_6_40;
/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info.
*
diff --git a/thirdparty/libpng/pngconf.h b/thirdparty/libpng/pngconf.h
index fcb4b43069..6671e3c335 100644
--- a/thirdparty/libpng/pngconf.h
+++ b/thirdparty/libpng/pngconf.h
@@ -1,7 +1,7 @@
/* pngconf.h - machine-configurable file for libpng
*
- * libpng version 1.6.39
+ * libpng version 1.6.40
*
* Copyright (c) 2018-2022 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/thirdparty/libpng/pngget.c b/thirdparty/libpng/pngget.c
index e44933c0d2..1490a032e1 100644
--- a/thirdparty/libpng/pngget.c
+++ b/thirdparty/libpng/pngget.c
@@ -1,7 +1,7 @@
/* pngget.c - retrieval of values from info struct
*
- * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -21,7 +21,18 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,
png_uint_32 flag)
{
if (png_ptr != NULL && info_ptr != NULL)
+ {
+#ifdef PNG_READ_tRNS_SUPPORTED
+ /* png_handle_PLTE() may have canceled a valid tRNS chunk but left the
+ * 'valid' flag for the detection of duplicate chunks. Do not report a
+ * valid tRNS chunk in this case.
+ */
+ if (flag == PNG_INFO_tRNS && png_ptr->num_trans == 0)
+ return(0);
+#endif
+
return(info_ptr->valid & flag);
+ }
return(0);
}
diff --git a/thirdparty/libpng/pnglibconf.h b/thirdparty/libpng/pnglibconf.h
index e5948c8ce1..c7033ae176 100644
--- a/thirdparty/libpng/pnglibconf.h
+++ b/thirdparty/libpng/pnglibconf.h
@@ -1,8 +1,8 @@
/* pnglibconf.h - library build configuration */
-/* libpng version 1.6.39 */
+/* libpng version 1.6.40 */
-/* Copyright (c) 2018-2022 Cosmin Truta */
+/* Copyright (c) 2018-2023 Cosmin Truta */
/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
/* This code is released under the libpng license. */
diff --git a/thirdparty/libpng/pngpriv.h b/thirdparty/libpng/pngpriv.h
index b8a73b685d..7c19373f0b 100644
--- a/thirdparty/libpng/pngpriv.h
+++ b/thirdparty/libpng/pngpriv.h
@@ -1,7 +1,7 @@
/* pngpriv.h - private declarations for use inside libpng
*
- * Copyright (c) 2018-2022 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -626,7 +626,7 @@
#define PNG_BACKGROUND_IS_GRAY 0x800U
#define PNG_HAVE_PNG_SIGNATURE 0x1000U
#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */
- /* 0x4000U (unused) */
+#define PNG_WROTE_eXIf 0x4000U
#define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */
/* Flags for the transformations the PNG library does on the image data */
@@ -1910,7 +1910,7 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
*/
#define PNG_FP_INVALID 512 /* Available for callers as a distinct value */
-/* Result codes for the parser (boolean - true meants ok, false means
+/* Result codes for the parser (boolean - true means ok, false means
* not ok yet.)
*/
#define PNG_FP_MAYBE 0 /* The number may be valid in the future */
diff --git a/thirdparty/libpng/pngset.c b/thirdparty/libpng/pngset.c
index 8c372cf415..3fc31feb0c 100644
--- a/thirdparty/libpng/pngset.c
+++ b/thirdparty/libpng/pngset.c
@@ -1,7 +1,7 @@
/* pngset.c - storage of image information into info struct
*
- * Copyright (c) 2018-2022 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -137,46 +137,40 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X,
#ifdef PNG_eXIf_SUPPORTED
void PNGAPI
png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
- png_bytep eXIf_buf)
+ png_bytep exif)
{
png_warning(png_ptr, "png_set_eXIf does not work; use png_set_eXIf_1");
PNG_UNUSED(info_ptr)
- PNG_UNUSED(eXIf_buf)
+ PNG_UNUSED(exif)
}
void PNGAPI
png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr,
- png_uint_32 num_exif, png_bytep eXIf_buf)
+ png_uint_32 num_exif, png_bytep exif)
{
- int i;
+ png_bytep new_exif;
png_debug1(1, "in %s storage function", "eXIf");
- if (png_ptr == NULL || info_ptr == NULL)
+ if (png_ptr == NULL || info_ptr == NULL ||
+ (png_ptr->mode & PNG_WROTE_eXIf) != 0)
return;
- if (info_ptr->exif)
- {
- png_free(png_ptr, info_ptr->exif);
- info_ptr->exif = NULL;
- }
+ new_exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, num_exif));
- info_ptr->num_exif = num_exif;
-
- info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr,
- info_ptr->num_exif));
-
- if (info_ptr->exif == NULL)
+ if (new_exif == NULL)
{
png_warning(png_ptr, "Insufficient memory for eXIf chunk data");
return;
}
- info_ptr->free_me |= PNG_FREE_EXIF;
+ memcpy(new_exif, exif, (size_t)num_exif);
- for (i = 0; i < (int) info_ptr->num_exif; i++)
- info_ptr->exif[i] = eXIf_buf[i];
+ png_free_data(png_ptr, info_ptr, PNG_FREE_EXIF, 0);
+ info_ptr->num_exif = num_exif;
+ info_ptr->exif = new_exif;
+ info_ptr->free_me |= PNG_FREE_EXIF;
info_ptr->valid |= PNG_INFO_eXIf;
}
#endif /* eXIf */
@@ -237,15 +231,13 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->hist == NULL)
{
png_warning(png_ptr, "Insufficient memory for hIST chunk data");
-
return;
}
- info_ptr->free_me |= PNG_FREE_HIST;
-
for (i = 0; i < info_ptr->num_palette; i++)
info_ptr->hist[i] = hist[i];
+ info_ptr->free_me |= PNG_FREE_HIST;
info_ptr->valid |= PNG_INFO_hIST;
}
#endif
@@ -367,6 +359,8 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
memcpy(info_ptr->pcal_purpose, purpose, length);
+ info_ptr->free_me |= PNG_FREE_PCAL;
+
png_debug(3, "storing X0, X1, type, and nparams in info");
info_ptr->pcal_X0 = X0;
info_ptr->pcal_X1 = X1;
@@ -383,7 +377,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->pcal_units == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL units");
-
return;
}
@@ -395,7 +388,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->pcal_params == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL params");
-
return;
}
@@ -413,7 +405,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->pcal_params[i] == NULL)
{
png_warning(png_ptr, "Insufficient memory for pCAL parameter");
-
return;
}
@@ -421,7 +412,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
}
info_ptr->valid |= PNG_INFO_pCAL;
- info_ptr->free_me |= PNG_FREE_PCAL;
}
#endif
@@ -478,18 +468,17 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
if (info_ptr->scal_s_height == NULL)
{
- png_free (png_ptr, info_ptr->scal_s_width);
+ png_free(png_ptr, info_ptr->scal_s_width);
info_ptr->scal_s_width = NULL;
png_warning(png_ptr, "Memory allocation failed while processing sCAL");
-
return;
}
memcpy(info_ptr->scal_s_height, sheight, lengthh);
- info_ptr->valid |= PNG_INFO_sCAL;
info_ptr->free_me |= PNG_FREE_SCAL;
+ info_ptr->valid |= PNG_INFO_sCAL;
}
# ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -625,11 +614,10 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
if (num_palette > 0)
memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
(sizeof (png_color)));
+
info_ptr->palette = png_ptr->palette;
info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
-
info_ptr->free_me |= PNG_FREE_PLTE;
-
info_ptr->valid |= PNG_INFO_PLTE;
}
@@ -1020,8 +1008,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans);
- info_ptr->valid |= PNG_INFO_tRNS;
info_ptr->free_me |= PNG_FREE_TRNS;
+ info_ptr->valid |= PNG_INFO_tRNS;
}
png_ptr->trans_alpha = info_ptr->trans_alpha;
}
@@ -1054,8 +1042,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
if (num_trans != 0)
{
- info_ptr->valid |= PNG_INFO_tRNS;
info_ptr->free_me |= PNG_FREE_TRNS;
+ info_ptr->valid |= PNG_INFO_tRNS;
}
}
#endif
@@ -1089,11 +1077,11 @@ png_set_sPLT(png_const_structrp png_ptr,
{
/* Out of memory or too many chunks */
png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR);
-
return;
}
png_free(png_ptr, info_ptr->splt_palettes);
+
info_ptr->splt_palettes = np;
info_ptr->free_me |= PNG_FREE_SPLT;
@@ -1247,11 +1235,11 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
{
png_chunk_report(png_ptr, "too many unknown chunks",
PNG_CHUNK_WRITE_ERROR);
-
return;
}
png_free(png_ptr, info_ptr->unknown_chunks);
+
info_ptr->unknown_chunks = np; /* safe because it is initialized */
info_ptr->free_me |= PNG_FREE_UNKN;
diff --git a/thirdparty/libpng/pngwrite.c b/thirdparty/libpng/pngwrite.c
index 4e58d776a9..32f4bfbe7d 100644
--- a/thirdparty/libpng/pngwrite.c
+++ b/thirdparty/libpng/pngwrite.c
@@ -1,7 +1,7 @@
/* pngwrite.c - general routines to write a PNG file
*
- * Copyright (c) 2018-2022 Cosmin Truta
+ * Copyright (c) 2018-2023 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -239,7 +239,10 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
#ifdef PNG_WRITE_eXIf_SUPPORTED
if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
+ {
png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
+ png_ptr->mode |= PNG_WROTE_eXIf;
+ }
#endif
#ifdef PNG_WRITE_hIST_SUPPORTED
@@ -439,8 +442,9 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr)
#endif
#ifdef PNG_WRITE_eXIf_SUPPORTED
- if ((info_ptr->valid & PNG_INFO_eXIf) != 0)
- png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
+ if ((info_ptr->valid & PNG_INFO_eXIf) != 0 &&
+ (png_ptr->mode & PNG_WROTE_eXIf) == 0)
+ png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif);
#endif
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS
index 2f0c537d1c..8359b20da9 100644
--- a/thirdparty/libwebp/AUTHORS
+++ b/thirdparty/libwebp/AUTHORS
@@ -32,6 +32,7 @@ Contributors:
- Mislav Bradac (mislavm at google dot com)
- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org)
+- Nozomi Isozaki (nontan at pixiv dot co dot jp)
- Oliver Wolff (oliver dot wolff at qt dot io)
- Owen Rodley (orodley at google dot com)
- Parag Salasakar (img dot mips1 at gmail dot com)
@@ -47,6 +48,7 @@ Contributors:
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
- Sriraman Tallam (tmsriram at google dot com)
- Tamar Levy (tamar dot levy at intel dot com)
+- Thiago Perrotta (tperrotta at google dot com)
- Timothy Gu (timothygu99 at gmail dot com)
- Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com)
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.c b/thirdparty/libwebp/sharpyuv/sharpyuv.c
index 7de34fb0b2..a074564888 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv.c
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv.c
@@ -440,6 +440,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
// users can declare it as extern and call it with an alternate VP8CPUInfo
// function.
+extern VP8CPUInfo SharpYuvGetCPUInfo;
SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
void SharpYuvInit(VP8CPUInfo cpu_info_func) {
static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.h b/thirdparty/libwebp/sharpyuv/sharpyuv.h
index 181b20a0bc..7b9904d6f9 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv.h
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv.h
@@ -37,7 +37,7 @@ extern "C" {
// SharpYUV API version following the convention from semver.org
#define SHARPYUV_VERSION_MAJOR 0
#define SHARPYUV_VERSION_MINOR 2
-#define SHARPYUV_VERSION_PATCH 0
+#define SHARPYUV_VERSION_PATCH 1
// Version as a uint32_t. The major number is the high 8 bits.
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
index 31c272c408..0da3efc0b8 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
@@ -72,6 +72,7 @@ void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
const uint16_t* best_y, uint16_t* out,
int bit_depth);
+extern VP8CPUInfo SharpYuvGetCPUInfo;
extern void InitSharpYuvSSE2(void);
extern void InitSharpYuvNEON(void);
diff --git a/thirdparty/libwebp/src/dec/tree_dec.c b/thirdparty/libwebp/src/dec/tree_dec.c
index 1c6fdea27c..2434605953 100644
--- a/thirdparty/libwebp/src/dec/tree_dec.c
+++ b/thirdparty/libwebp/src/dec/tree_dec.c
@@ -12,10 +12,11 @@
// Author: Skal (pascal.massimino@gmail.com)
#include "src/dec/vp8i_dec.h"
+#include "src/dsp/cpu.h"
#include "src/utils/bit_reader_inl_utils.h"
#if !defined(USE_GENERIC_TREE)
-#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
+#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
#else
diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c
index 2003935ec4..20b92e84c4 100644
--- a/thirdparty/libwebp/src/dec/vp8_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8_dec.c
@@ -494,6 +494,8 @@ static int GetCoeffsAlt(VP8BitReader* const br,
return 16;
}
+extern VP8CPUInfo VP8GetCPUInfo;
+
WEBP_DSP_INIT_FUNC(InitGetCoeffs) {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
GetCoeffs = GetCoeffsAlt;
diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h
index 83791ecd25..1ae4ff62f2 100644
--- a/thirdparty/libwebp/src/dec/vp8i_dec.h
+++ b/thirdparty/libwebp/src/dec/vp8i_dec.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
#define DEC_MIN_VERSION 3
-#define DEC_REV_VERSION 0
+#define DEC_REV_VERSION 1
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
diff --git a/thirdparty/libwebp/src/dec/webp_dec.c b/thirdparty/libwebp/src/dec/webp_dec.c
index 3f4f7bb659..f557868b99 100644
--- a/thirdparty/libwebp/src/dec/webp_dec.c
+++ b/thirdparty/libwebp/src/dec/webp_dec.c
@@ -658,19 +658,26 @@ uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height, uint8_t** u, uint8_t** v,
int* stride, int* uv_stride) {
- WebPDecBuffer output; // only to preserve the side-infos
- uint8_t* const out = Decode(MODE_YUV, data, data_size,
- width, height, &output);
-
- if (out != NULL) {
- const WebPYUVABuffer* const buf = &output.u.YUVA;
- *u = buf->u;
- *v = buf->v;
- *stride = buf->y_stride;
- *uv_stride = buf->u_stride;
- assert(buf->u_stride == buf->v_stride);
- }
- return out;
+ // data, width and height are checked by Decode().
+ if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) {
+ return NULL;
+ }
+
+ {
+ WebPDecBuffer output; // only to preserve the side-infos
+ uint8_t* const out = Decode(MODE_YUV, data, data_size,
+ width, height, &output);
+
+ if (out != NULL) {
+ const WebPYUVABuffer* const buf = &output.u.YUVA;
+ *u = buf->u;
+ *v = buf->v;
+ *stride = buf->y_stride;
+ *uv_stride = buf->u_stride;
+ assert(buf->u_stride == buf->v_stride);
+ }
+ return out;
+ }
}
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c
index 324e5eb993..fd45a2500e 100644
--- a/thirdparty/libwebp/src/demux/demux.c
+++ b/thirdparty/libwebp/src/demux/demux.c
@@ -25,7 +25,7 @@
#define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 3
-#define DMUX_REV_VERSION 0
+#define DMUX_REV_VERSION 1
typedef struct {
size_t start_; // start location of the data
diff --git a/thirdparty/libwebp/src/dsp/alpha_processing.c b/thirdparty/libwebp/src/dsp/alpha_processing.c
index 1892929a43..1d152f24da 100644
--- a/thirdparty/libwebp/src/dsp/alpha_processing.c
+++ b/thirdparty/libwebp/src/dsp/alpha_processing.c
@@ -425,6 +425,7 @@ void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color);
//------------------------------------------------------------------------------
// Init function
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitAlphaProcessingMIPSdspR2(void);
extern void WebPInitAlphaProcessingSSE2(void);
extern void WebPInitAlphaProcessingSSE41(void);
diff --git a/thirdparty/libwebp/src/dsp/cost.c b/thirdparty/libwebp/src/dsp/cost.c
index 460ec4f2a7..73d2140177 100644
--- a/thirdparty/libwebp/src/dsp/cost.c
+++ b/thirdparty/libwebp/src/dsp/cost.c
@@ -374,6 +374,7 @@ static void SetResidualCoeffs_C(const int16_t* const coeffs,
VP8GetResidualCostFunc VP8GetResidualCost;
VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspCostInitMIPS32(void);
extern void VP8EncDspCostInitMIPSdspR2(void);
extern void VP8EncDspCostInitSSE2(void);
diff --git a/thirdparty/libwebp/src/dsp/cost_neon.c b/thirdparty/libwebp/src/dsp/cost_neon.c
index 8cc8ce58aa..6582669cb3 100644
--- a/thirdparty/libwebp/src/dsp/cost_neon.c
+++ b/thirdparty/libwebp/src/dsp/cost_neon.c
@@ -29,7 +29,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1));
const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position));
-#ifdef __aarch64__
+#if WEBP_AARCH64
res->last = vmaxvq_u8(masked) - 1;
#else
const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked));
@@ -43,7 +43,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0);
--res->last;
-#endif // __aarch64__
+#endif // WEBP_AARCH64
res->coeffs = coeffs;
}
diff --git a/thirdparty/libwebp/src/dsp/cpu.c b/thirdparty/libwebp/src/dsp/cpu.c
index 62de73f750..2234c77b35 100644
--- a/thirdparty/libwebp/src/dsp/cpu.c
+++ b/thirdparty/libwebp/src/dsp/cpu.c
@@ -173,6 +173,7 @@ static int x86CPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test.
static int AndroidCPUInfo(CPUFeature feature) {
@@ -184,6 +185,7 @@ static int AndroidCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
#elif defined(EMSCRIPTEN) // also needs to be before generic NEON test
// Use compile flags as an indicator of SIMD support instead of a runtime check.
@@ -208,6 +210,7 @@ static int wasmCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo;
#elif defined(WEBP_HAVE_NEON)
// In most cases this function doesn't check for NEON support (it's assumed by
@@ -236,6 +239,7 @@ static int armCPUInfo(CPUFeature feature) {
return 1;
#endif
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \
defined(WEBP_USE_MSA)
@@ -247,7 +251,9 @@ static int mipsCPUInfo(CPUFeature feature) {
}
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo;
#else
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = NULL;
#endif
diff --git a/thirdparty/libwebp/src/dsp/cpu.h b/thirdparty/libwebp/src/dsp/cpu.h
index be80727c0d..c86540f280 100644
--- a/thirdparty/libwebp/src/dsp/cpu.h
+++ b/thirdparty/libwebp/src/dsp/cpu.h
@@ -43,6 +43,9 @@
#define __has_builtin(x) 0
#endif
+//------------------------------------------------------------------------------
+// x86 defines.
+
#if !defined(HAVE_CONFIG_H)
#if defined(_MSC_VER) && _MSC_VER > 1310 && \
(defined(_M_X64) || defined(_M_IX86))
@@ -80,6 +83,9 @@
#undef WEBP_MSC_SSE41
#undef WEBP_MSC_SSE2
+//------------------------------------------------------------------------------
+// Arm defines.
+
// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
// inline assembly would need to be modified for use with Native Client.
#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
@@ -98,16 +104,26 @@
// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
// vtbl4_u8(); a fix was made in 16.6.
-#if defined(_MSC_VER) && ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
- (_MSC_VER >= 1926 && defined(_M_ARM64)))
+#if defined(_MSC_VER) && \
+ ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
+ (_MSC_VER >= 1926 && (defined(_M_ARM64) || defined(_M_ARM64EC))))
#define WEBP_USE_NEON
#define WEBP_USE_INTRINSICS
#endif
+#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
+#define WEBP_AARCH64 1
+#else
+#define WEBP_AARCH64 0
+#endif
+
#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
#define WEBP_HAVE_NEON
#endif
+//------------------------------------------------------------------------------
+// MIPS defines.
+
#if defined(__mips__) && !defined(__mips64) && defined(__mips_isa_rev) && \
(__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
#define WEBP_USE_MIPS32
@@ -123,6 +139,8 @@
#define WEBP_USE_MSA
#endif
+//------------------------------------------------------------------------------
+
#ifndef WEBP_DSP_OMIT_C_CODE
#define WEBP_DSP_OMIT_C_CODE 1
#endif
@@ -133,13 +151,14 @@
#define WEBP_NEON_OMIT_C_CODE 0
#endif
-#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || \
- defined(__aarch64__))
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
#define WEBP_NEON_WORK_AROUND_GCC 1
#else
#define WEBP_NEON_WORK_AROUND_GCC 0
#endif
+//------------------------------------------------------------------------------
+
// This macro prevents thread_sanitizer from reporting known concurrent writes.
#define WEBP_TSAN_IGNORE_FUNCTION
#if defined(__has_feature)
@@ -241,16 +260,7 @@ typedef enum {
kMSA
} CPUFeature;
-#ifdef __cplusplus
-extern "C" {
-#endif
-
// returns true if the CPU supports the feature.
typedef int (*VP8CPUInfo)(CPUFeature feature);
-WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
#endif // WEBP_DSP_CPU_H_
diff --git a/thirdparty/libwebp/src/dsp/dec.c b/thirdparty/libwebp/src/dsp/dec.c
index 537c701282..33d8df8a62 100644
--- a/thirdparty/libwebp/src/dsp/dec.c
+++ b/thirdparty/libwebp/src/dsp/dec.c
@@ -734,6 +734,7 @@ VP8SimpleFilterFunc VP8SimpleHFilter16i;
void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst,
int dst_stride);
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8DspInitSSE2(void);
extern void VP8DspInitSSE41(void);
extern void VP8DspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/dec_neon.c b/thirdparty/libwebp/src/dsp/dec_neon.c
index fa851707e2..22784cf15a 100644
--- a/thirdparty/libwebp/src/dsp/dec_neon.c
+++ b/thirdparty/libwebp/src/dsp/dec_neon.c
@@ -1428,7 +1428,7 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x8_t A = vld1_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p2 = vaddlv_u8(A);
sum_top = vdupq_n_u16(p2);
#else
@@ -1511,7 +1511,7 @@ static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x16_t A = vld1q_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p3 = vaddlvq_u8(A);
sum_top = vdupq_n_u16(p3);
#else
diff --git a/thirdparty/libwebp/src/dsp/enc.c b/thirdparty/libwebp/src/dsp/enc.c
index ea47a3fd95..2ba97ba8d6 100644
--- a/thirdparty/libwebp/src/dsp/enc.c
+++ b/thirdparty/libwebp/src/dsp/enc.c
@@ -732,6 +732,7 @@ VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
VP8BlockCopy VP8Copy4x4;
VP8BlockCopy VP8Copy16x8;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspInitSSE2(void);
extern void VP8EncDspInitSSE41(void);
extern void VP8EncDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/enc_neon.c b/thirdparty/libwebp/src/dsp/enc_neon.c
index 3a04111c55..714800367b 100644
--- a/thirdparty/libwebp/src/dsp/enc_neon.c
+++ b/thirdparty/libwebp/src/dsp/enc_neon.c
@@ -764,7 +764,7 @@ static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a,
// Horizontal sum of all four uint32_t values in 'sum'.
static int SumToInt_NEON(uint32x4_t sum) {
-#if defined(__aarch64__)
+#if WEBP_AARCH64
return (int)vaddvq_u32(sum);
#else
const uint64x2_t sum2 = vpaddlq_u32(sum);
@@ -865,7 +865,7 @@ static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16],
uint8x8x4_t shuffles;
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
uint8x16x2_t all_out;
INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1));
diff --git a/thirdparty/libwebp/src/dsp/enc_sse2.c b/thirdparty/libwebp/src/dsp/enc_sse2.c
index 1d1055668f..010624a2f7 100644
--- a/thirdparty/libwebp/src/dsp/enc_sse2.c
+++ b/thirdparty/libwebp/src/dsp/enc_sse2.c
@@ -25,9 +25,160 @@
//------------------------------------------------------------------------------
// Transforms (Paragraph 14.4)
-// Does one or two inverse transforms.
-static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two) {
+// Does one inverse transform.
+static void ITransform_One_SSE2(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
+ // This implementation makes use of 16-bit fixed point versions of two
+ // multiply constants:
+ // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
+ // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
+ //
+ // To be able to use signed 16-bit integers, we use the following trick to
+ // have constants within range:
+ // - Associated constants are obtained by subtracting the 16-bit fixed point
+ // version of one:
+ // k = K - (1 << 16) => K = k + (1 << 16)
+ // K1 = 85267 => k1 = 20091
+ // K2 = 35468 => k2 = -30068
+ // - The multiplication of a variable by a constant become the sum of the
+ // variable and the multiplication of that variable by the associated
+ // constant:
+ // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
+ const __m128i k1k2 = _mm_set_epi16(-30068, -30068, -30068, -30068,
+ 20091, 20091, 20091, 20091);
+ const __m128i k2k1 = _mm_set_epi16(20091, 20091, 20091, 20091,
+ -30068, -30068, -30068, -30068);
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i zero_four = _mm_set_epi16(0, 0, 0, 0, 4, 4, 4, 4);
+ __m128i T01, T23;
+
+ // Load and concatenate the transform coefficients.
+ const __m128i in01 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i in23 = _mm_loadu_si128((const __m128i*)&in[8]);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+
+ // Vertical pass and subsequent transpose.
+ {
+ const __m128i in1 = _mm_unpackhi_epi64(in01, in01);
+ const __m128i in3 = _mm_unpackhi_epi64(in23, in23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
+ // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
+ const __m128i a_d3 = _mm_add_epi16(in01, in23);
+ const __m128i b_c3 = _mm_sub_epi16(in01, in23);
+ const __m128i c1d1 = _mm_mulhi_epi16(in1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(in3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(tmp01, tmp23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(tmp01, tmp23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Horizontal pass and subsequent transpose.
+ {
+ const __m128i T1 = _mm_unpackhi_epi64(T01, T01);
+ const __m128i T3 = _mm_unpackhi_epi64(T23, T23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i dc = _mm_add_epi16(T01, zero_four);
+
+ // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
+ // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
+ const __m128i a_d3 = _mm_add_epi16(dc, T23);
+ const __m128i b_c3 = _mm_sub_epi16(dc, T23);
+ const __m128i c1d1 = _mm_mulhi_epi16(T1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(T3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i shifted01 = _mm_srai_epi16(tmp01, 3);
+ const __m128i shifted23 = _mm_srai_epi16(tmp23, 3);
+ // a00 a01 a02 a03 a10 a11 a12 a13
+ // a20 a21 a22 a23 a30 a31 a32 a33
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(shifted01, shifted23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(shifted01, shifted23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Add inverse transform to 'ref' and store.
+ {
+ // Load the reference(s).
+ __m128i ref01, ref23, ref0123;
+ int32_t buf[4];
+
+ // Load four bytes/pixels per line.
+ const __m128i ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
+ const __m128i ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
+ const __m128i ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
+ const __m128i ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
+ ref01 = _mm_unpacklo_epi32(ref0, ref1);
+ ref23 = _mm_unpacklo_epi32(ref2, ref3);
+
+ // Convert to 16b.
+ ref01 = _mm_unpacklo_epi8(ref01, zero);
+ ref23 = _mm_unpacklo_epi8(ref23, zero);
+ // Add the inverse transform(s).
+ ref01 = _mm_add_epi16(ref01, T01);
+ ref23 = _mm_add_epi16(ref23, T23);
+ // Unsigned saturate to 8b.
+ ref0123 = _mm_packus_epi16(ref01, ref23);
+
+ _mm_storeu_si128((__m128i *)buf, ref0123);
+
+ // Store four bytes/pixels per line.
+ WebPInt32ToMem(&dst[0 * BPS], buf[0]);
+ WebPInt32ToMem(&dst[1 * BPS], buf[1]);
+ WebPInt32ToMem(&dst[2 * BPS], buf[2]);
+ WebPInt32ToMem(&dst[3 * BPS], buf[3]);
+ }
+}
+
+// Does two inverse transforms.
+static void ITransform_Two_SSE2(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
// This implementation makes use of 16-bit fixed point versions of two
// multiply constants:
// K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
@@ -49,33 +200,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
__m128i T0, T1, T2, T3;
// Load and concatenate the transform coefficients (we'll do two inverse
- // transforms in parallel). In the case of only one inverse transform, the
- // second half of the vectors will just contain random value we'll never
- // use nor store.
+ // transforms in parallel).
__m128i in0, in1, in2, in3;
{
- in0 = _mm_loadl_epi64((const __m128i*)&in[0]);
- in1 = _mm_loadl_epi64((const __m128i*)&in[4]);
- in2 = _mm_loadl_epi64((const __m128i*)&in[8]);
- in3 = _mm_loadl_epi64((const __m128i*)&in[12]);
- // a00 a10 a20 a30 x x x x
- // a01 a11 a21 a31 x x x x
- // a02 a12 a22 a32 x x x x
- // a03 a13 a23 a33 x x x x
- if (do_two) {
- const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]);
- const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]);
- const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]);
- const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]);
- in0 = _mm_unpacklo_epi64(in0, inB0);
- in1 = _mm_unpacklo_epi64(in1, inB1);
- in2 = _mm_unpacklo_epi64(in2, inB2);
- in3 = _mm_unpacklo_epi64(in3, inB3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
+ const __m128i tmp0 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i tmp1 = _mm_loadu_si128((const __m128i*)&in[8]);
+ const __m128i tmp2 = _mm_loadu_si128((const __m128i*)&in[16]);
+ const __m128i tmp3 = _mm_loadu_si128((const __m128i*)&in[24]);
+ in0 = _mm_unpacklo_epi64(tmp0, tmp2);
+ in1 = _mm_unpackhi_epi64(tmp0, tmp2);
+ in2 = _mm_unpacklo_epi64(tmp1, tmp3);
+ in3 = _mm_unpackhi_epi64(tmp1, tmp3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
}
// Vertical pass and subsequent transpose.
@@ -148,19 +287,11 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
const __m128i zero = _mm_setzero_si128();
// Load the reference(s).
__m128i ref0, ref1, ref2, ref3;
- if (do_two) {
- // Load eight bytes/pixels per line.
- ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
- ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
- ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
- ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
- } else {
- // Load four bytes/pixels per line.
- ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
- ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
- ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
- ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
- }
+ // Load eight bytes/pixels per line.
+ ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
// Convert to 16b.
ref0 = _mm_unpacklo_epi8(ref0, zero);
ref1 = _mm_unpacklo_epi8(ref1, zero);
@@ -176,20 +307,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
ref1 = _mm_packus_epi16(ref1, ref1);
ref2 = _mm_packus_epi16(ref2, ref2);
ref3 = _mm_packus_epi16(ref3, ref3);
- // Store the results.
- if (do_two) {
- // Store eight bytes/pixels per line.
- _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
- _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
- _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
- _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
- } else {
- // Store four bytes/pixels per line.
- WebPInt32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
- WebPInt32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
- WebPInt32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
- WebPInt32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
- }
+ // Store eight bytes/pixels per line.
+ _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
+ _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
+ _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
+ _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
+ }
+}
+
+// Does one or two inverse transforms.
+static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two) {
+ if (do_two) {
+ ITransform_Two_SSE2(ref, in, dst);
+ } else {
+ ITransform_One_SSE2(ref, in, dst);
}
}
diff --git a/thirdparty/libwebp/src/dsp/filters.c b/thirdparty/libwebp/src/dsp/filters.c
index 4506567ba3..85eee5098f 100644
--- a/thirdparty/libwebp/src/dsp/filters.c
+++ b/thirdparty/libwebp/src/dsp/filters.c
@@ -233,6 +233,7 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8FiltersInitMIPSdspR2(void);
extern void VP8FiltersInitMSA(void);
extern void VP8FiltersInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless.c b/thirdparty/libwebp/src/dsp/lossless.c
index fb86e58d4a..9f81209453 100644
--- a/thirdparty/libwebp/src/dsp/lossless.c
+++ b/thirdparty/libwebp/src/dsp/lossless.c
@@ -588,6 +588,7 @@ VP8LConvertFunc VP8LConvertBGRAToBGR;
VP8LMapARGBFunc VP8LMapColor32b;
VP8LMapAlphaFunc VP8LMapColor8b;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8LDspInitSSE2(void);
extern void VP8LDspInitSSE41(void);
extern void VP8LDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c
index b1f9f26d72..cde1280617 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc.c
@@ -791,6 +791,7 @@ VP8LBundleColorMapFunc VP8LBundleColorMap;
VP8LPredictorAddSubFunc VP8LPredictorsSub[16];
VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8LEncDspInitSSE2(void);
extern void VP8LEncDspInitSSE41(void);
extern void VP8LEncDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
index 7c7b73f8b6..e32c7961a2 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
@@ -25,7 +25,7 @@
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
diff --git a/thirdparty/libwebp/src/dsp/lossless_neon.c b/thirdparty/libwebp/src/dsp/lossless_neon.c
index 89e3e013a0..ddc9b61711 100644
--- a/thirdparty/libwebp/src/dsp/lossless_neon.c
+++ b/thirdparty/libwebp/src/dsp/lossless_neon.c
@@ -498,7 +498,7 @@ static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper,
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
diff --git a/thirdparty/libwebp/src/dsp/neon.h b/thirdparty/libwebp/src/dsp/neon.h
index c591f9b9a7..14acb4044b 100644
--- a/thirdparty/libwebp/src/dsp/neon.h
+++ b/thirdparty/libwebp/src/dsp/neon.h
@@ -21,7 +21,7 @@
// Right now, some intrinsics functions seem slower, so we disable them
// everywhere except newer clang/gcc or aarch64 where the inline assembly is
// incompatible.
-#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__)
+#if LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 9) || WEBP_AARCH64
#define WEBP_USE_INTRINSICS // use intrinsics when possible
#endif
@@ -46,7 +46,7 @@
// if using intrinsics, this flag avoids some functions that make gcc-4.6.3
// crash ("internal compiler error: in immed_double_const, at emit-rtl.").
// (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183)
-#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
#define WORK_AROUND_GCC
#endif
diff --git a/thirdparty/libwebp/src/dsp/quant.h b/thirdparty/libwebp/src/dsp/quant.h
index fc099bf9d6..bf7734cb11 100644
--- a/thirdparty/libwebp/src/dsp/quant.h
+++ b/thirdparty/libwebp/src/dsp/quant.h
@@ -22,7 +22,7 @@
#define IsFlat IsFlat_NEON
static uint32_t horizontal_add_uint32x4(const uint32x4_t a) {
-#if defined(__aarch64__)
+#if WEBP_AARCH64
return vaddvq_u32(a);
#else
const uint64x2_t b = vpaddlq_u32(a);
diff --git a/thirdparty/libwebp/src/dsp/rescaler.c b/thirdparty/libwebp/src/dsp/rescaler.c
index 14620ce4f1..325d8be180 100644
--- a/thirdparty/libwebp/src/dsp/rescaler.c
+++ b/thirdparty/libwebp/src/dsp/rescaler.c
@@ -197,6 +197,7 @@ WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPRescalerDspInitSSE2(void);
extern void WebPRescalerDspInitMIPS32(void);
extern void WebPRescalerDspInitMIPSdspR2(void);
diff --git a/thirdparty/libwebp/src/dsp/ssim.c b/thirdparty/libwebp/src/dsp/ssim.c
index f85c2e6e5b..9a1341ed95 100644
--- a/thirdparty/libwebp/src/dsp/ssim.c
+++ b/thirdparty/libwebp/src/dsp/ssim.c
@@ -137,6 +137,7 @@ VP8SSIMGetClippedFunc VP8SSIMGetClipped;
VP8AccumulateSSEFunc VP8AccumulateSSE;
#endif
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8SSIMDspInitSSE2(void);
WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
diff --git a/thirdparty/libwebp/src/dsp/upsampling.c b/thirdparty/libwebp/src/dsp/upsampling.c
index 87f771f3eb..983b9c42d3 100644
--- a/thirdparty/libwebp/src/dsp/upsampling.c
+++ b/thirdparty/libwebp/src/dsp/upsampling.c
@@ -215,6 +215,7 @@ static void EmptyYuv444Func(const uint8_t* y,
WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitYUV444ConvertersMIPSdspR2(void);
extern void WebPInitYUV444ConvertersSSE2(void);
extern void WebPInitYUV444ConvertersSSE41(void);
diff --git a/thirdparty/libwebp/src/dsp/upsampling_neon.c b/thirdparty/libwebp/src/dsp/upsampling_neon.c
index 6ba71a7de5..bbc000ca2d 100644
--- a/thirdparty/libwebp/src/dsp/upsampling_neon.c
+++ b/thirdparty/libwebp/src/dsp/upsampling_neon.c
@@ -111,7 +111,7 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 };
vst4_u8(out, v255_r_g_b); \
} while (0)
-#if !defined(WEBP_SWAP_16BIT_CSP)
+#if (WEBP_SWAP_16BIT_CSP == 0)
#define ZIP_U8(lo, hi) vzip_u8((lo), (hi))
#else
#define ZIP_U8(lo, hi) vzip_u8((hi), (lo))
diff --git a/thirdparty/libwebp/src/dsp/yuv.c b/thirdparty/libwebp/src/dsp/yuv.c
index d16c13d3ca..8a04b85d82 100644
--- a/thirdparty/libwebp/src/dsp/yuv.c
+++ b/thirdparty/libwebp/src/dsp/yuv.c
@@ -70,6 +70,7 @@ void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
WebPSamplerRowFunc WebPSamplers[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitSamplersSSE2(void);
extern void WebPInitSamplersSSE41(void);
extern void WebPInitSamplersMIPS32(void);
diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c
index f7c02690e3..26f003485a 100644
--- a/thirdparty/libwebp/src/enc/alpha_enc.c
+++ b/thirdparty/libwebp/src/enc/alpha_enc.c
@@ -13,6 +13,7 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#include "src/enc/vp8i_enc.h"
#include "src/dsp/dsp.h"
@@ -140,6 +141,11 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
!reduce_levels, &tmp_bw, &result->stats);
if (ok) {
output = VP8LBitWriterFinish(&tmp_bw);
+ if (tmp_bw.error_) {
+ VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
+ return 0;
+ }
output_size = VP8LBitWriterNumBytes(&tmp_bw);
if (output_size > data_size) {
// compressed size is larger than source! Revert to uncompressed mode.
@@ -148,6 +154,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
}
} else {
VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
return 0;
}
}
@@ -162,7 +169,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
header = method | (filter << 2);
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
- VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
+ if (!VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size)) ok = 0;
ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
@@ -312,11 +319,11 @@ static int EncodeAlpha(VP8Encoder* const enc,
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
if (quality < 0 || quality > 100) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method == ALPHA_NO_COMPRESSION) {
@@ -326,7 +333,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
if (quant_alpha == NULL) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Extract alpha data (width x height) from raw_data (stride x height).
@@ -346,6 +353,9 @@ static int EncodeAlpha(VP8Encoder* const enc,
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
filter, reduce_levels, effort_level, output,
output_size, pic->stats);
+ if (!ok) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
#if !defined(WEBP_DISABLE_STATS)
if (pic->stats != NULL) { // need stats?
pic->stats->coded_size += (int)(*output_size);
@@ -405,7 +415,7 @@ int VP8EncStartAlpha(VP8Encoder* const enc) {
WebPWorker* const worker = &enc->alpha_worker_;
// Makes sure worker is good to go.
if (!WebPGetWorkerInterface()->Reset(worker)) {
- return 0;
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
WebPGetWorkerInterface()->Launch(worker);
return 1;
diff --git a/thirdparty/libwebp/src/enc/analysis_enc.c b/thirdparty/libwebp/src/enc/analysis_enc.c
index a0001ac034..962eaa998f 100644
--- a/thirdparty/libwebp/src/enc/analysis_enc.c
+++ b/thirdparty/libwebp/src/enc/analysis_enc.c
@@ -474,6 +474,10 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
} else { // Use only one default segment.
ResetAllMBInfo(enc);
}
+ if (!ok) {
+ return WebPEncodingSetError(enc->pic_,
+ VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
return ok;
}
diff --git a/thirdparty/libwebp/src/enc/backward_references_enc.c b/thirdparty/libwebp/src/enc/backward_references_enc.c
index 49a0fac034..dc98bf1719 100644
--- a/thirdparty/libwebp/src/enc/backward_references_enc.c
+++ b/thirdparty/libwebp/src/enc/backward_references_enc.c
@@ -283,8 +283,7 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
hash_to_first_index =
(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
if (hash_to_first_index == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
percent_range = remaining_percent / 2;
@@ -1050,8 +1049,7 @@ int VP8LGetBackwardReferences(
refs_best = GetBackwardReferencesLowEffort(
width, height, argb, cache_bits_best, hash_chain, refs);
if (refs_best == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Set it in first position.
BackwardRefsSwap(refs_best, &refs[0]);
@@ -1059,8 +1057,7 @@ int VP8LGetBackwardReferences(
if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
cache_bits_max, do_no_cache, hash_chain, refs,
cache_bits_best)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
}
diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c
index b93d9e5b99..9a98dc1f3e 100644
--- a/thirdparty/libwebp/src/enc/frame_enc.c
+++ b/thirdparty/libwebp/src/enc/frame_enc.c
@@ -689,7 +689,7 @@ static int PreLoopInitialize(VP8Encoder* const enc) {
}
if (!ok) {
VP8EncFreeBitWriters(enc); // malloc error occurred
- WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return ok;
}
@@ -719,6 +719,7 @@ static int PostLoopFinalize(VP8EncIterator* const it, int ok) {
} else {
// Something bad happened -> need to do some memory cleanup.
VP8EncFreeBitWriters(enc);
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return ok;
}
@@ -754,6 +755,11 @@ int VP8EncLoop(VP8Encoder* const enc) {
// *then* decide how to code the skip decision if there's one.
if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
CodeResiduals(it.bw_, &it, &info);
+ if (it.bw_->error_) {
+ // enc->pic_->error_code is set in PostLoopFinalize().
+ ok = 0;
+ break;
+ }
} else { // reset predictors after a skip
ResetAfterSkip(&it);
}
diff --git a/thirdparty/libwebp/src/enc/picture_csp_enc.c b/thirdparty/libwebp/src/enc/picture_csp_enc.c
index 78c8ca479b..a9280e6c30 100644
--- a/thirdparty/libwebp/src/enc/picture_csp_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_csp_enc.c
@@ -98,6 +98,7 @@ static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1];
static uint16_t kGammaToLinearTab[256];
static volatile int kGammaTablesOk = 0;
static void InitGammaTables(void);
+extern VP8CPUInfo VP8GetCPUInfo;
WEBP_DSP_INIT_FUNC(InitGammaTables) {
if (!kGammaTablesOk) {
@@ -534,7 +535,9 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
WebPInitConvertARGBToYUV();
InitGammaTables();
- if (tmp_rgb == NULL) return 0; // malloc error
+ if (tmp_rgb == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
// Downsample Y/U/V planes, two rows at a time
for (y = 0; y < (height >> 1); ++y) {
diff --git a/thirdparty/libwebp/src/enc/picture_rescale_enc.c b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
index 839f91cacc..ea90d82548 100644
--- a/thirdparty/libwebp/src/enc/picture_rescale_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
@@ -137,7 +137,9 @@ int WebPPictureCrop(WebPPicture* pic,
PictureGrabSpecs(pic, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(pic, tmp.error_code);
+ }
if (!pic->use_argb) {
const int y_offset = top * pic->y_stride + left;
@@ -212,26 +214,28 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
prev_height = picture->height;
if (!WebPRescalerGetScaledDimensions(
prev_width, prev_height, &width, &height)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
PictureGrabSpecs(picture, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(picture, tmp.error_code);
+ }
if (!picture->use_argb) {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// If present, we need to rescale alpha first (for AlphaMultiplyY).
if (picture->a != NULL) {
WebPInitAlphaProcessing();
if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
tmp.a, width, height, tmp.a_stride, work, 1)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
}
@@ -246,14 +250,14 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
!RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
tmp.uv_stride, work, 1)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyY(&tmp, 1);
} else {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// In order to correctly interpolate colors, we need to apply the alpha
// weighting first (black-matting), scale the RGB values, and remove
@@ -263,7 +267,7 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
height, tmp.argb_stride * 4, work, 4)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyARGB(&tmp, 1);
}
diff --git a/thirdparty/libwebp/src/enc/syntax_enc.c b/thirdparty/libwebp/src/enc/syntax_enc.c
index e18cf650ca..9b8f524d69 100644
--- a/thirdparty/libwebp/src/enc/syntax_enc.c
+++ b/thirdparty/libwebp/src/enc/syntax_enc.c
@@ -258,7 +258,10 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
buf[3 * p + 1] = (part_size >> 8) & 0xff;
buf[3 * p + 2] = (part_size >> 16) & 0xff;
}
- return p ? pic->writer(buf, 3 * p, pic) : 1;
+ if (p && !pic->writer(buf, 3 * p, pic)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
+ }
+ return 1;
}
//------------------------------------------------------------------------------
@@ -381,6 +384,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
+ if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
return ok;
}
diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h
index c9927c47d8..19d9a6edb7 100644
--- a/thirdparty/libwebp/src/enc/vp8i_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8i_enc.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 3
-#define ENC_REV_VERSION 0
+#define ENC_REV_VERSION 1
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c
index 0b07e529a9..3a8ec3dd1e 100644
--- a/thirdparty/libwebp/src/enc/vp8l_enc.c
+++ b/thirdparty/libwebp/src/enc/vp8l_enc.c
@@ -196,8 +196,7 @@ static int CoOccurrenceBuild(const WebPPicture* const pic,
uint32_t palette_sorted[MAX_PALETTE_SIZE];
lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
if (lines == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
line_top = &lines[0];
line_current = &lines[pic->width];
@@ -255,10 +254,10 @@ static int PaletteSortModifiedZeng(
cooccurrence =
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
if (cooccurrence == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) {
+ WebPSafeFree(cooccurrence);
return 0;
}
@@ -1012,8 +1011,7 @@ static int StoreImageToBitMask(
VP8LRefsCursorNext(&c);
}
if (bw->error_) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return 1;
}
@@ -1297,7 +1295,10 @@ static int EncodeImageInternal(
}
}
tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
- if (tokens == NULL) goto Error;
+ if (tokens == NULL) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto Error;
+ }
for (i = 0; i < 5 * histogram_image_size; ++i) {
HuffmanTreeCode* const codes = &huffman_codes[i];
StoreHuffmanCode(bw, huff_tree, tokens, codes);
@@ -1448,18 +1449,21 @@ static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw,
const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
const size_t pad = vp8l_size & 1;
const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
+ *coded_size = 0;
+
+ if (bw->error_) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
if (!WriteRiffHeader(pic, riff_size, vp8l_size) ||
!pic->writer(webpll_data, webpll_size, pic)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
if (pad) {
const uint8_t pad_byte[1] = { 0 };
if (!pic->writer(pad_byte, 1, pic)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
}
*coded_size = CHUNK_HEADER_SIZE + riff_size;
@@ -1504,8 +1508,7 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width,
ClearTransformBuffer(enc);
mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
if (mem == NULL) {
- WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
enc->transform_mem_ = mem;
enc->transform_mem_size_ = (size_t)mem_size;
@@ -1613,8 +1616,7 @@ static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst,
int x, y;
if (tmp_row == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
@@ -1968,9 +1970,8 @@ int VP8LEncodeStream(const WebPConfig* const config,
int ok_main;
if (enc_main == NULL || !VP8LBitWriterInit(&bw_side, 0)) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
VP8LEncoderDelete(enc_main);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Avoid "garbage value" error from Clang's static analysis tool.
@@ -2117,8 +2118,7 @@ int VP8LEncodeImage(const WebPConfig* const config,
if (picture == NULL) return 0;
if (config == NULL || picture->argb == NULL) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
}
width = picture->width;
diff --git a/thirdparty/libwebp/src/enc/webp_enc.c b/thirdparty/libwebp/src/enc/webp_enc.c
index 9620e05070..583fe6a8bb 100644
--- a/thirdparty/libwebp/src/enc/webp_enc.c
+++ b/thirdparty/libwebp/src/enc/webp_enc.c
@@ -307,7 +307,10 @@ int WebPEncodingSetError(const WebPPicture* const pic,
WebPEncodingError error) {
assert((int)error < VP8_ENC_ERROR_LAST);
assert((int)error >= VP8_ENC_OK);
- ((WebPPicture*)pic)->error_code = error;
+ // The oldest error reported takes precedence over the new one.
+ if (pic->error_code == VP8_ENC_OK) {
+ ((WebPPicture*)pic)->error_code = error;
+ }
return 0;
}
@@ -317,8 +320,7 @@ int WebPReportProgress(const WebPPicture* const pic,
*percent_store = percent;
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
// user abort requested
- WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
}
}
return 1; // ok
@@ -329,7 +331,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
int ok = 0;
if (pic == NULL) return 0;
- WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
+ pic->error_code = VP8_ENC_OK; // all ok so far
if (config == NULL) { // bad params
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
}
diff --git a/thirdparty/libwebp/src/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h
index 7929138c44..fc44d6f2fe 100644
--- a/thirdparty/libwebp/src/mux/muxi.h
+++ b/thirdparty/libwebp/src/mux/muxi.h
@@ -29,7 +29,7 @@ extern "C" {
#define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 3
-#define MUX_REV_VERSION 0
+#define MUX_REV_VERSION 1
// Chunk object.
typedef struct WebPChunk WebPChunk;
diff --git a/thirdparty/libwebp/src/mux/muxread.c b/thirdparty/libwebp/src/mux/muxread.c
index 80050396e1..9862ec68ee 100644
--- a/thirdparty/libwebp/src/mux/muxread.c
+++ b/thirdparty/libwebp/src/mux/muxread.c
@@ -116,9 +116,12 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
// Each of ANMF chunk contain a header at the beginning. So, its size should
// be at least 'hdr_size'.
if (size < hdr_size) goto Fail;
- ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
+ if (ChunkAssignData(&subchunk, &temp, copy_data,
+ chunk->tag_) != WEBP_MUX_OK) {
+ goto Fail;
+ }
}
- ChunkSetHead(&subchunk, &wpi->header_);
+ if (ChunkSetHead(&subchunk, &wpi->header_) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
// Rest of the chunks.
diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.c b/thirdparty/libwebp/src/utils/bit_reader_utils.c
index 857cd60988..a26557aa49 100644
--- a/thirdparty/libwebp/src/utils/bit_reader_utils.c
+++ b/thirdparty/libwebp/src/utils/bit_reader_utils.c
@@ -15,6 +15,7 @@
#include "src/webp/config.h"
#endif
+#include "src/dsp/cpu.h"
#include "src/utils/bit_reader_inl_utils.h"
#include "src/utils/utils.h"
@@ -121,7 +122,7 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits,
#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits.
-#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \
+#if defined(__arm__) || defined(_M_ARM) || WEBP_AARCH64 || \
defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64__) || defined(_M_X64)
#define VP8L_USE_FAST_LOAD
diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.h b/thirdparty/libwebp/src/utils/bit_reader_utils.h
index e64156e318..25ff31e5d9 100644
--- a/thirdparty/libwebp/src/utils/bit_reader_utils.h
+++ b/thirdparty/libwebp/src/utils/bit_reader_utils.h
@@ -19,6 +19,7 @@
#ifdef _MSC_VER
#include <stdlib.h> // _byteswap_ulong
#endif
+#include "src/dsp/cpu.h"
#include "src/webp/types.h"
// Warning! This macro triggers quite some MACRO wizardry around func signature!
@@ -64,7 +65,7 @@ extern "C" {
#define BITS 56
#elif defined(__arm__) || defined(_M_ARM) // ARM
#define BITS 24
-#elif defined(__aarch64__) // ARM 64bit
+#elif WEBP_AARCH64 // ARM 64bit
#define BITS 56
#elif defined(__mips__) // MIPS
#define BITS 24
diff --git a/thirdparty/libwebp/src/webp/decode.h b/thirdparty/libwebp/src/webp/decode.h
index d98247509a..0177b12089 100644
--- a/thirdparty/libwebp/src/webp/decode.h
+++ b/thirdparty/libwebp/src/webp/decode.h
@@ -81,10 +81,11 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
// returned is the Y samples buffer. Upon return, *u and *v will point to
// the U and V chroma data. These U and V buffers need NOT be passed to
// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
-// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
+// planes are both (*width + 1) / 2 and (*height + 1) / 2.
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
// have a common stride returned as '*uv_stride'.
-// Return NULL in case of error.
+// 'width' and 'height' may be NULL, the other pointers must not be.
+// Returns NULL in case of error.
// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr
WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height,
diff --git a/thirdparty/mbedtls/include/mbedtls/aria.h b/thirdparty/mbedtls/include/mbedtls/aria.h
index 9856a1cae7..d307ff9e47 100644
--- a/thirdparty/mbedtls/include/mbedtls/aria.h
+++ b/thirdparty/mbedtls/include/mbedtls/aria.h
@@ -274,10 +274,6 @@ int mbedtls_aria_crypt_cfb128(mbedtls_aria_context *ctx,
* \brief This function performs an ARIA-CTR encryption or decryption
* operation.
*
- * This function performs the operation defined in the \p mode
- * parameter (encrypt/decrypt), on the input data buffer
- * defined in the \p input parameter.
- *
* Due to the nature of CTR, you must use the same key schedule
* for both encryption and decryption operations. Therefore, you
* must use the context initialized with mbedtls_aria_setkey_enc()
diff --git a/thirdparty/mbedtls/include/mbedtls/asn1.h b/thirdparty/mbedtls/include/mbedtls/asn1.h
index 540cdcc469..82aaee8f30 100644
--- a/thirdparty/mbedtls/include/mbedtls/asn1.h
+++ b/thirdparty/mbedtls/include/mbedtls/asn1.h
@@ -453,7 +453,7 @@ void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq);
* on a successful invocation.
* \param end The end of the ASN.1 SEQUENCE container.
* \param tag_must_mask A mask to be applied to the ASN.1 tags found within
- * the SEQUENCE before comparing to \p tag_must_value.
+ * the SEQUENCE before comparing to \p tag_must_val.
* \param tag_must_val The required value of each ASN.1 tag found in the
* SEQUENCE, after masking with \p tag_must_mask.
* Mismatching tags lead to an error.
@@ -462,7 +462,7 @@ void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq);
* while a value of \c 0xFF for \p tag_must_mask means
* that \p tag_must_val is the only allowed tag.
* \param tag_may_mask A mask to be applied to the ASN.1 tags found within
- * the SEQUENCE before comparing to \p tag_may_value.
+ * the SEQUENCE before comparing to \p tag_may_val.
* \param tag_may_val The desired value of each ASN.1 tag found in the
* SEQUENCE, after masking with \p tag_may_mask.
* Mismatching tags will be silently ignored.
diff --git a/thirdparty/mbedtls/include/mbedtls/bignum.h b/thirdparty/mbedtls/include/mbedtls/bignum.h
index 788ea21a8d..cbed25984e 100644
--- a/thirdparty/mbedtls/include/mbedtls/bignum.h
+++ b/thirdparty/mbedtls/include/mbedtls/bignum.h
@@ -533,7 +533,7 @@ int mbedtls_mpi_write_file(const char *p, const mbedtls_mpi *X,
* \param X The destination MPI. This must point to an initialized MPI.
* \param buf The input buffer. This must be a readable buffer of length
* \p buflen Bytes.
- * \param buflen The length of the input buffer \p p in Bytes.
+ * \param buflen The length of the input buffer \p buf in Bytes.
*
* \return \c 0 if successful.
* \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
@@ -548,7 +548,7 @@ int mbedtls_mpi_read_binary(mbedtls_mpi *X, const unsigned char *buf,
* \param X The destination MPI. This must point to an initialized MPI.
* \param buf The input buffer. This must be a readable buffer of length
* \p buflen Bytes.
- * \param buflen The length of the input buffer \p p in Bytes.
+ * \param buflen The length of the input buffer \p buf in Bytes.
*
* \return \c 0 if successful.
* \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
@@ -986,8 +986,8 @@ int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A,
* \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
* \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p N is less than
* or equal to one.
- * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p has no modular inverse
- * with respect to \p N.
+ * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p A has no modular
+ * inverse with respect to \p N.
*/
int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A,
const mbedtls_mpi *N);
@@ -1039,7 +1039,7 @@ MBEDTLS_DEPRECATED int mbedtls_mpi_is_prime(const mbedtls_mpi *X,
* This must point to an initialized MPI.
* \param rounds The number of bases to perform the Miller-Rabin primality
* test for. The probability of returning 0 on a composite is
- * at most 2<sup>-2*\p rounds</sup>.
+ * at most 2<sup>-2*\p rounds </sup>.
* \param f_rng The RNG function to use. This must not be \c NULL.
* \param p_rng The RNG parameter to be passed to \p f_rng.
* This may be \c NULL if \p f_rng doesn't use
diff --git a/thirdparty/mbedtls/include/mbedtls/bn_mul.h b/thirdparty/mbedtls/include/mbedtls/bn_mul.h
index a0bc4d061d..6414e54291 100644
--- a/thirdparty/mbedtls/include/mbedtls/bn_mul.h
+++ b/thirdparty/mbedtls/include/mbedtls/bn_mul.h
@@ -677,6 +677,15 @@
#if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7)
#if defined(__thumb__) && !defined(__thumb2__)
+#if !defined(__ARMCC_VERSION) && !defined(__clang__) \
+ && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+/*
+ * Thumb 1 ISA. This code path has only been tested successfully on gcc;
+ * it does not compile on clang or armclang.
+ *
+ * Other compilers which define __GNUC__ may not work. The above macro
+ * attempts to exclude these untested compilers.
+ */
#define MULADDC_INIT \
asm( \
@@ -731,6 +740,8 @@
"r6", "r7", "r8", "r9", "cc" \
);
+#endif /* Compiler is gcc */
+
#elif (__ARM_ARCH >= 6) && \
defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)
diff --git a/thirdparty/mbedtls/include/mbedtls/camellia.h b/thirdparty/mbedtls/include/mbedtls/camellia.h
index 05397d2316..e840947d4b 100644
--- a/thirdparty/mbedtls/include/mbedtls/camellia.h
+++ b/thirdparty/mbedtls/include/mbedtls/camellia.h
@@ -231,7 +231,7 @@ int mbedtls_camellia_crypt_cfb128(mbedtls_camellia_context *ctx,
* *note Due to the nature of CTR mode, you should use the same
* key for both encryption and decryption. In particular, calls
* to this function should be preceded by a key-schedule via
- * mbedtls_camellia_setkey_enc() regardless of whether \p mode
+ * mbedtls_camellia_setkey_enc() regardless of whether the mode
* is #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT.
*
* \warning You must never reuse a nonce value with the same key. Doing so
diff --git a/thirdparty/mbedtls/include/mbedtls/cipher.h b/thirdparty/mbedtls/include/mbedtls/cipher.h
index aa155d7bf8..56fc2d828a 100644
--- a/thirdparty/mbedtls/include/mbedtls/cipher.h
+++ b/thirdparty/mbedtls/include/mbedtls/cipher.h
@@ -427,7 +427,7 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values(const mbedtls_ciphe
const mbedtls_cipher_mode_t mode);
/**
- * \brief This function initializes a \p cipher_context as NONE.
+ * \brief This function initializes a \p ctx as NONE.
*
* \param ctx The context to be initialized. This must not be \c NULL.
*/
@@ -602,7 +602,7 @@ static inline const char *mbedtls_cipher_get_name(
* \param ctx The context of the cipher. This must be initialized.
*
* \return The key length of the cipher in bits.
- * \return #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been
+ * \return #MBEDTLS_KEY_LENGTH_NONE if \p ctx has not been
* initialized.
*/
static inline int mbedtls_cipher_get_key_bitlen(
@@ -779,7 +779,7 @@ int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx,
* \param ctx The generic cipher context. This must be initialized and
* bound to a key.
* \param output The buffer to write data to. This needs to be a writable
- * buffer of at least \p block_size Bytes.
+ * buffer of at least block_size Bytes.
* \param olen The length of the data written to the \p output buffer.
* This may not be \c NULL.
*
diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h
index 1381c1fd16..04acc1c343 100644
--- a/thirdparty/mbedtls/include/mbedtls/config.h
+++ b/thirdparty/mbedtls/include/mbedtls/config.h
@@ -1458,8 +1458,8 @@
* );
* ```
* The \c context value is initialized to 0 before the first call.
- * The function must fill the \c output buffer with \p output_size bytes
- * of random data and set \c *output_length to \p output_size.
+ * The function must fill the \c output buffer with \c output_size bytes
+ * of random data and set \c *output_length to \c output_size.
*
* Requires: MBEDTLS_PSA_CRYPTO_C
*
diff --git a/thirdparty/mbedtls/include/mbedtls/ecdsa.h b/thirdparty/mbedtls/include/mbedtls/ecdsa.h
index e42d114c42..b7029d7d56 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecdsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecdsa.h
@@ -266,8 +266,9 @@ int mbedtls_ecdsa_sign_det(mbedtls_ecp_group *grp, mbedtls_mpi *r,
* \param md_alg The hash algorithm used to hash the original data.
* \param f_rng_blind The RNG function used for blinding. This must not be
* \c NULL.
- * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be
- * \c NULL if \p f_rng doesn't need a context parameter.
+ * \param p_rng_blind The RNG context to be passed to \p f_rng_blind. This
+ * may be \c NULL if \p f_rng_blind doesn't need
+ * a context parameter.
*
* \return \c 0 on success.
* \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
@@ -344,7 +345,7 @@ int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
* via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
* \param md_alg The message digest that was used to hash the message.
* \param hash The message hash to be signed. This must be a readable
- * buffer of length \p blen Bytes.
+ * buffer of length \p hlen Bytes.
* \param hlen The length of the hash \p hash in Bytes.
* \param sig The buffer to which to write the signature. This must be a
* writable buffer of length at least twice as large as the
@@ -386,7 +387,7 @@ int mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx,
* via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
* \param md_alg The message digest that was used to hash the message.
* \param hash The message hash to be signed. This must be a readable
- * buffer of length \p blen Bytes.
+ * buffer of length \p hlen Bytes.
* \param hlen The length of the hash \p hash in Bytes.
* \param sig The buffer to which to write the signature. This must be a
* writable buffer of length at least twice as large as the
@@ -453,7 +454,7 @@ int mbedtls_ecdsa_write_signature_restartable(mbedtls_ecdsa_context *ctx,
* and have a group and private key bound to it, for example
* via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
* \param hash The message hash to be signed. This must be a readable
- * buffer of length \p blen Bytes.
+ * buffer of length \p hlen Bytes.
* \param hlen The length of the hash \p hash in Bytes.
* \param sig The buffer to which to write the signature. This must be a
* writable buffer of length at least twice as large as the
@@ -490,7 +491,7 @@ int mbedtls_ecdsa_write_signature_det(mbedtls_ecdsa_context *ctx,
* \param ctx The ECDSA context to use. This must be initialized
* and have a group and public key bound to it.
* \param hash The message hash that was signed. This must be a readable
- * buffer of length \p size Bytes.
+ * buffer of length \p hlen Bytes.
* \param hlen The size of the hash \p hash.
* \param sig The signature to read and verify. This must be a readable
* buffer of length \p slen Bytes.
@@ -520,7 +521,7 @@ int mbedtls_ecdsa_read_signature(mbedtls_ecdsa_context *ctx,
* \param ctx The ECDSA context to use. This must be initialized
* and have a group and public key bound to it.
* \param hash The message hash that was signed. This must be a readable
- * buffer of length \p size Bytes.
+ * buffer of length \p hlen Bytes.
* \param hlen The size of the hash \p hash.
* \param sig The signature to read and verify. This must be a readable
* buffer of length \p slen Bytes.
diff --git a/thirdparty/mbedtls/include/mbedtls/ecp.h b/thirdparty/mbedtls/include/mbedtls/ecp.h
index 5402e74b00..d56069ec4e 100644
--- a/thirdparty/mbedtls/include/mbedtls/ecp.h
+++ b/thirdparty/mbedtls/include/mbedtls/ecp.h
@@ -1081,7 +1081,7 @@ int mbedtls_ecp_muladd_restartable(
*
* It only checks that the point is non-zero, has
* valid coordinates and lies on the curve. It does not verify
- * that it is indeed a multiple of \p G. This additional
+ * that it is indeed a multiple of \c G. This additional
* check is computationally more expensive, is not required
* by standards, and should not be necessary if the group
* used has a small cofactor. In particular, it is useless for
@@ -1106,7 +1106,7 @@ int mbedtls_ecp_check_pubkey(const mbedtls_ecp_group *grp,
const mbedtls_ecp_point *pt);
/**
- * \brief This function checks that an \p mbedtls_mpi is a
+ * \brief This function checks that an \c mbedtls_mpi is a
* valid private key for this curve.
*
* \note This function uses bare components rather than an
diff --git a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h
index 3ccf61c7fd..6b2248531b 100644
--- a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h
+++ b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h
@@ -186,8 +186,8 @@ void mbedtls_hmac_drbg_init(mbedtls_hmac_drbg_context *ctx);
* \param len The length of the personalization string.
* This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT
* and also at most
- * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len * 3 / 2
- * where \p entropy_len is the entropy length
+ * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len * 3 / 2
+ * where \c entropy_len is the entropy length
* described above.
*
* \return \c 0 if successful.
@@ -316,8 +316,8 @@ int mbedtls_hmac_drbg_update_ret(mbedtls_hmac_drbg_context *ctx,
* \param len The length of the additional data.
* This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT
* and also at most
- * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len
- * where \p entropy_len is the entropy length
+ * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len
+ * where \c entropy_len is the entropy length
* (see mbedtls_hmac_drbg_set_entropy_len()).
*
* \return \c 0 if successful.
diff --git a/thirdparty/mbedtls/include/mbedtls/pk.h b/thirdparty/mbedtls/include/mbedtls/pk.h
index ec83551368..0e9d58aec6 100644
--- a/thirdparty/mbedtls/include/mbedtls/pk.h
+++ b/thirdparty/mbedtls/include/mbedtls/pk.h
@@ -395,7 +395,7 @@ int mbedtls_pk_can_do(const mbedtls_pk_context *ctx, mbedtls_pk_type_t type);
*
* \return 0 on success (signature is valid),
* #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid
- * signature in sig but its length is less than \p siglen,
+ * signature in \p sig but its length is less than \p sig_len,
* or a specific error code.
*
* \note For RSA keys, the default padding type is PKCS#1 v1.5.
@@ -459,7 +459,7 @@ int mbedtls_pk_verify_restartable(mbedtls_pk_context *ctx,
* #MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be
* used for this type of signatures,
* #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid
- * signature in sig but its length is less than \p siglen,
+ * signature in \p sig but its length is less than \p sig_len,
* or a specific error code.
*
* \note If hash_len is 0, then the length associated with md_alg
diff --git a/thirdparty/mbedtls/include/mbedtls/platform.h b/thirdparty/mbedtls/include/mbedtls/platform.h
index 9033852be1..d6faa7eda6 100644
--- a/thirdparty/mbedtls/include/mbedtls/platform.h
+++ b/thirdparty/mbedtls/include/mbedtls/platform.h
@@ -144,6 +144,8 @@ extern "C" {
#if defined(MBEDTLS_PLATFORM_MEMORY)
#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && \
defined(MBEDTLS_PLATFORM_CALLOC_MACRO)
+#undef mbedtls_free
+#undef mbedtls_calloc
#define mbedtls_free MBEDTLS_PLATFORM_FREE_MACRO
#define mbedtls_calloc MBEDTLS_PLATFORM_CALLOC_MACRO
#else
@@ -165,6 +167,8 @@ int mbedtls_platform_set_calloc_free(void *(*calloc_func)(size_t, size_t),
void (*free_func)(void *));
#endif /* MBEDTLS_PLATFORM_FREE_MACRO && MBEDTLS_PLATFORM_CALLOC_MACRO */
#else /* !MBEDTLS_PLATFORM_MEMORY */
+#undef mbedtls_free
+#undef mbedtls_calloc
#define mbedtls_free free
#define mbedtls_calloc calloc
#endif /* MBEDTLS_PLATFORM_MEMORY && !MBEDTLS_PLATFORM_{FREE,CALLOC}_MACRO */
@@ -189,6 +193,7 @@ extern int (*mbedtls_fprintf)(FILE *stream, const char *format, ...);
int mbedtls_platform_set_fprintf(int (*fprintf_func)(FILE *stream, const char *,
...));
#else
+#undef mbedtls_fprintf
#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO)
#define mbedtls_fprintf MBEDTLS_PLATFORM_FPRINTF_MACRO
#else
@@ -213,6 +218,7 @@ extern int (*mbedtls_printf)(const char *format, ...);
*/
int mbedtls_platform_set_printf(int (*printf_func)(const char *, ...));
#else /* !MBEDTLS_PLATFORM_PRINTF_ALT */
+#undef mbedtls_printf
#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO)
#define mbedtls_printf MBEDTLS_PLATFORM_PRINTF_MACRO
#else
@@ -248,6 +254,7 @@ extern int (*mbedtls_snprintf)(char *s, size_t n, const char *format, ...);
int mbedtls_platform_set_snprintf(int (*snprintf_func)(char *s, size_t n,
const char *format, ...));
#else /* MBEDTLS_PLATFORM_SNPRINTF_ALT */
+#undef mbedtls_snprintf
#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO)
#define mbedtls_snprintf MBEDTLS_PLATFORM_SNPRINTF_MACRO
#else
@@ -284,6 +291,7 @@ extern int (*mbedtls_vsnprintf)(char *s, size_t n, const char *format, va_list a
int mbedtls_platform_set_vsnprintf(int (*vsnprintf_func)(char *s, size_t n,
const char *format, va_list arg));
#else /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */
+#undef mbedtls_vsnprintf
#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO)
#define mbedtls_vsnprintf MBEDTLS_PLATFORM_VSNPRINTF_MACRO
#else
@@ -308,6 +316,7 @@ extern void (*mbedtls_exit)(int status);
*/
int mbedtls_platform_set_exit(void (*exit_func)(int status));
#else
+#undef mbedtls_exit
#if defined(MBEDTLS_PLATFORM_EXIT_MACRO)
#define mbedtls_exit MBEDTLS_PLATFORM_EXIT_MACRO
#else
@@ -360,6 +369,8 @@ int mbedtls_platform_set_nv_seed(
int (*nv_seed_write_func)(unsigned char *buf, size_t buf_len)
);
#else
+#undef mbedtls_nv_seed_read
+#undef mbedtls_nv_seed_write
#if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) && \
defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO)
#define mbedtls_nv_seed_read MBEDTLS_PLATFORM_NV_SEED_READ_MACRO
diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h
index f8725ffb1e..37f07c0766 100644
--- a/thirdparty/mbedtls/include/mbedtls/rsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/rsa.h
@@ -260,15 +260,15 @@ int mbedtls_rsa_import_raw(mbedtls_rsa_context *ctx,
* \brief This function completes an RSA context from
* a set of imported core parameters.
*
- * To setup an RSA public key, precisely \p N and \p E
+ * To setup an RSA public key, precisely \c N and \c E
* must have been imported.
*
* To setup an RSA private key, sufficient information must
* be present for the other parameters to be derivable.
*
* The default implementation supports the following:
- * <ul><li>Derive \p P, \p Q from \p N, \p D, \p E.</li>
- * <li>Derive \p N, \p D from \p P, \p Q, \p E.</li></ul>
+ * <ul><li>Derive \c P, \c Q from \c N, \c D, \c E.</li>
+ * <li>Derive \c N, \c D from \c P, \c Q, \c E.</li></ul>
* Alternative implementations need not support these.
*
* If this function runs successfully, it guarantees that
@@ -537,7 +537,7 @@ int mbedtls_rsa_check_pub_priv(const mbedtls_rsa_context *pub,
* \note This function does not handle message padding.
*
* \note Make sure to set \p input[0] = 0 or ensure that
- * input is smaller than \p N.
+ * input is smaller than \c N.
*
* \return \c 0 on success.
* \return An \c MBEDTLS_ERR_RSA_XXX error code on failure.
@@ -1109,8 +1109,8 @@ int mbedtls_rsa_rsassa_pss_sign(mbedtls_rsa_context *ctx,
* verification using the mode from the context.
*
* \note For PKCS#1 v2.1 encoding, see comments on
- * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and
- * \p hash_id.
+ * mbedtls_rsa_rsassa_pss_verify() about \c md_alg and
+ * \c hash_id.
*
* \deprecated It is deprecated and discouraged to call this function
* in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h
index 26e4ec400c..cc9a27082b 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl.h
@@ -494,6 +494,7 @@
/* Dummy type used only for its size */
union mbedtls_ssl_premaster_secret {
+ unsigned char dummy; /* Make the union non-empty even with SSL disabled */
#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED)
unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */
#endif
@@ -1746,10 +1747,10 @@ void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl,
* \param own_cid The address of the readable buffer holding the CID we want
* the peer to use when sending encrypted messages to us.
* This may be \c NULL if \p own_cid_len is \c 0.
- * This parameter is unused if \p enabled is set to
+ * This parameter is unused if \p enable is set to
* MBEDTLS_SSL_CID_DISABLED.
* \param own_cid_len The length of \p own_cid.
- * This parameter is unused if \p enabled is set to
+ * This parameter is unused if \p enable is set to
* MBEDTLS_SSL_CID_DISABLED.
*
* \note The value of \p own_cid_len must match the value of the
@@ -2573,8 +2574,8 @@ int mbedtls_ssl_session_load(mbedtls_ssl_session *session,
*
* \param session The session structure to be saved.
* \param buf The buffer to write the serialized data to. It must be a
- * writeable buffer of at least \p len bytes, or may be \c
- * NULL if \p len is \c 0.
+ * writeable buffer of at least \p buf_len bytes, or may be \c
+ * NULL if \p buf_len is \c 0.
* \param buf_len The number of bytes available for writing in \p buf.
* \param olen The size in bytes of the data that has been or would have
* been written. It must point to a valid \c size_t.
@@ -2659,7 +2660,7 @@ void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config *conf,
* record headers.
*
* \return \c 0 on success.
- * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if \p own_cid_len
+ * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if \p len
* is too large.
*/
int mbedtls_ssl_conf_cid(mbedtls_ssl_config *conf, size_t len,
diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h
index 0ef52138fd..1ae06e6868 100644
--- a/thirdparty/mbedtls/include/mbedtls/version.h
+++ b/thirdparty/mbedtls/include/mbedtls/version.h
@@ -38,16 +38,16 @@
*/
#define MBEDTLS_VERSION_MAJOR 2
#define MBEDTLS_VERSION_MINOR 28
-#define MBEDTLS_VERSION_PATCH 3
+#define MBEDTLS_VERSION_PATCH 4
/**
* The single version number has the following structure:
* MMNNPP00
* Major version | Minor version | Patch version
*/
-#define MBEDTLS_VERSION_NUMBER 0x021C0300
-#define MBEDTLS_VERSION_STRING "2.28.3"
-#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.3"
+#define MBEDTLS_VERSION_NUMBER 0x021C0400
+#define MBEDTLS_VERSION_STRING "2.28.4"
+#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.4"
#if defined(MBEDTLS_VERSION_C)
diff --git a/thirdparty/mbedtls/include/mbedtls/x509_crt.h b/thirdparty/mbedtls/include/mbedtls/x509_crt.h
index 466611f797..d436635a5e 100644
--- a/thirdparty/mbedtls/include/mbedtls/x509_crt.h
+++ b/thirdparty/mbedtls/include/mbedtls/x509_crt.h
@@ -509,7 +509,7 @@ int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path);
* \param san_buf The buffer holding the raw data item of the subject
* alternative name.
* \param san The target structure to populate with the parsed presentation
- * of the subject alternative name encoded in \p san_raw.
+ * of the subject alternative name encoded in \p san_buf.
*
* \note Only "dnsName" and "otherName" of type hardware_module_name
* as defined in RFC 4180 is supported.
@@ -517,7 +517,7 @@ int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path);
* \note This function should be called on a single raw data of
* subject alternative name. For example, after successful
* certificate parsing, one must iterate on every item in the
- * \p crt->subject_alt_names sequence, and pass it to
+ * \c crt->subject_alt_names sequence, and pass it to
* this function.
*
* \warning The target structure contains pointers to the raw data of the
diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c
index f08a21f595..c506709500 100644
--- a/thirdparty/mbedtls/library/aes.c
+++ b/thirdparty/mbedtls/library/aes.c
@@ -58,6 +58,8 @@ static int aes_padlock_ace = -1;
/*
* Forward S-box
*/
+#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_DEC_ALT)
static const unsigned char FSb[256] =
{
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
@@ -93,6 +95,8 @@ static const unsigned char FSb[256] =
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};
+#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */
/*
* Forward tables
@@ -164,6 +168,7 @@ static const unsigned char FSb[256] =
V(C3, 41, 41, 82), V(B0, 99, 99, 29), V(77, 2D, 2D, 5A), V(11, 0F, 0F, 1E), \
V(CB, B0, B0, 7B), V(FC, 54, 54, A8), V(D6, BB, BB, 6D), V(3A, 16, 16, 2C)
+#if !defined(MBEDTLS_AES_ENCRYPT_ALT)
#define V(a, b, c, d) 0x##a##b##c##d
static const uint32_t FT0[256] = { FT };
#undef V
@@ -184,8 +189,11 @@ static const uint32_t FT3[256] = { FT };
#endif /* !MBEDTLS_AES_FEWER_TABLES */
+#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) */
+
#undef FT
+#if !defined(MBEDTLS_AES_DECRYPT_ALT)
/*
* Reverse S-box
*/
@@ -224,6 +232,7 @@ static const unsigned char RSb[256] =
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};
+#endif /* defined(MBEDTLS_AES_DECRYPT_ALT)) */
/*
* Reverse tables
@@ -295,6 +304,8 @@ static const unsigned char RSb[256] =
V(71, 01, A8, 39), V(DE, B3, 0C, 08), V(9C, E4, B4, D8), V(90, C1, 56, 64), \
V(61, 84, CB, 7B), V(70, B6, 32, D5), V(74, 5C, 6C, 48), V(42, 57, B8, D0)
+#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT)
+
#define V(a, b, c, d) 0x##a##b##c##d
static const uint32_t RT0[256] = { RT };
#undef V
@@ -315,8 +326,11 @@ static const uint32_t RT3[256] = { RT };
#endif /* !MBEDTLS_AES_FEWER_TABLES */
+#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */
+
#undef RT
+#if !defined(MBEDTLS_AES_SETKEY_ENC_ALT)
/*
* Round constants
*/
@@ -326,31 +340,44 @@ static const uint32_t RCON[10] =
0x00000010, 0x00000020, 0x00000040, 0x00000080,
0x0000001B, 0x00000036
};
+#endif /* !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */
#else /* MBEDTLS_AES_ROM_TABLES */
/*
* Forward S-box & tables
*/
+#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_DEC_ALT)
static unsigned char FSb[256];
+#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */
+#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT)
static uint32_t FT0[256];
#if !defined(MBEDTLS_AES_FEWER_TABLES)
static uint32_t FT1[256];
static uint32_t FT2[256];
static uint32_t FT3[256];
#endif /* !MBEDTLS_AES_FEWER_TABLES */
+#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */
/*
* Reverse S-box & tables
*/
+#if !(defined(MBEDTLS_AES_SETKEY_ENC_ALT) && defined(MBEDTLS_AES_DECRYPT_ALT))
static unsigned char RSb[256];
+#endif /* !(defined(MBEDTLS_AES_SETKEY_ENC_ALT) && defined(MBEDTLS_AES_DECRYPT_ALT)) */
+
+#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT)
static uint32_t RT0[256];
#if !defined(MBEDTLS_AES_FEWER_TABLES)
static uint32_t RT1[256];
static uint32_t RT2[256];
static uint32_t RT3[256];
#endif /* !MBEDTLS_AES_FEWER_TABLES */
+#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */
+#if !defined(MBEDTLS_AES_SETKEY_ENC_ALT)
/*
* Round constants
*/
@@ -428,6 +455,7 @@ static void aes_gen_tables(void)
x = RSb[i];
+#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT)
RT0[i] = ((uint32_t) MUL(0x0E, x)) ^
((uint32_t) MUL(0x09, x) << 8) ^
((uint32_t) MUL(0x0D, x) << 16) ^
@@ -438,9 +466,12 @@ static void aes_gen_tables(void)
RT2[i] = ROTL8(RT1[i]);
RT3[i] = ROTL8(RT2[i]);
#endif /* !MBEDTLS_AES_FEWER_TABLES */
+#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */
}
}
+#endif /* !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */
+
#undef ROTL8
#endif /* MBEDTLS_AES_ROM_TABLES */
@@ -521,6 +552,9 @@ void mbedtls_aes_xts_free(mbedtls_aes_xts_context *ctx)
(defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2)
#define MAY_NEED_TO_ALIGN
#endif
+
+#if defined(MAY_NEED_TO_ALIGN) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_ENC_ALT)
static unsigned mbedtls_aes_rk_offset(uint32_t *buf)
{
#if defined(MAY_NEED_TO_ALIGN)
@@ -557,6 +591,8 @@ static unsigned mbedtls_aes_rk_offset(uint32_t *buf)
return 0;
}
+#endif /* defined(MAY_NEED_TO_ALIGN) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) || \
+ !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */
/*
* AES key schedule (encryption)
diff --git a/thirdparty/mbedtls/library/aesni.c b/thirdparty/mbedtls/library/aesni.c
index c909f654c6..866b6cbfbf 100644
--- a/thirdparty/mbedtls/library/aesni.c
+++ b/thirdparty/mbedtls/library/aesni.c
@@ -41,6 +41,8 @@
#if MBEDTLS_AESNI_HAVE_CODE == 2
#if !defined(_WIN32)
#include <cpuid.h>
+#else
+#include <intrin.h>
#endif
#include <immintrin.h>
#endif
diff --git a/thirdparty/mbedtls/library/certs.c b/thirdparty/mbedtls/library/certs.c
index af1f98cb02..5b2948d652 100644
--- a/thirdparty/mbedtls/library/certs.c
+++ b/thirdparty/mbedtls/library/certs.c
@@ -38,69 +38,69 @@
/* This is taken from tests/data_files/test-ca2.crt */
/* BEGIN FILE string macro TEST_CA_CRT_EC_PEM tests/data_files/test-ca2.crt */
-#define TEST_CA_CRT_EC_PEM \
- "-----BEGIN CERTIFICATE-----\r\n" \
- "MIICBDCCAYigAwIBAgIJAMFD4n5iQ8zoMAwGCCqGSM49BAMCBQAwPjELMAkGA1UE\r\n" \
- "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \
- "IEVDIENBMB4XDTE5MDIxMDE0NDQwMFoXDTI5MDIxMDE0NDQwMFowPjELMAkGA1UE\r\n" \
- "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \
- "IEVDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEw9orNEE3WC+HVv78ibopQ0tO\r\n" \
- "4G7DDldTMzlY1FK0kZU5CyPfXxckYkj8GpUpziwth8KIUoCv1mqrId240xxuWLjK\r\n" \
- "6LJpjvNBrSnDtF91p0dv1RkpVWmaUzsgtGYWYDMeo1AwTjAMBgNVHRMEBTADAQH/\r\n" \
- "MB0GA1UdDgQWBBSdbSAkSQE/K8t4tRm8fiTJ2/s2fDAfBgNVHSMEGDAWgBSdbSAk\r\n" \
- "SQE/K8t4tRm8fiTJ2/s2fDAMBggqhkjOPQQDAgUAA2gAMGUCMFHKrjAPpHB0BN1a\r\n" \
- "LH8TwcJ3vh0AxeKZj30mRdOKBmg/jLS3rU3g8VQBHpn8sOTTBwIxANxPO5AerimZ\r\n" \
- "hCjMe0d4CTHf1gFZMF70+IqEP+o5VHsIp2Cqvflb0VGWFC5l9a4cQg==\r\n" \
+#define TEST_CA_CRT_EC_PEM \
+ "-----BEGIN CERTIFICATE-----\r\n" \
+ "MIICBDCCAYigAwIBAgIJAMFD4n5iQ8zoMAwGCCqGSM49BAMCBQAwPjELMAkGA1UE\r\n" \
+ "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \
+ "IEVDIENBMB4XDTE5MDIxMDE0NDQwMFoXDTI5MDIxMDE0NDQwMFowPjELMAkGA1UE\r\n" \
+ "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \
+ "IEVDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEw9orNEE3WC+HVv78ibopQ0tO\r\n" \
+ "4G7DDldTMzlY1FK0kZU5CyPfXxckYkj8GpUpziwth8KIUoCv1mqrId240xxuWLjK\r\n" \
+ "6LJpjvNBrSnDtF91p0dv1RkpVWmaUzsgtGYWYDMeo1AwTjAMBgNVHRMEBTADAQH/\r\n" \
+ "MB0GA1UdDgQWBBSdbSAkSQE/K8t4tRm8fiTJ2/s2fDAfBgNVHSMEGDAWgBSdbSAk\r\n" \
+ "SQE/K8t4tRm8fiTJ2/s2fDAMBggqhkjOPQQDAgUAA2gAMGUCMFHKrjAPpHB0BN1a\r\n" \
+ "LH8TwcJ3vh0AxeKZj30mRdOKBmg/jLS3rU3g8VQBHpn8sOTTBwIxANxPO5AerimZ\r\n" \
+ "hCjMe0d4CTHf1gFZMF70+IqEP+o5VHsIp2Cqvflb0VGWFC5l9a4cQg==\r\n" \
"-----END CERTIFICATE-----\r\n"
/* END FILE */
/* This is generated from tests/data_files/test-ca2.crt.der using `xxd -i`. */
/* BEGIN FILE binary macro TEST_CA_CRT_EC_DER tests/data_files/test-ca2.crt.der */
-#define TEST_CA_CRT_EC_DER { \
- 0x30, 0x82, 0x02, 0x04, 0x30, 0x82, 0x01, 0x88, 0xa0, 0x03, 0x02, 0x01, \
- 0x02, 0x02, 0x09, 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, \
- 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, \
- 0x05, 0x00, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \
- 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \
- 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \
- 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \
- 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \
- 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, \
- 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, \
- 0x0d, 0x32, 0x39, 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, \
- 0x30, 0x5a, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \
- 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \
- 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \
- 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \
- 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \
- 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, \
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, \
- 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xc3, 0xda, 0x2b, 0x34, 0x41, 0x37, \
- 0x58, 0x2f, 0x87, 0x56, 0xfe, 0xfc, 0x89, 0xba, 0x29, 0x43, 0x4b, 0x4e, \
- 0xe0, 0x6e, 0xc3, 0x0e, 0x57, 0x53, 0x33, 0x39, 0x58, 0xd4, 0x52, 0xb4, \
- 0x91, 0x95, 0x39, 0x0b, 0x23, 0xdf, 0x5f, 0x17, 0x24, 0x62, 0x48, 0xfc, \
- 0x1a, 0x95, 0x29, 0xce, 0x2c, 0x2d, 0x87, 0xc2, 0x88, 0x52, 0x80, 0xaf, \
- 0xd6, 0x6a, 0xab, 0x21, 0xdd, 0xb8, 0xd3, 0x1c, 0x6e, 0x58, 0xb8, 0xca, \
- 0xe8, 0xb2, 0x69, 0x8e, 0xf3, 0x41, 0xad, 0x29, 0xc3, 0xb4, 0x5f, 0x75, \
- 0xa7, 0x47, 0x6f, 0xd5, 0x19, 0x29, 0x55, 0x69, 0x9a, 0x53, 0x3b, 0x20, \
- 0xb4, 0x66, 0x16, 0x60, 0x33, 0x1e, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0c, \
- 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, \
- 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9d, \
- 0x6d, 0x20, 0x24, 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, \
- 0x7e, 0x24, 0xc9, 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x1f, 0x06, 0x03, 0x55, \
- 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, \
- 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, \
- 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \
- 0x3d, 0x04, 0x03, 0x02, 0x05, 0x00, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, \
- 0x30, 0x51, 0xca, 0xae, 0x30, 0x0f, 0xa4, 0x70, 0x74, 0x04, 0xdd, 0x5a, \
- 0x2c, 0x7f, 0x13, 0xc1, 0xc2, 0x77, 0xbe, 0x1d, 0x00, 0xc5, 0xe2, 0x99, \
- 0x8f, 0x7d, 0x26, 0x45, 0xd3, 0x8a, 0x06, 0x68, 0x3f, 0x8c, 0xb4, 0xb7, \
- 0xad, 0x4d, 0xe0, 0xf1, 0x54, 0x01, 0x1e, 0x99, 0xfc, 0xb0, 0xe4, 0xd3, \
- 0x07, 0x02, 0x31, 0x00, 0xdc, 0x4f, 0x3b, 0x90, 0x1e, 0xae, 0x29, 0x99, \
- 0x84, 0x28, 0xcc, 0x7b, 0x47, 0x78, 0x09, 0x31, 0xdf, 0xd6, 0x01, 0x59, \
- 0x30, 0x5e, 0xf4, 0xf8, 0x8a, 0x84, 0x3f, 0xea, 0x39, 0x54, 0x7b, 0x08, \
- 0xa7, 0x60, 0xaa, 0xbd, 0xf9, 0x5b, 0xd1, 0x51, 0x96, 0x14, 0x2e, 0x65, \
- 0xf5, 0xae, 0x1c, 0x42 \
+#define TEST_CA_CRT_EC_DER { \
+ 0x30, 0x82, 0x02, 0x04, 0x30, 0x82, 0x01, 0x88, 0xa0, 0x03, 0x02, 0x01, \
+ 0x02, 0x02, 0x09, 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, \
+ 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, \
+ 0x05, 0x00, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \
+ 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \
+ 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \
+ 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \
+ 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \
+ 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, \
+ 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, \
+ 0x0d, 0x32, 0x39, 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, \
+ 0x30, 0x5a, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \
+ 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \
+ 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \
+ 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \
+ 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \
+ 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, \
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, \
+ 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xc3, 0xda, 0x2b, 0x34, 0x41, 0x37, \
+ 0x58, 0x2f, 0x87, 0x56, 0xfe, 0xfc, 0x89, 0xba, 0x29, 0x43, 0x4b, 0x4e, \
+ 0xe0, 0x6e, 0xc3, 0x0e, 0x57, 0x53, 0x33, 0x39, 0x58, 0xd4, 0x52, 0xb4, \
+ 0x91, 0x95, 0x39, 0x0b, 0x23, 0xdf, 0x5f, 0x17, 0x24, 0x62, 0x48, 0xfc, \
+ 0x1a, 0x95, 0x29, 0xce, 0x2c, 0x2d, 0x87, 0xc2, 0x88, 0x52, 0x80, 0xaf, \
+ 0xd6, 0x6a, 0xab, 0x21, 0xdd, 0xb8, 0xd3, 0x1c, 0x6e, 0x58, 0xb8, 0xca, \
+ 0xe8, 0xb2, 0x69, 0x8e, 0xf3, 0x41, 0xad, 0x29, 0xc3, 0xb4, 0x5f, 0x75, \
+ 0xa7, 0x47, 0x6f, 0xd5, 0x19, 0x29, 0x55, 0x69, 0x9a, 0x53, 0x3b, 0x20, \
+ 0xb4, 0x66, 0x16, 0x60, 0x33, 0x1e, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0c, \
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, \
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9d, \
+ 0x6d, 0x20, 0x24, 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, \
+ 0x7e, 0x24, 0xc9, 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x1f, 0x06, 0x03, 0x55, \
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, \
+ 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, \
+ 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \
+ 0x3d, 0x04, 0x03, 0x02, 0x05, 0x00, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, \
+ 0x30, 0x51, 0xca, 0xae, 0x30, 0x0f, 0xa4, 0x70, 0x74, 0x04, 0xdd, 0x5a, \
+ 0x2c, 0x7f, 0x13, 0xc1, 0xc2, 0x77, 0xbe, 0x1d, 0x00, 0xc5, 0xe2, 0x99, \
+ 0x8f, 0x7d, 0x26, 0x45, 0xd3, 0x8a, 0x06, 0x68, 0x3f, 0x8c, 0xb4, 0xb7, \
+ 0xad, 0x4d, 0xe0, 0xf1, 0x54, 0x01, 0x1e, 0x99, 0xfc, 0xb0, 0xe4, 0xd3, \
+ 0x07, 0x02, 0x31, 0x00, 0xdc, 0x4f, 0x3b, 0x90, 0x1e, 0xae, 0x29, 0x99, \
+ 0x84, 0x28, 0xcc, 0x7b, 0x47, 0x78, 0x09, 0x31, 0xdf, 0xd6, 0x01, 0x59, \
+ 0x30, 0x5e, 0xf4, 0xf8, 0x8a, 0x84, 0x3f, 0xea, 0x39, 0x54, 0x7b, 0x08, \
+ 0xa7, 0x60, 0xaa, 0xbd, 0xf9, 0x5b, 0xd1, 0x51, 0x96, 0x14, 0x2e, 0x65, \
+ 0xf5, 0xae, 0x1c, 0x42 \
}
/* END FILE */
@@ -348,33 +348,33 @@
#define TEST_CA_KEY_RSA_PEM \
"-----BEGIN RSA PRIVATE KEY-----\r\n" \
"Proc-Type: 4,ENCRYPTED\r\n" \
- "DEK-Info: DES-EDE3-CBC,A8A95B05D5B7206B\r\n" \
+ "AES-128-CBC,781840E6B804AE83D2AF71127C4CE314\r\n" \
"\r\n" \
- "9Qd9GeArejl1GDVh2lLV1bHt0cPtfbh5h/5zVpAVaFpqtSPMrElp50Rntn9et+JA\r\n" \
- "7VOyboR+Iy2t/HU4WvA687k3Bppe9GwKHjHhtl//8xFKwZr3Xb5yO5JUP8AUctQq\r\n" \
- "Nb8CLlZyuUC+52REAAthdWgsX+7dJO4yabzUcQ22Tp9JSD0hiL43BlkWYUNK3dAo\r\n" \
- "PZlmiptjnzVTjg1MxsBSydZinWOLBV8/JQgxSPo2yD4uEfig28qbvQ2wNIn0pnAb\r\n" \
- "GxnSAOazkongEGfvcjIIs+LZN9gXFhxcOh6kc4Q/c99B7QWETwLLkYgZ+z1a9VY9\r\n" \
- "gEU7CwCxYCD+h9hY6FPmsK0/lC4O7aeRKpYq00rPPxs6i7phiexg6ax6yTMmArQq\r\n" \
- "QmK3TAsJm8V/J5AWpLEV6jAFgRGymGGHnof0DXzVWZidrcZJWTNuGEX90nB3ee2w\r\n" \
- "PXJEFWKoD3K3aFcSLdHYr3mLGxP7H9ThQai9VsycxZKS5kwvBKQ//YMrmFfwPk8x\r\n" \
- "vTeY4KZMaUrveEel5tWZC94RSMKgxR6cyE1nBXyTQnDOGbfpNNgBKxyKbINWoOJU\r\n" \
- "WJZAwlsQn+QzCDwpri7+sV1mS3gBE6UY7aQmnmiiaC2V3Hbphxct/en5QsfDOt1X\r\n" \
- "JczSfpRWLlbPznZg8OQh/VgCMA58N5DjOzTIK7sJJ5r+94ZBTCpgAMbF588f0NTR\r\n" \
- "KCe4yrxGJR7X02M4nvD4IwOlpsQ8xQxZtOSgXv4LkxvdU9XJJKWZ/XNKJeWztxSe\r\n" \
- "Z1vdTc2YfsDBA2SEv33vxHx2g1vqtw8SjDRT2RaQSS0QuSaMJimdOX6mTOCBKk1J\r\n" \
- "9Q5mXTrER+/LnK0jEmXsBXWA5bqqVZIyahXSx4VYZ7l7w/PHiUDtDgyRhMMKi4n2\r\n" \
- "iQvQcWSQTjrpnlJbca1/DkpRt3YwrvJwdqb8asZU2VrNETh5x0QVefDRLFiVpif/\r\n" \
- "tUaeAe/P1F8OkS7OIZDs1SUbv/sD2vMbhNkUoCms3/PvNtdnvgL4F0zhaDpKCmlT\r\n" \
- "P8vx49E7v5CyRNmED9zZg4o3wmMqrQO93PtTug3Eu9oVx1zPQM1NVMyBa2+f29DL\r\n" \
- "1nuTCeXdo9+ni45xx+jAI4DCwrRdhJ9uzZyC6962H37H6D+5naNvClFR1s6li1Gb\r\n" \
- "nqPoiy/OBsEx9CaDGcqQBp5Wme/3XW+6z1ISOx+igwNTVCT14mHdBMbya0eIKft5\r\n" \
- "X+GnwtgEMyCYyyWuUct8g4RzErcY9+yW9Om5Hzpx4zOuW4NPZgPDTgK+t2RSL/Yq\r\n" \
- "rE1njrgeGYcVeG3f+OftH4s6fPbq7t1A5ZgUscbLMBqr9tK+OqygR4EgKBPsH6Cz\r\n" \
- "L6zlv/2RV0qAHvVuDJcIDIgwY5rJtINEm32rhOeFNJwZS5MNIC1czXZx5//ugX7l\r\n" \
- "I4sy5nbVhwSjtAk8Xg5dZbdTZ6mIrb7xqH+fdakZor1khG7bC2uIwibD3cSl2XkR\r\n" \
- "wN48lslbHnqqagr6Xm1nNOSVl8C/6kbJEsMpLhAezfRtGwvOucoaE+WbeUNolGde\r\n" \
- "P/eQiddSf0brnpiLJRh7qZrl9XuqYdpUqnoEdMAfotDOID8OtV7gt8a48ad8VPW2\r\n" \
+ "etQ3xgGLbuYF9vR1km03TH5fwfly1hOlix0PtfQ+t9HG065vTtSEHYc/OyHwdy79\r\n" \
+ "NCLX5RUrPh06E/XlKzMNVHAXqkwFnIwNzRLsOozeP1L7iZEZb9QMeiN5Org+btCO\r\n" \
+ "bylXPB4YirfuE7GSJalWY/pq3FQtD33zTIKmNhXfVj3sbwGI/8D9XjaKUb8PODOB\r\n" \
+ "skOalmx6RvYRvg0lmRxB3+T3wejIsrrDPweYqte9B6dVHIVG1ZmvoA6/wnKZZZeV\r\n" \
+ "sjj8OpL3OwUBrjuGSknE9Rs6kCuSCbHOYVK8VzcZmCYpie0TFnb3Sk8M6vjfW+45\r\n" \
+ "U7WUMlSAPxKH6lJDzWdwHqLvsVJwuNnaAaBXg9/8U/rzQEWuq8Ar3s8fw2Jg3F1G\r\n" \
+ "L6N5ZAEfCz3Sa0N9WKafR/RSQj+rq8Z3w4POAafhbzk249uo5K8B1Z3cQwLxeXIl\r\n" \
+ "UbRQz1TZy4oNTfQzCahYruPNyvwgTkfwAFFvbLAdaiJd2ZtLBoqYE64TYakYnvcC\r\n" \
+ "itim1bmySIKoxlMfBGFmMuF03epT0pSx701jlGzGi0l0m16NEjoVxDwo5j93SmiM\r\n" \
+ "sQdjC1lOGk2iCLkphIQqHFjFJYWjvh1UUIqWZf+ZWOOxlf4x9a1pUVj6FvtECxNB\r\n" \
+ "/mA/m4Iq4LAuVXHE1MpHeq067lJ6wWlrsb2WVmiNGfQ2AC7fMtpcPuunBVT9NV1m\r\n" \
+ "1rbDzIgLIWAzqz/cy3N8Q8vfxnrFtmNUyM191Zyq+YF14hIKWX9J1qR4LXwWAzVV\r\n" \
+ "UrC8IL4pA2mtRkW4qFsB0EmHAxO/cedDTPjVFty5WSzhNuvYZxX45HAkGIfK6d21\r\n" \
+ "7WHPhHG+zaaUTWMUVixB0IcKp6RecjYPFzBHS0YeX88Ue2cyT/90jMiQ9ssOgRrG\r\n" \
+ "ZJRJvZAc3TSCnY9sNPYoGrJPiZuCnlUj3ENNurYVy12ai0WFxwnNUZjRUhDS6hjm\r\n" \
+ "cDHD5TlI9MZ6M+Mb/Bw4Ig8HuTHOtQBYD9vhtXsG+B7H/j6cS+1umaKjrnG/kK4W\r\n" \
+ "R6YXwM2faAi+DwgjjoMXSzRqSTF8PdTIWbAXo3bc2qsXPTMBA8PEp4nb5scHZ4Ts\r\n" \
+ "EcBNp2jv0j4gBkRmGIab17cWMrlagjFy89DhqZUFwKdeZs+yJ92A5xstWxOUfpEP\r\n" \
+ "90T/bsp1G5d7WW5fl2TRJvYJNDM+djkKIh0zCkduiZ36oVM6nDdbjmXqjQXopeSD\r\n" \
+ "gtOourBRF8g99W0fW8QT+yPhP0Pkyz6EG8eQO6Zwh439xdoVwu9jUzQAPmZ0uNeR\r\n" \
+ "xTXXihYyv72z27rInjLiIPXL25K9eDVLlcSR3RyG7YYgjdQAL2VJDLcBz5jox1uQ\r\n" \
+ "0guoD5wmfu2FWLqYE7HeTYntdY53lCflwq0GHRMjrrsVpx+5VDQ6Yi47Ny9SWLcp\r\n" \
+ "fPI3iBkXuGRWupzs6N4pQdSO0dU28KfpMM5QvFoLIn67brCHEQij4dgFrCTYEyBX\r\n" \
+ "9+jiNImUFYUhAFuxvUbfZt4O/ABLIElvHLfJs1oYCmI/nWpvLFqXB5rnzPNfEi0H\r\n" \
+ "PGGe1Hj/t+CJIp/6ios3yNy2QtXO754TZH2UVu51Ykyig5PFjZVoUkbRvHQYcWfU\r\n" \
"-----END RSA PRIVATE KEY-----\r\n"
/* END FILE */
@@ -501,72 +501,72 @@
/* This is taken from tests/data_files/server5.crt. */
/* BEGIN FILE string macro TEST_SRV_CRT_EC_PEM tests/data_files/server5.crt */
-#define TEST_SRV_CRT_EC_PEM \
- "-----BEGIN CERTIFICATE-----\r\n" \
- "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" \
- "A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" \
- "MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" \
- "A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" \
- "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" \
- "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" \
- "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" \
- "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\r\n" \
- "clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" \
- "CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\r\n" \
- "C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\r\n" \
- "fGa5kHvHARBPc8YAIVIqDvHH1Q==\r\n" \
+#define TEST_SRV_CRT_EC_PEM \
+ "-----BEGIN CERTIFICATE-----\r\n" \
+ "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" \
+ "A1UECgwIUG9sYXJTU0wxHDAaBgNVBAMME1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" \
+ "MjMwNjE1MDMzNDE4WhcNMzMwNjEyMDMzNDE4WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" \
+ "A1UECgwIUG9sYXJTU0wxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" \
+ "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" \
+ "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" \
+ "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" \
+ "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKDAhQb2xh\r\n" \
+ "clNTTDEcMBoGA1UEAwwTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" \
+ "CCqGSM49BAMCA2gAMGUCMAHFbGEzx8dZaUlIltT5s1QO9FvKmvFer4uRY3ntEy9S\r\n" \
+ "k7DCCozM86WWLjfzbJ78bwIxAJYRPF1CzNEiXPHb9O46ZPHKo2S5x//g/54RowAK\r\n" \
+ "uZz+hKPuMi6YY6cIm81jfeaSZQ==\r\n" \
"-----END CERTIFICATE-----\r\n"
/* END FILE */
/* This is generated from tests/data_files/server5.crt.der using `xxd -i`. */
/* BEGIN FILE binary macro TEST_SRV_CRT_EC_DER tests/data_files/server5.crt.der */
-#define TEST_SRV_CRT_EC_DER { \
- 0x30, 0x82, 0x02, 0x1f, 0x30, 0x82, 0x01, 0xa5, 0xa0, 0x03, 0x02, 0x01, \
- 0x02, 0x02, 0x01, 0x09, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \
- 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \
- 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \
- 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \
- 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, \
- 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, \
- 0x31, 0x33, 0x30, 0x39, 0x32, 0x34, 0x31, 0x35, 0x35, 0x32, 0x30, 0x34, \
- 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x39, 0x32, 0x32, 0x31, 0x35, 0x35, \
- 0x32, 0x30, 0x34, 0x5a, 0x30, 0x34, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \
- 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \
- 0x53, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \
- 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x59, \
- 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \
- 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, \
- 0x04, 0x37, 0xcc, 0x56, 0xd9, 0x76, 0x09, 0x1e, 0x5a, 0x72, 0x3e, 0xc7, \
- 0x59, 0x2d, 0xff, 0x20, 0x6e, 0xee, 0x7c, 0xf9, 0x06, 0x91, 0x74, 0xd0, \
- 0xad, 0x14, 0xb5, 0xf7, 0x68, 0x22, 0x59, 0x62, 0x92, 0x4e, 0xe5, 0x00, \
- 0xd8, 0x23, 0x11, 0xff, 0xea, 0x2f, 0xd2, 0x34, 0x5d, 0x5d, 0x16, 0xbd, \
- 0x8a, 0x88, 0xc2, 0x6b, 0x77, 0x0d, 0x55, 0xcd, 0x8a, 0x2a, 0x0e, 0xfa, \
- 0x01, 0xc8, 0xb4, 0xed, 0xff, 0xa3, 0x81, 0x9d, 0x30, 0x81, 0x9a, 0x30, \
- 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, \
- 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x50, 0x61, 0xa5, \
- 0x8f, 0xd4, 0x07, 0xd9, 0xd7, 0x82, 0x01, 0x0c, 0xe5, 0x65, 0x7f, 0x8c, \
- 0x63, 0x46, 0xa7, 0x13, 0xbe, 0x30, 0x6e, 0x06, 0x03, 0x55, 0x1d, 0x23, \
- 0x04, 0x67, 0x30, 0x65, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, 0x49, 0x01, \
- 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, 0xdb, 0xfb, \
- 0x36, 0x7c, 0xa1, 0x42, 0xa4, 0x40, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, \
- 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, \
- 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, \
- 0x72, 0x53, 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, \
- 0x03, 0x13, 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, \
- 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x82, 0x09, \
- 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, 0x30, 0x0a, 0x06, \
- 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x68, 0x00, \
- 0x30, 0x65, 0x02, 0x31, 0x00, 0x9a, 0x2c, 0x5c, 0xd7, 0xa6, 0xdb, 0xa2, \
- 0xe5, 0x64, 0x0d, 0xf0, 0xb9, 0x4e, 0xdd, 0xd7, 0x61, 0xd6, 0x13, 0x31, \
- 0xc7, 0xab, 0x73, 0x80, 0xbb, 0xd3, 0xd3, 0x73, 0x13, 0x54, 0xad, 0x92, \
- 0x0b, 0x5d, 0xab, 0xd0, 0xbc, 0xf7, 0xae, 0x2f, 0xe6, 0xa1, 0x21, 0x29, \
- 0x35, 0x95, 0xaa, 0x3e, 0x39, 0x02, 0x30, 0x21, 0x36, 0x7f, 0x9d, 0xc6, \
- 0x5d, 0xc6, 0x0b, 0xab, 0x27, 0xf2, 0x25, 0x1d, 0x3b, 0xf1, 0xcf, 0xf1, \
- 0x35, 0x25, 0x14, 0xe7, 0xe5, 0xf1, 0x97, 0xb5, 0x59, 0xe3, 0x5e, 0x15, \
- 0x7c, 0x66, 0xb9, 0x90, 0x7b, 0xc7, 0x01, 0x10, 0x4f, 0x73, 0xc6, 0x00, \
- 0x21, 0x52, 0x2a, 0x0e, 0xf1, 0xc7, 0xd5 \
+#define TEST_SRV_CRT_EC_DER { \
+ 0x30, 0x82, 0x02, 0x1f, 0x30, 0x82, 0x01, 0xa5, 0xa0, 0x03, 0x02, 0x01, \
+ 0x02, 0x02, 0x01, 0x09, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \
+ 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \
+ 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, \
+ 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, \
+ 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, \
+ 0x32, 0x33, 0x30, 0x36, 0x31, 0x35, 0x30, 0x33, 0x33, 0x34, 0x31, 0x38, \
+ 0x5a, 0x17, 0x0d, 0x33, 0x33, 0x30, 0x36, 0x31, 0x32, 0x30, 0x33, 0x33, \
+ 0x34, 0x31, 0x38, 0x5a, 0x30, 0x34, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \
+ 0x53, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, \
+ 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x59, \
+ 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, \
+ 0x04, 0x37, 0xcc, 0x56, 0xd9, 0x76, 0x09, 0x1e, 0x5a, 0x72, 0x3e, 0xc7, \
+ 0x59, 0x2d, 0xff, 0x20, 0x6e, 0xee, 0x7c, 0xf9, 0x06, 0x91, 0x74, 0xd0, \
+ 0xad, 0x14, 0xb5, 0xf7, 0x68, 0x22, 0x59, 0x62, 0x92, 0x4e, 0xe5, 0x00, \
+ 0xd8, 0x23, 0x11, 0xff, 0xea, 0x2f, 0xd2, 0x34, 0x5d, 0x5d, 0x16, 0xbd, \
+ 0x8a, 0x88, 0xc2, 0x6b, 0x77, 0x0d, 0x55, 0xcd, 0x8a, 0x2a, 0x0e, 0xfa, \
+ 0x01, 0xc8, 0xb4, 0xed, 0xff, 0xa3, 0x81, 0x9d, 0x30, 0x81, 0x9a, 0x30, \
+ 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, \
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x50, 0x61, 0xa5, \
+ 0x8f, 0xd4, 0x07, 0xd9, 0xd7, 0x82, 0x01, 0x0c, 0xe5, 0x65, 0x7f, 0x8c, \
+ 0x63, 0x46, 0xa7, 0x13, 0xbe, 0x30, 0x6e, 0x06, 0x03, 0x55, 0x1d, 0x23, \
+ 0x04, 0x67, 0x30, 0x65, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, 0x49, 0x01, \
+ 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, 0xdb, 0xfb, \
+ 0x36, 0x7c, 0xa1, 0x42, 0xa4, 0x40, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, \
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, \
+ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, \
+ 0x72, 0x53, 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, \
+ 0x03, 0x0c, 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, \
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x82, 0x09, \
+ 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, 0x30, 0x0a, 0x06, \
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x68, 0x00, \
+ 0x30, 0x65, 0x02, 0x30, 0x01, 0xc5, 0x6c, 0x61, 0x33, 0xc7, 0xc7, 0x59, \
+ 0x69, 0x49, 0x48, 0x96, 0xd4, 0xf9, 0xb3, 0x54, 0x0e, 0xf4, 0x5b, 0xca, \
+ 0x9a, 0xf1, 0x5e, 0xaf, 0x8b, 0x91, 0x63, 0x79, 0xed, 0x13, 0x2f, 0x52, \
+ 0x93, 0xb0, 0xc2, 0x0a, 0x8c, 0xcc, 0xf3, 0xa5, 0x96, 0x2e, 0x37, 0xf3, \
+ 0x6c, 0x9e, 0xfc, 0x6f, 0x02, 0x31, 0x00, 0x96, 0x11, 0x3c, 0x5d, 0x42, \
+ 0xcc, 0xd1, 0x22, 0x5c, 0xf1, 0xdb, 0xf4, 0xee, 0x3a, 0x64, 0xf1, 0xca, \
+ 0xa3, 0x64, 0xb9, 0xc7, 0xff, 0xe0, 0xff, 0x9e, 0x11, 0xa3, 0x00, 0x0a, \
+ 0xb9, 0x9c, 0xfe, 0x84, 0xa3, 0xee, 0x32, 0x2e, 0x98, 0x63, 0xa7, 0x08, \
+ 0x9b, 0xcd, 0x63, 0x7d, 0xe6, 0x92, 0x65 \
}
/* END FILE */
diff --git a/thirdparty/mbedtls/library/constant_time.c b/thirdparty/mbedtls/library/constant_time.c
index 527930129b..c0f53bbe77 100644
--- a/thirdparty/mbedtls/library/constant_time.c
+++ b/thirdparty/mbedtls/library/constant_time.c
@@ -263,40 +263,6 @@ unsigned mbedtls_ct_uint_if(unsigned condition,
#if defined(MBEDTLS_BIGNUM_C)
-/** Select between two sign values without branches.
- *
- * This is functionally equivalent to `condition ? if1 : if0` but uses only bit
- * operations in order to avoid branches.
- *
- * \note if1 and if0 must be either 1 or -1, otherwise the result
- * is undefined.
- *
- * \param condition Condition to test; must be either 0 or 1.
- * \param if1 The first sign; must be either +1 or -1.
- * \param if0 The second sign; must be either +1 or -1.
- *
- * \return \c if1 if \p condition is nonzero, otherwise \c if0.
- * */
-static int mbedtls_ct_cond_select_sign(unsigned char condition,
- int if1,
- int if0)
-{
- /* In order to avoid questions about what we can reasonably assume about
- * the representations of signed integers, move everything to unsigned
- * by taking advantage of the fact that if1 and if0 are either +1 or -1. */
- unsigned uif1 = if1 + 1;
- unsigned uif0 = if0 + 1;
-
- /* condition was 0 or 1, mask is 0 or 2 as are uif1 and uif0 */
- const unsigned mask = condition << 1;
-
- /* select uif1 or uif0 */
- unsigned ur = (uif0 & ~mask) | (uif1 & mask);
-
- /* ur is now 0 or 2, convert back to -1 or +1 */
- return (int) ur - 1;
-}
-
void mbedtls_ct_mpi_uint_cond_assign(size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
@@ -559,7 +525,7 @@ int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X,
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
- X->s = mbedtls_ct_cond_select_sign(assign, Y->s, X->s);
+ X->s = (int) mbedtls_ct_uint_if(assign, Y->s, X->s);
mbedtls_ct_mpi_uint_cond_assign(Y->n, X->p, Y->p, assign);
@@ -599,8 +565,8 @@ int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X,
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n));
s = X->s;
- X->s = mbedtls_ct_cond_select_sign(swap, Y->s, X->s);
- Y->s = mbedtls_ct_cond_select_sign(swap, s, Y->s);
+ X->s = (int) mbedtls_ct_uint_if(swap, Y->s, X->s);
+ Y->s = (int) mbedtls_ct_uint_if(swap, s, Y->s);
for (i = 0; i < X->n; i++) {
diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c
index 652c5cbc28..45d925437e 100644
--- a/thirdparty/mbedtls/library/ctr_drbg.c
+++ b/thirdparty/mbedtls/library/ctr_drbg.c
@@ -30,6 +30,7 @@
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
+#include <limits.h>
#include <string.h>
#if defined(MBEDTLS_FS_IO)
diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c
index ab8b3524d4..3e794b5565 100644
--- a/thirdparty/mbedtls/library/debug.c
+++ b/thirdparty/mbedtls/library/debug.c
@@ -30,6 +30,7 @@
#include <stdio.h>
#include <string.h>
+/* DEBUG_BUF_SIZE must be at least 2 */
#define DEBUG_BUF_SIZE 512
static int debug_threshold = 0;
@@ -69,6 +70,8 @@ void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level,
char str[DEBUG_BUF_SIZE];
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ MBEDTLS_STATIC_ASSERT(DEBUG_BUF_SIZE >= 2, "DEBUG_BUF_SIZE too small");
+
if (NULL == ssl ||
NULL == ssl->conf ||
NULL == ssl->conf->f_dbg ||
@@ -80,10 +83,15 @@ void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level,
ret = mbedtls_vsnprintf(str, DEBUG_BUF_SIZE, format, argp);
va_end(argp);
- if (ret >= 0 && ret < DEBUG_BUF_SIZE - 1) {
- str[ret] = '\n';
- str[ret + 1] = '\0';
+ if (ret < 0) {
+ ret = 0;
+ } else {
+ if (ret >= DEBUG_BUF_SIZE - 1) {
+ ret = DEBUG_BUF_SIZE - 2;
+ }
}
+ str[ret] = '\n';
+ str[ret + 1] = '\0';
debug_send_line(ssl, level, file, line, str);
}
diff --git a/thirdparty/mbedtls/library/ecdh.c b/thirdparty/mbedtls/library/ecdh.c
index 2007e1670c..9f002d9682 100644
--- a/thirdparty/mbedtls/library/ecdh.c
+++ b/thirdparty/mbedtls/library/ecdh.c
@@ -20,7 +20,7 @@
/*
* References:
*
- * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ * SEC1 https://www.secg.org/sec1-v2.pdf
* RFC 4492
*/
diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c
index 42a65dcb59..1f0b37dfd4 100644
--- a/thirdparty/mbedtls/library/ecdsa.c
+++ b/thirdparty/mbedtls/library/ecdsa.c
@@ -20,7 +20,7 @@
/*
* References:
*
- * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ * SEC1 https://www.secg.org/sec1-v2.pdf
*/
#include "common.h"
diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c
index 9051490b12..5b60291cc8 100644
--- a/thirdparty/mbedtls/library/ecp.c
+++ b/thirdparty/mbedtls/library/ecp.c
@@ -20,13 +20,15 @@
/*
* References:
*
- * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ * SEC1 https://www.secg.org/sec1-v2.pdf
* GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone
* FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
* RFC 4492 for the related TLS structures and constants
+ * - https://www.rfc-editor.org/rfc/rfc4492
* RFC 7748 for the Curve448 and Curve25519 curve definitions
+ * - https://www.rfc-editor.org/rfc/rfc7748
*
- * [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf
+ * [Curve25519] https://cr.yp.to/ecdh/curve25519-20060209.pdf
*
* [2] CORON, Jean-S'ebastien. Resistance against differential power analysis
* for elliptic curve cryptosystems. In : Cryptographic Hardware and
@@ -2591,6 +2593,7 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
void *p_rng)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ int have_rng = 1;
size_t i;
unsigned char b;
mbedtls_ecp_point RP;
@@ -2623,9 +2626,8 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
/* RP.X might be slightly larger than P, so reduce it */
MOD_ADD(RP.X);
- /* Randomize coordinates of the starting point */
- int have_rng = 1;
#if defined(MBEDTLS_ECP_NO_INTERNAL_RNG)
+ /* Derandomize coordinates of the starting point */
if (f_rng == NULL) {
have_rng = 0;
}
diff --git a/thirdparty/mbedtls/library/ecp_invasive.h b/thirdparty/mbedtls/library/ecp_invasive.h
index 18815be089..d6f6f9565e 100644
--- a/thirdparty/mbedtls/library/ecp_invasive.h
+++ b/thirdparty/mbedtls/library/ecp_invasive.h
@@ -61,7 +61,7 @@ void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits);
* This is the bit-size of the key minus 1:
* 254 for Curve25519 or 447 for Curve448.
* \param d The randomly generated key. This is a number of size
- * exactly \p n_bits + 1 bits, with the least significant bits
+ * exactly \p high_bit + 1 bits, with the least significant bits
* masked as specified in [Curve25519] and in [RFC7748] §5.
* \param f_rng The RNG function.
* \param p_rng The RNG context to be passed to \p f_rng.
@@ -69,7 +69,7 @@ void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits);
* \return \c 0 on success.
* \return \c MBEDTLS_ERR_ECP_xxx or MBEDTLS_ERR_MPI_xxx on failure.
*/
-int mbedtls_ecp_gen_privkey_mx(size_t n_bits,
+int mbedtls_ecp_gen_privkey_mx(size_t high_bit,
mbedtls_mpi *d,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng);
diff --git a/thirdparty/mbedtls/library/entropy.c b/thirdparty/mbedtls/library/entropy.c
index af78acc1bf..e9a7ae63d3 100644
--- a/thirdparty/mbedtls/library/entropy.c
+++ b/thirdparty/mbedtls/library/entropy.c
@@ -31,6 +31,8 @@
#include "mbedtls/entropy_poll.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
+#include "mbedtls/sha256.h"
+#include "mbedtls/sha512.h"
#include <string.h>
diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c
index bdd82ac6fe..2c2a876b02 100644
--- a/thirdparty/mbedtls/library/net_sockets.c
+++ b/thirdparty/mbedtls/library/net_sockets.c
@@ -90,6 +90,7 @@ static int wsa_init_done = 0;
#include <errno.h>
#define IS_EINTR(ret) ((ret) == EINTR)
+#define SOCKET int
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
@@ -494,13 +495,13 @@ int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout)
FD_ZERO(&read_fds);
if (rw & MBEDTLS_NET_POLL_READ) {
rw &= ~MBEDTLS_NET_POLL_READ;
- FD_SET(fd, &read_fds);
+ FD_SET((SOCKET) fd, &read_fds);
}
FD_ZERO(&write_fds);
if (rw & MBEDTLS_NET_POLL_WRITE) {
rw &= ~MBEDTLS_NET_POLL_WRITE;
- FD_SET(fd, &write_fds);
+ FD_SET((SOCKET) fd, &write_fds);
}
if (rw != 0) {
@@ -608,7 +609,7 @@ int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf,
}
FD_ZERO(&read_fds);
- FD_SET(fd, &read_fds);
+ FD_SET((SOCKET) fd, &read_fds);
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
diff --git a/thirdparty/mbedtls/library/pk.c b/thirdparty/mbedtls/library/pk.c
index d46a93461a..12f4120225 100644
--- a/thirdparty/mbedtls/library/pk.c
+++ b/thirdparty/mbedtls/library/pk.c
@@ -646,6 +646,7 @@ int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk,
psa_key_type_t key_type;
size_t bits;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ psa_status_t status;
/* export the private key material in the format PSA wants */
if (mbedtls_pk_get_type(pk) != MBEDTLS_PK_ECKEY) {
@@ -668,7 +669,9 @@ int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk,
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(hash_alg));
/* import private key into PSA */
- if (PSA_SUCCESS != psa_import_key(&attributes, d, d_len, key)) {
+ status = psa_import_key(&attributes, d, d_len, key);
+ mbedtls_platform_zeroize(d, sizeof(d));
+ if (status != PSA_SUCCESS) {
return MBEDTLS_ERR_PK_HW_ACCEL_FAILED;
}
diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c
index deaff0b310..76fe0c81e4 100644
--- a/thirdparty/mbedtls/library/pkparse.c
+++ b/thirdparty/mbedtls/library/pkparse.c
@@ -1235,6 +1235,8 @@ int mbedtls_pk_parse_key(mbedtls_pk_context *pk,
mbedtls_pem_context pem;
#endif
+ (void) pk_info;
+
PK_VALIDATE_RET(pk != NULL);
if (keylen == 0) {
return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
diff --git a/thirdparty/mbedtls/library/pkwrite.c b/thirdparty/mbedtls/library/pkwrite.c
index 0107f20b1d..88e685503b 100644
--- a/thirdparty/mbedtls/library/pkwrite.c
+++ b/thirdparty/mbedtls/library/pkwrite.c
@@ -178,6 +178,11 @@ int mbedtls_pk_write_pubkey(unsigned char **p, unsigned char *start,
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t len = 0;
+ (void) p;
+ (void) start;
+ (void) key;
+ (void) ret;
+
PK_VALIDATE_RET(p != NULL);
PK_VALIDATE_RET(*p != NULL);
PK_VALIDATE_RET(start != NULL);
@@ -313,6 +318,10 @@ int mbedtls_pk_write_key_der(mbedtls_pk_context *key, unsigned char *buf, size_t
unsigned char *c;
size_t len = 0;
+ (void) ret;
+ (void) c;
+ (void) key;
+
PK_VALIDATE_RET(key != NULL);
if (size == 0) {
return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c
index eeb1d503a8..58c2c2730d 100644
--- a/thirdparty/mbedtls/library/timing.c
+++ b/thirdparty/mbedtls/library/timing.c
@@ -17,6 +17,8 @@
* limitations under the License.
*/
+#include <string.h>
+
#include "common.h"
#include "mbedtls/platform.h"
@@ -233,17 +235,20 @@ volatile int mbedtls_timing_alarmed = 0;
unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset)
{
- struct _hr_time *t = (struct _hr_time *) val;
+ struct _hr_time t;
if (reset) {
- QueryPerformanceCounter(&t->start);
+ QueryPerformanceCounter(&t.start);
+ memcpy(val, &t, sizeof(struct _hr_time));
return 0;
} else {
unsigned long delta;
LARGE_INTEGER now, hfreq;
+ /* We can't safely cast val because it may not be aligned, so use memcpy */
+ memcpy(&t, val, sizeof(struct _hr_time));
QueryPerformanceCounter(&now);
QueryPerformanceFrequency(&hfreq);
- delta = (unsigned long) ((now.QuadPart - t->start.QuadPart) * 1000ul
+ delta = (unsigned long) ((now.QuadPart - t.start.QuadPart) * 1000ul
/ hfreq.QuadPart);
return delta;
}
@@ -279,17 +284,20 @@ void mbedtls_set_alarm(int seconds)
unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset)
{
- struct _hr_time *t = (struct _hr_time *) val;
+ struct _hr_time t;
if (reset) {
- gettimeofday(&t->start, NULL);
+ gettimeofday(&t.start, NULL);
+ memcpy(val, &t, sizeof(struct _hr_time));
return 0;
} else {
unsigned long delta;
struct timeval now;
+ /* We can't safely cast val because it may not be aligned, so use memcpy */
+ memcpy(&t, val, sizeof(struct _hr_time));
gettimeofday(&now, NULL);
- delta = (now.tv_sec - t->start.tv_sec) * 1000ul
- + (now.tv_usec - t->start.tv_usec) / 1000;
+ delta = (now.tv_sec - t.start.tv_sec) * 1000ul
+ + (now.tv_usec - t.start.tv_usec) / 1000;
return delta;
}
}
diff --git a/thirdparty/mbedtls/library/x509.c b/thirdparty/mbedtls/library/x509.c
index 38eb2e6607..d61ef4a279 100644
--- a/thirdparty/mbedtls/library/x509.c
+++ b/thirdparty/mbedtls/library/x509.c
@@ -53,13 +53,17 @@
#include <time.h>
#endif
-#define CHECK(code) if ((ret = (code)) != 0) { return ret; }
+#define CHECK(code) \
+ do { \
+ if ((ret = (code)) != 0) { \
+ return ret; \
+ } \
+ } while (0)
+
#define CHECK_RANGE(min, max, val) \
- do \
- { \
- if ((val) < (min) || (val) > (max)) \
- { \
- return ret; \
+ do { \
+ if ((val) < (min) || (val) > (max)) { \
+ return ret; \
} \
} while (0)
diff --git a/thirdparty/mbedtls/library/x509_create.c b/thirdparty/mbedtls/library/x509_create.c
index 50db95688f..cdfc82aa5d 100644
--- a/thirdparty/mbedtls/library/x509_create.c
+++ b/thirdparty/mbedtls/library/x509_create.c
@@ -125,7 +125,7 @@ static const x509_attr_descriptor_t *x509_attr_descr_from_name(const char *name,
int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name)
{
- int ret = 0;
+ int ret = MBEDTLS_ERR_X509_INVALID_NAME;
const char *s = name, *c = s;
const char *end = s + strlen(s);
const char *oid = NULL;
@@ -177,6 +177,9 @@ int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *nam
s = c + 1;
in_tag = 1;
+
+ /* Successfully parsed one name, update ret to success */
+ ret = 0;
}
if (!in_tag && s != c + 1) {
diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
index e7fcaf4627..5b51694740 100644
--- a/thirdparty/mbedtls/library/x509_crt.c
+++ b/thirdparty/mbedtls/library/x509_crt.c
@@ -1944,16 +1944,19 @@ int mbedtls_x509_parse_subject_alt_name(const mbedtls_x509_buf *san_buf,
return 0;
}
-#define PRINT_ITEM(i) \
- { \
- ret = mbedtls_snprintf(p, n, "%s" i, sep); \
- MBEDTLS_X509_SAFE_SNPRINTF; \
- sep = ", "; \
- }
-
-#define CERT_TYPE(type, name) \
- if (ns_cert_type & (type)) \
- PRINT_ITEM(name);
+#define PRINT_ITEM(i) \
+ do { \
+ ret = mbedtls_snprintf(p, n, "%s" i, sep); \
+ MBEDTLS_X509_SAFE_SNPRINTF; \
+ sep = ", "; \
+ } while (0)
+
+#define CERT_TYPE(type, name) \
+ do { \
+ if (ns_cert_type & (type)) { \
+ PRINT_ITEM(name); \
+ } \
+ } while (0)
static int x509_info_cert_type(char **buf, size_t *size,
unsigned char ns_cert_type)
@@ -1978,9 +1981,12 @@ static int x509_info_cert_type(char **buf, size_t *size,
return 0;
}
-#define KEY_USAGE(code, name) \
- if (key_usage & (code)) \
- PRINT_ITEM(name);
+#define KEY_USAGE(code, name) \
+ do { \
+ if (key_usage & (code)) { \
+ PRINT_ITEM(name); \
+ } \
+ } while (0)
static int x509_info_key_usage(char **buf, size_t *size,
unsigned int key_usage)
diff --git a/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff
index 9a0c430216..721509453a 100644
--- a/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff
+++ b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff
@@ -1,8 +1,8 @@
diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c
-index 47e34f9227..eeb1d503a8 100644
+index 94b55b3715..58c2c2730d 100644
--- a/thirdparty/mbedtls/library/timing.c
+++ b/thirdparty/mbedtls/library/timing.c
-@@ -188,8 +188,10 @@ unsigned long mbedtls_timing_hardclock(void)
+@@ -190,8 +190,10 @@ unsigned long mbedtls_timing_hardclock(void)
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
__GNUC__ && __ia64__ */
diff --git a/thirdparty/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE
index 67ff3bb627..6eff8d2683 100644
--- a/thirdparty/miniupnpc/LICENSE
+++ b/thirdparty/miniupnpc/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2005-2022, Thomas BERNARD
+Copyright (c) 2005-2023, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/thirdparty/miniupnpc/include/miniupnpc.h b/thirdparty/miniupnpc/include/miniupnpc.h
index 75fb8b702d..721819583f 100644
--- a/thirdparty/miniupnpc/include/miniupnpc.h
+++ b/thirdparty/miniupnpc/include/miniupnpc.h
@@ -1,4 +1,4 @@
-/* $Id: miniupnpc.h,v 1.61 2022/10/21 21:15:02 nanard Exp $ */
+/* $Id: miniupnpc.h,v 1.62 2023/06/11 23:25:46 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
@@ -20,7 +20,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
-#define MINIUPNPC_VERSION "2.2.4"
+#define MINIUPNPC_VERSION "2.2.5"
#define MINIUPNPC_API_VERSION 17
/* Source port:
diff --git a/thirdparty/miniupnpc/src/miniupnpcstrings.h b/thirdparty/miniupnpc/src/miniupnpcstrings.h
index 25abc1016e..d89d9e443b 100644
--- a/thirdparty/miniupnpc/src/miniupnpcstrings.h
+++ b/thirdparty/miniupnpc/src/miniupnpcstrings.h
@@ -4,7 +4,7 @@
#include "core/version.h"
#define OS_STRING VERSION_NAME "/1.0"
-#define MINIUPNPC_VERSION_STRING "2.2.4"
+#define MINIUPNPC_VERSION_STRING "2.2.5"
#if 0
/* according to "UPnP Device Architecture 1.0" */
diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h
index d08e97f6cd..e5ac54a506 100644
--- a/thirdparty/tinyexr/tinyexr.h
+++ b/thirdparty/tinyexr/tinyexr.h
@@ -114,6 +114,11 @@ extern "C" {
#define TINYEXR_USE_STB_ZLIB (0)
#endif
+// Use nanozlib.
+#ifndef TINYEXR_USE_NANOZLIB
+#define TINYEXR_USE_NANOZLIB (0)
+#endif
+
// Disable PIZ compression when applying cpplint.
#ifndef TINYEXR_USE_PIZ
#define TINYEXR_USE_PIZ (1)
@@ -608,7 +613,10 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#define NOMINMAX
#endif
#include <windows.h> // for UTF-8 and memory-mapping
+
+#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
#define TINYEXR_USE_WIN32_MMAP (1)
+#endif
#elif defined(__linux__) || defined(__unix__)
#include <fcntl.h> // for open()
@@ -650,7 +658,7 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#include <omp.h>
#endif
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
#include <miniz.h>
#else
// Issue #46. Please include your own zlib-compatible API header before
@@ -658,6 +666,11 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
//#include "zlib.h"
#endif
+#if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+#define NANOZLIB_IMPLEMENTATION
+#include "nanozlib.h"
+#endif
+
#if TINYEXR_USE_STB_ZLIB
// Since we don't know where a project has stb_image.h and stb_image_write.h
// and whether they are in the include path, we don't include them here, and
@@ -668,6 +681,7 @@ extern "C" int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuf
extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality);
#endif
+
#if TINYEXR_USE_ZFP
#ifdef __clang__
@@ -1343,7 +1357,7 @@ static bool CompressZip(unsigned char *dst,
}
}
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
//
// Compress the data using miniz
//
@@ -1357,7 +1371,7 @@ static bool CompressZip(unsigned char *dst,
}
compressedSize = outSize;
-#elif TINYEXR_USE_STB_ZLIB
+#elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1)
int outSize;
unsigned char* ret = stbi_zlib_compress(const_cast<unsigned char*>(&tmpBuf.at(0)), src_size, &outSize, 8);
if (!ret) {
@@ -1367,6 +1381,18 @@ static bool CompressZip(unsigned char *dst,
free(ret);
compressedSize = outSize;
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+ uint64_t dstSize = nanoz_compressBound(static_cast<uint64_t>(src_size));
+ int outSize{0};
+ unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, /* quality */8);
+ if (!ret) {
+ return false;
+ }
+
+ memcpy(dst, ret, outSize);
+ free(ret);
+
+ compressedSize = outSize;
#else
uLong outSize = compressBound(static_cast<uLong>(src_size));
int ret = compress(dst, &outSize, static_cast<const Bytef *>(&tmpBuf.at(0)),
@@ -1398,7 +1424,7 @@ static bool DecompressZip(unsigned char *dst,
}
std::vector<unsigned char> tmpBuf(*uncompressed_size);
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
int ret =
mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
if (MZ_OK != ret) {
@@ -1410,6 +1436,17 @@ static bool DecompressZip(unsigned char *dst,
if (ret < 0) {
return false;
}
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+ uint64_t dest_size = (*uncompressed_size);
+ uint64_t uncomp_size{0};
+ nanoz_status_t ret =
+ nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size);
+ if (NANOZ_SUCCESS != ret) {
+ return false;
+ }
+ if ((*uncompressed_size) != uncomp_size) {
+ return false;
+ }
#else
int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
if (Z_OK != ret) {
@@ -5715,7 +5752,7 @@ static bool isValidTile(const EXRHeader* exr_header,
static bool ReconstructTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header,
- const unsigned char* head, const unsigned char* marker, const size_t /*size*/,
+ const unsigned char* head, const unsigned char* marker, const size_t size,
bool isMultiPartFile,
bool isDeep) {
int numXLevels = offset_data.num_x_levels;
@@ -5724,11 +5761,20 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
tinyexr::tinyexr_uint64 tileOffset = tinyexr::tinyexr_uint64(marker - head);
+
if (isMultiPartFile) {
+ if ((marker + sizeof(int)) >= (head + size)) {
+ return false;
+ }
+
//int partNumber;
marker += sizeof(int);
}
+ if ((marker + 4 * sizeof(int)) >= (head + size)) {
+ return false;
+ }
+
int tileX;
memcpy(&tileX, marker, sizeof(int));
tinyexr::swap4(&tileX);
@@ -5750,6 +5796,9 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
marker += sizeof(int);
if (isDeep) {
+ if ((marker + 2 * sizeof(tinyexr::tinyexr_int64)) >= (head + size)) {
+ return false;
+ }
tinyexr::tinyexr_int64 packed_offset_table_size;
memcpy(&packed_offset_table_size, marker, sizeof(tinyexr::tinyexr_int64));
tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64*>(&packed_offset_table_size));
@@ -5763,13 +5812,26 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
// next Int64 is unpacked sample size - skip that too
marker += packed_offset_table_size + packed_sample_size + 8;
+ if (marker >= (head + size)) {
+ return false;
+ }
+
} else {
- int dataSize;
- memcpy(&dataSize, marker, sizeof(int));
+ if ((marker + sizeof(uint32_t)) >= (head + size)) {
+ return false;
+ }
+
+ uint32_t dataSize;
+ memcpy(&dataSize, marker, sizeof(uint32_t));
tinyexr::swap4(&dataSize);
- marker += sizeof(int);
+ marker += sizeof(uint32_t);
+
marker += dataSize;
+
+ if (marker >= (head + size)) {
+ return false;
+ }
}
if (!isValidTile(exr_header, offset_data,
@@ -5781,6 +5843,19 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
if (level_idx < 0) {
return false;
}
+
+ if (size_t(level_idx) >= offset_data.offsets.size()) {
+ return false;
+ }
+
+ if (size_t(tileY) >= offset_data.offsets[size_t(level_idx)].size()) {
+ return false;
+ }
+
+ if (size_t(tileX) >= offset_data.offsets[size_t(level_idx)][size_t(tileY)].size()) {
+ return false;
+ }
+
offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
}
}
@@ -7043,13 +7118,16 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
} else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
(compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
std::vector<unsigned char> block(mz_compressBound(
static_cast<unsigned long>(buf.size())));
#elif TINYEXR_USE_STB_ZLIB
// there is no compressBound() function, so we use a value that
// is grossly overestimated, but should always work
std::vector<unsigned char> block(256 + 2 * buf.size());
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB == 1)
+ std::vector<unsigned char> block(nanoz_compressBound(
+ static_cast<unsigned long>(buf.size())));
#else
std::vector<unsigned char> block(
compressBound(static_cast<uLong>(buf.size())));