summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux_builds.yml13
-rw-r--r--SConstruct12
-rw-r--r--core/config/project_settings.cpp17
-rw-r--r--core/config/project_settings.h1
-rw-r--r--core/core_bind.cpp7
-rw-r--r--core/core_bind.h1
-rw-r--r--core/extension/gdextension_interface.cpp20
-rw-r--r--core/extension/gdextension_interface.h13
-rw-r--r--core/input/input.cpp9
-rw-r--r--core/input/input.h4
-rw-r--r--core/io/remote_filesystem_client.cpp2
-rw-r--r--core/math/a_star_grid_2d.cpp34
-rw-r--r--core/math/a_star_grid_2d.h3
-rw-r--r--core/math/delaunay_3d.h13
-rw-r--r--core/variant/variant_utility.cpp1571
-rw-r--r--core/variant/variant_utility.h153
-rw-r--r--doc/classes/@GlobalScope.xml5
-rw-r--r--doc/classes/AStarGrid2D.xml18
-rw-r--r--doc/classes/AudioServer.xml2
-rw-r--r--doc/classes/BaseButton.xml6
-rw-r--r--doc/classes/Bone2D.xml2
-rw-r--r--doc/classes/EditorExportPlugin.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/Geometry3D.xml11
-rw-r--r--doc/classes/Input.xml14
-rw-r--r--doc/classes/NavigationMeshGenerator.xml2
-rw-r--r--doc/classes/Node3D.xml2
-rw-r--r--doc/classes/Performance.xml2
-rw-r--r--doc/classes/PhysicsDirectSpaceState3D.xml2
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/RDPipelineMultisampleState.xml2
-rw-r--r--doc/classes/RDPipelineRasterizationState.xml2
-rw-r--r--doc/classes/RayCast3D.xml6
-rw-r--r--doc/classes/Rect2.xml2
-rw-r--r--doc/classes/Rect2i.xml2
-rw-r--r--doc/classes/RenderSceneBuffersRD.xml2
-rw-r--r--doc/classes/RenderingDevice.xml10
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--doc/classes/SceneTree.xml3
-rw-r--r--doc/classes/SoftBody3D.xml1
-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/Viewport.xml2
-rw-r--r--doc/classes/Window.xml7
-rw-r--r--doc/classes/XRInterfaceExtension.xml2
-rw-r--r--doc/classes/bool.xml36
-rw-r--r--doc/classes/float.xml2
-rw-r--r--drivers/png/SCsub40
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp25
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp62
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp2
-rw-r--r--editor/debugger/script_editor_debugger.cpp12
-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_node.cpp7
-rw-r--r--editor/editor_properties.cpp4
-rw-r--r--editor/editor_property_name_processor.cpp4
-rw-r--r--editor/editor_resource_preview.cpp2
-rw-r--r--editor/editor_themes.cpp40
-rw-r--r--editor/export/editor_export_preset.cpp33
-rw-r--r--editor/export/editor_export_preset.h7
-rw-r--r--editor/filesystem_dock.cpp39
-rw-r--r--editor/gui/editor_run_bar.cpp6
-rw-r--r--editor/gui/editor_run_bar.h4
-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.cpp125
-rw-r--r--editor/gui/scene_tree_editor.h8
-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/GizmoCamera3D.svg1
-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/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/Texture3D.svg1
-rw-r--r--editor/icons/UndoRedo.svg1
-rw-r--r--editor/icons/XRCamera3D.svg2
-rw-r--r--editor/import_dock.cpp15
-rw-r--r--editor/import_dock.h1
-rw-r--r--editor/plugins/bit_map_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp12
-rw-r--r--editor/plugins/curve_editor_plugin.cpp5
-rw-r--r--editor/plugins/curve_editor_plugin.h2
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp3
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp46
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h4
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp7
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp7
-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_gizmos.cpp13
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h4
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp9
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp14
-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/voxel_gi_editor_plugin.cpp4
-rw-r--r--editor/scene_create_dialog.cpp90
-rw-r--r--editor/scene_create_dialog.h14
-rw-r--r--editor/scene_tree_dock.cpp129
-rw-r--r--editor/scene_tree_dock.h8
-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.cpp18
-rw-r--r--misc/error_suppressions/tsan.txt8
-rw-r--r--misc/extension_api_validation/4.0-stable.expected80
-rwxr-xr-xmisc/scripts/clang_format.sh2
-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.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp66
-rw-r--r--modules/gdscript/gdscript_editor.cpp4
-rw-r--r--modules/gdscript/language_server/godot_lsp.h2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd14
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out5
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml7
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml18
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml4
-rw-r--r--modules/gltf/extensions/gltf_document_extension.cpp15
-rw-r--r--modules/gltf/extensions/gltf_document_extension.h4
-rw-r--r--modules/gltf/extensions/gltf_document_extension_texture_webp.cpp6
-rw-r--r--modules/gltf/extensions/gltf_document_extension_texture_webp.h1
-rw-r--r--modules/gltf/gltf_document.cpp182
-rw-r--r--modules/gltf/gltf_document.h14
-rw-r--r--modules/gltf/gltf_state.cpp15
-rw-r--r--modules/gltf/gltf_state.h9
-rw-r--r--modules/mono/csharp_script.cpp4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs24
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs54
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs21
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs13
-rw-r--r--modules/mono/editor/bindings_generator.cpp137
-rw-r--r--modules/mono/editor/bindings_generator.h12
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs71
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs6
-rw-r--r--modules/mono/glue/runtime_interop.cpp21
-rw-r--r--modules/mono/godotsharp_dirs.cpp45
-rw-r--r--modules/mono/icons/BuildCSharp.svg1
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp6
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp20
-rw-r--r--modules/navigation/godot_navigation_server.cpp55
-rw-r--r--modules/navigation/godot_navigation_server.h5
-rw-r--r--modules/navigation/nav_map.cpp3
-rw-r--r--modules/navigation/nav_mesh_generator_3d.cpp728
-rw-r--r--modules/navigation/nav_mesh_generator_3d.h81
-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/icons/NoiseTexture3D.svg1
-rw-r--r--modules/openxr/openxr_api.cpp4
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct1
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml6
-rw-r--r--platform/android/export/export_plugin.cpp10
-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.py5
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml4
-rw-r--r--platform/ios/export/export_plugin.cpp4
-rw-r--r--platform/linuxbsd/joypad_linux.cpp25
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp45
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h4
-rw-r--r--platform/macos/display_server_macos.mm2
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml8
-rw-r--r--platform/macos/export/export_plugin.cpp4
-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/display_server_windows.cpp9
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml6
-rw-r--r--platform/windows/export/export_plugin.cpp8
-rw-r--r--platform/windows/joypad_windows.cpp4
-rw-r--r--platform/windows/os_windows.cpp2
-rw-r--r--scene/2d/tile_map.compat.inc6
-rw-r--r--scene/3d/lightmap_gi.cpp2
-rw-r--r--scene/3d/navigation_region_3d.cpp6
-rw-r--r--scene/3d/ray_cast_3d.cpp6
-rw-r--r--scene/3d/ray_cast_3d.h2
-rw-r--r--scene/gui/base_button.cpp4
-rw-r--r--scene/gui/code_edit.compat.inc41
-rw-r--r--scene/gui/code_edit.cpp1
-rw-r--r--scene/gui/code_edit.h5
-rw-r--r--scene/gui/color_picker.cpp48
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/tree.cpp92
-rw-r--r--scene/gui/tree.h6
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/main/window.cpp22
-rw-r--r--scene/main/window.h5
-rw-r--r--scene/resources/font.cpp11
-rw-r--r--scene/resources/font.h1
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
-rw-r--r--scu_builders.py23
-rw-r--r--servers/navigation_server_3d.h4
-rw-r--r--servers/navigation_server_3d_dummy.h2
-rw-r--r--servers/physics_3d/godot_body_pair_3d.cpp4
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.cpp3
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp29
-rw-r--r--servers/physics_3d/godot_shape_3d.h25
-rw-r--r--servers/physics_3d/godot_soft_body_3d.cpp2
-rw-r--r--servers/physics_3d/godot_soft_body_3d.h2
-rw-r--r--servers/physics_3d/godot_space_3d.cpp6
-rw-r--r--servers/physics_server_3d.cpp1
-rw-r--r--servers/physics_server_3d.h1
-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.cpp6
-rw-r--r--servers/rendering/renderer_rd/effects/vrs.cpp3
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp6
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp23
-rw-r--r--servers/rendering/renderer_rd/environment/sky.h3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp6
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp2
-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/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.glsl6
-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/scene_forward_lights_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp6
-rw-r--r--servers/rendering/renderer_scene_cull.cpp1
-rw-r--r--servers/rendering/rendering_device.compat.inc41
-rw-r--r--servers/rendering/rendering_device.cpp7
-rw-r--r--servers/rendering/rendering_device.h5
-rw-r--r--servers/rendering/shader_preprocessor.cpp65
-rw-r--r--servers/rendering/shader_preprocessor.h1
-rw-r--r--servers/xr/xr_positional_tracker.cpp9
-rw-r--r--tests/core/config/test_project_settings.h60
-rw-r--r--tests/core/string/test_string.h6
-rw-r--r--tests/core/string/test_translation_server.h136
-rw-r--r--tests/scene/test_packed_scene.h114
-rw-r--r--tests/scene/test_text_edit.h4
-rw-r--r--tests/scene/test_viewport.h407
-rw-r--r--tests/servers/rendering/test_shader_preprocessor.h334
-rw-r--r--tests/test_macros.h14
-rw-r--r--tests/test_main.cpp3
-rw-r--r--thirdparty/README.md6
-rw-r--r--thirdparty/libpng/LICENSE4
-rw-r--r--thirdparty/libpng/intel/filter_sse2_intrinsics.c391
-rw-r--r--thirdparty/libpng/intel/intel_init.c52
-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/libpng/powerpc/filter_vsx_intrinsics.c768
-rw-r--r--thirdparty/libpng/powerpc/powerpc_init.c126
-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
338 files changed, 7257 insertions, 3287 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index e5ab2fde43..c812996fd4 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -9,6 +9,7 @@ env:
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
+ TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux
@@ -58,6 +59,16 @@ jobs:
# Skip 2GiB artifact speeding up action.
artifact: false
+ - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)
+ cache-name: linux-editor-thread-sanitizer
+ target: editor
+ tests: true
+ sconsflags: dev_build=yes use_tsan=yes use_llvm=yes linker=lld
+ bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san"
+ build-mono: false
+ # Skip 2GiB artifact speeding up action.
+ artifact: false
+
- name: Template w/ Mono (target=template_release)
cache-name: linux-template-mono
target: template_release
@@ -165,7 +176,7 @@ jobs:
- name: Check for GDExtension compatibility
if: ${{ matrix.api-compat }}
run: |
- ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" || true # don't fail the CI for now
+ ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}"
# Download and run the test project
- name: Test Godot project
diff --git a/SConstruct b/SConstruct
index f82c9c656e..6968967380 100644
--- a/SConstruct
+++ b/SConstruct
@@ -220,6 +220,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 +552,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.
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 715ed61770..e8c885162b 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://");
@@ -1260,6 +1260,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC("application/config/name", "");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary());
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), "");
+ GLOBAL_DEF_BASIC("application/config/version", "");
GLOBAL_DEF_INTERNAL(PropertyInfo(Variant::STRING, "application/config/tags"), PackedStringArray());
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), "");
GLOBAL_DEF("application/run/disable_stdout", false);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index b89e6694b0..a1f52fa1e0 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -43,6 +43,7 @@ class TypedArray;
class ProjectSettings : public Object {
GDCLASS(ProjectSettings, Object);
_THREAD_SAFE_CLASS_
+ friend class TestProjectSettingsInternalsAccessor;
public:
typedef HashMap<String, Variant> CustomMap;
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a73b198be2..4e220d0839 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -961,6 +961,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point
return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s);
}
+Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
+ Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point);
+ return res;
+}
+
Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
Vector3 res;
if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) {
@@ -1034,6 +1039,8 @@ void Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped);
+ ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords);
+
ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere);
diff --git a/core/core_bind.h b/core/core_bind.h
index dc0b2a1cf5..1cbbcdd251 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -326,6 +326,7 @@ public:
Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2);
Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
+ Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
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 d481acf005..4a32abfafa 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -113,6 +113,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis);
ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name);
ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid);
+ ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info);
ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device);
ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads);
ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
@@ -437,11 +438,12 @@ static String _hex_str(uint8_t p_byte) {
return ret;
}
-void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) {
+void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) {
_THREAD_SAFE_METHOD_
Joypad js;
js.name = p_connected ? p_name : "";
js.uid = p_connected ? p_guid : "";
+ js.info = p_connected ? p_joypad_info : Dictionary();
if (p_connected) {
String uidname = p_guid;
@@ -1499,6 +1501,11 @@ String Input::get_joy_guid(int p_device) const {
return joy_names[p_device].uid;
}
+Dictionary Input::get_joy_info(int p_device) const {
+ ERR_FAIL_COND_V(!joy_names.has(p_device), Dictionary());
+ return joy_names[p_device].info;
+}
+
bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const {
uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id);
return ignored_device_ids.has(full_id);
diff --git a/core/input/input.h b/core/input/input.h
index ec16871b72..c63a4e52e3 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -149,6 +149,7 @@ private:
HatMask last_hat = HatMask::CENTER;
int mapping = -1;
int hat_current = 0;
+ Dictionary info;
};
VelocityTrack mouse_velocity_track;
@@ -276,7 +277,7 @@ public:
Vector2 get_joy_vibration_strength(int p_device);
float get_joy_vibration_duration(int p_device);
uint64_t get_joy_vibration_timestamp(int p_device);
- void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "");
+ void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary());
Vector3 get_gravity() const;
Vector3 get_accelerometer() const;
@@ -332,6 +333,7 @@ public:
bool is_joy_known(int p_device);
String get_joy_guid(int p_device) const;
bool should_ignore_device(int p_vendor_id, int p_product_id) const;
+ Dictionary get_joy_info(int p_device) const;
void set_fallback_mapping(String p_guid);
void flush_buffered_events();
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/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index 63f7c80bdd..9ba4c2ff9a 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -194,6 +194,38 @@ real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const {
return GET_POINT_UNCHECKED(p_id).weight_scale;
}
+void AStarGrid2D::fill_solid_region(const Rect2i &p_region, bool p_solid) {
+ ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
+
+ Rect2i safe_region = p_region.intersection(region);
+ int from_x = safe_region.get_position().x;
+ int from_y = safe_region.get_position().y;
+ int end_x = safe_region.get_end().x;
+ int end_y = safe_region.get_end().y;
+
+ for (int x = from_x; x < end_x; x++) {
+ for (int y = from_y; y < end_y; y++) {
+ GET_POINT_UNCHECKED(Vector2i(x, y)).solid = p_solid;
+ }
+ }
+}
+
+void AStarGrid2D::fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale) {
+ ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
+ ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
+
+ Rect2i safe_region = p_region.intersection(region);
+ int from_x = safe_region.get_position().x;
+ int from_y = safe_region.get_position().y;
+ int end_x = safe_region.get_end().x;
+ int end_y = safe_region.get_end().y;
+ for (int x = from_x; x < end_x; x++) {
+ for (int y = from_y; y < end_y; y++) {
+ GET_POINT_UNCHECKED(Vector2i(x, y)).weight_scale = p_weight_scale;
+ }
+ }
+}
+
AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) {
if (!p_to || p_to->solid) {
return nullptr;
@@ -606,6 +638,8 @@ void AStarGrid2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid);
ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStarGrid2D::set_point_weight_scale);
ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStarGrid2D::get_point_weight_scale);
+ ClassDB::bind_method(D_METHOD("fill_solid_region", "region", "solid"), &AStarGrid2D::fill_solid_region, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("fill_weight_scale_region", "region", "weight_scale"), &AStarGrid2D::fill_weight_scale_region);
ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear);
ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position);
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index 50df58e0e9..dd5f9d0575 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -177,6 +177,9 @@ public:
void set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale);
real_t get_point_weight_scale(const Vector2i &p_id) const;
+ void fill_solid_region(const Rect2i &p_region, bool p_solid = true);
+ void fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale);
+
void clear();
Vector2 get_point_position(const Vector2i &p_id) const;
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/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 545825011a..4f6bcb58b3 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "variant.h"
+#include "variant_utility.h"
#include "core/core_string_names.h"
#include "core/io/marshalls.h"
@@ -40,755 +40,772 @@
#include "core/variant/binder_common.h"
#include "core/variant/variant_parser.h"
-struct VariantUtilityFunctions {
- // Math
-
- static inline double sin(double arg) {
- return Math::sin(arg);
- }
+// Math
+double VariantUtilityFunctions::sin(double arg) {
+ return Math::sin(arg);
+}
- static inline double cos(double arg) {
- return Math::cos(arg);
- }
+double VariantUtilityFunctions::cos(double arg) {
+ return Math::cos(arg);
+}
- static inline double tan(double arg) {
- return Math::tan(arg);
- }
+double VariantUtilityFunctions::tan(double arg) {
+ return Math::tan(arg);
+}
- static inline double sinh(double arg) {
- return Math::sinh(arg);
- }
+double VariantUtilityFunctions::sinh(double arg) {
+ return Math::sinh(arg);
+}
- static inline double cosh(double arg) {
- return Math::cosh(arg);
- }
+double VariantUtilityFunctions::cosh(double arg) {
+ return Math::cosh(arg);
+}
- static inline double tanh(double arg) {
- return Math::tanh(arg);
- }
+double VariantUtilityFunctions::tanh(double arg) {
+ return Math::tanh(arg);
+}
- static inline double asin(double arg) {
- return Math::asin(arg);
- }
+double VariantUtilityFunctions::asin(double arg) {
+ return Math::asin(arg);
+}
- static inline double acos(double arg) {
- return Math::acos(arg);
- }
+double VariantUtilityFunctions::acos(double arg) {
+ return Math::acos(arg);
+}
- static inline double atan(double arg) {
- return Math::atan(arg);
- }
+double VariantUtilityFunctions::atan(double arg) {
+ return Math::atan(arg);
+}
- static inline double atan2(double y, double x) {
- return Math::atan2(y, x);
- }
+double VariantUtilityFunctions::atan2(double y, double x) {
+ return Math::atan2(y, x);
+}
- static inline double sqrt(double x) {
- return Math::sqrt(x);
- }
+double VariantUtilityFunctions::sqrt(double x) {
+ return Math::sqrt(x);
+}
- static inline double fmod(double b, double r) {
- return Math::fmod(b, r);
- }
+double VariantUtilityFunctions::fmod(double b, double r) {
+ return Math::fmod(b, r);
+}
- static inline double fposmod(double b, double r) {
- return Math::fposmod(b, r);
- }
+double VariantUtilityFunctions::fposmod(double b, double r) {
+ return Math::fposmod(b, r);
+}
- static inline int64_t posmod(int64_t b, int64_t r) {
- return Math::posmod(b, r);
- }
+int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) {
+ return Math::posmod(b, r);
+}
- static inline Variant floor(Variant x, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- switch (x.get_type()) {
- case Variant::INT: {
- return VariantInternalAccessor<int64_t>::get(&x);
- } break;
- case Variant::FLOAT: {
- return Math::floor(VariantInternalAccessor<double>::get(&x));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).floor();
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).floor();
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).floor();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::floor(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).floor();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).floor();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).floor();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
}
}
+}
- static inline double floorf(double x) {
- return Math::floor(x);
- }
+double VariantUtilityFunctions::floorf(double x) {
+ return Math::floor(x);
+}
- static inline int64_t floori(double x) {
- return int64_t(Math::floor(x));
- }
+int64_t VariantUtilityFunctions::floori(double x) {
+ return int64_t(Math::floor(x));
+}
- static inline Variant ceil(Variant x, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- switch (x.get_type()) {
- case Variant::INT: {
- return VariantInternalAccessor<int64_t>::get(&x);
- } break;
- case Variant::FLOAT: {
- return Math::ceil(VariantInternalAccessor<double>::get(&x));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).ceil();
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).ceil();
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).ceil();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::ceil(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).ceil();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).ceil();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).ceil();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
}
}
+}
- static inline double ceilf(double x) {
- return Math::ceil(x);
- }
+double VariantUtilityFunctions::ceilf(double x) {
+ return Math::ceil(x);
+}
- static inline int64_t ceili(double x) {
- return int64_t(Math::ceil(x));
- }
+int64_t VariantUtilityFunctions::ceili(double x) {
+ return int64_t(Math::ceil(x));
+}
- static inline Variant round(Variant x, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- switch (x.get_type()) {
- case Variant::INT: {
- return VariantInternalAccessor<int64_t>::get(&x);
- } break;
- case Variant::FLOAT: {
- return Math::round(VariantInternalAccessor<double>::get(&x));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).round();
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).round();
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).round();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::round(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).round();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).round();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).round();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
}
}
+}
- static inline double roundf(double x) {
- return Math::round(x);
- }
+double VariantUtilityFunctions::roundf(double x) {
+ return Math::round(x);
+}
- static inline int64_t roundi(double x) {
- return int64_t(Math::round(x));
- }
+int64_t VariantUtilityFunctions::roundi(double x) {
+ return int64_t(Math::round(x));
+}
- static inline Variant abs(const Variant &x, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- switch (x.get_type()) {
- case Variant::INT: {
- return ABS(VariantInternalAccessor<int64_t>::get(&x));
- } break;
- case Variant::FLOAT: {
- return Math::absd(VariantInternalAccessor<double>::get(&x));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).abs();
- } break;
- case Variant::VECTOR2I: {
- return VariantInternalAccessor<Vector2i>::get(&x).abs();
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).abs();
- } break;
- case Variant::VECTOR3I: {
- return VariantInternalAccessor<Vector3i>::get(&x).abs();
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).abs();
- } break;
- case Variant::VECTOR4I: {
- return VariantInternalAccessor<Vector4i>::get(&x).abs();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return ABS(VariantInternalAccessor<int64_t>::get(&x));
+ } break;
+ case Variant::FLOAT: {
+ return Math::absd(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).abs();
+ } break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x).abs();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).abs();
+ } break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x).abs();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).abs();
+ } break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x).abs();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
}
}
+}
- static inline double absf(double x) {
- return Math::absd(x);
- }
+double VariantUtilityFunctions::absf(double x) {
+ return Math::absd(x);
+}
- static inline int64_t absi(int64_t x) {
- return ABS(x);
- }
+int64_t VariantUtilityFunctions::absi(int64_t x) {
+ return ABS(x);
+}
- static inline Variant sign(const Variant &x, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- switch (x.get_type()) {
- case Variant::INT: {
- return SIGN(VariantInternalAccessor<int64_t>::get(&x));
- } break;
- case Variant::FLOAT: {
- return SIGN(VariantInternalAccessor<double>::get(&x));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).sign();
- } break;
- case Variant::VECTOR2I: {
- return VariantInternalAccessor<Vector2i>::get(&x).sign();
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).sign();
- } break;
- case Variant::VECTOR3I: {
- return VariantInternalAccessor<Vector3i>::get(&x).sign();
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).sign();
- } break;
- case Variant::VECTOR4I: {
- return VariantInternalAccessor<Vector4i>::get(&x).sign();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return SIGN(VariantInternalAccessor<int64_t>::get(&x));
+ } break;
+ case Variant::FLOAT: {
+ return SIGN(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).sign();
+ } break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x).sign();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).sign();
+ } break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x).sign();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).sign();
+ } break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x).sign();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
}
}
+}
- static inline double signf(double x) {
- return SIGN(x);
- }
+double VariantUtilityFunctions::signf(double x) {
+ return SIGN(x);
+}
- static inline int64_t signi(int64_t x) {
- return SIGN(x);
- }
+int64_t VariantUtilityFunctions::signi(int64_t x) {
+ return SIGN(x);
+}
- static inline double pow(double x, double y) {
- return Math::pow(x, y);
- }
+double VariantUtilityFunctions::pow(double x, double y) {
+ return Math::pow(x, y);
+}
- static inline double log(double x) {
- return Math::log(x);
- }
+double VariantUtilityFunctions::log(double x) {
+ return Math::log(x);
+}
- static inline double exp(double x) {
- return Math::exp(x);
- }
+double VariantUtilityFunctions::exp(double x) {
+ return Math::exp(x);
+}
- static inline bool is_nan(double x) {
- return Math::is_nan(x);
- }
+bool VariantUtilityFunctions::is_nan(double x) {
+ return Math::is_nan(x);
+}
- static inline bool is_inf(double x) {
- return Math::is_inf(x);
- }
+bool VariantUtilityFunctions::is_inf(double x) {
+ return Math::is_inf(x);
+}
- static inline bool is_equal_approx(double x, double y) {
- return Math::is_equal_approx(x, y);
- }
+bool VariantUtilityFunctions::is_equal_approx(double x, double y) {
+ return Math::is_equal_approx(x, y);
+}
- static inline bool is_zero_approx(double x) {
- return Math::is_zero_approx(x);
- }
+bool VariantUtilityFunctions::is_zero_approx(double x) {
+ return Math::is_zero_approx(x);
+}
- static inline bool is_finite(double x) {
- return Math::is_finite(x);
- }
+bool VariantUtilityFunctions::is_finite(double x) {
+ return Math::is_finite(x);
+}
- static inline double ease(float x, float curve) {
- return Math::ease(x, curve);
- }
+double VariantUtilityFunctions::ease(float x, float curve) {
+ return Math::ease(x, curve);
+}
- static inline int step_decimals(float step) {
- return Math::step_decimals(step);
- }
+int VariantUtilityFunctions::step_decimals(float step) {
+ return Math::step_decimals(step);
+}
- static inline Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
+Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ return Variant();
+ }
+
+ switch (step.get_type()) {
+ case Variant::INT: {
+ return snappedi(x, VariantInternalAccessor<int64_t>::get(&step));
+ } break;
+ case Variant::FLOAT: {
+ return snappedf(x, VariantInternalAccessor<double>::get(&step));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step));
+ } break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step));
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step));
+ } break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step));
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step));
+ } break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step));
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
-
- switch (step.get_type()) {
- case Variant::INT: {
- return snappedi(x, VariantInternalAccessor<int64_t>::get(&step));
- } break;
- case Variant::FLOAT: {
- return snappedf(x, VariantInternalAccessor<double>::get(&step));
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step));
- } break;
- case Variant::VECTOR2I: {
- return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step));
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step));
- } break;
- case Variant::VECTOR3I: {
- return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step));
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step));
- } break;
- case Variant::VECTOR4I: {
- return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step));
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
- }
}
+}
- static inline double snappedf(double x, double step) {
- return Math::snapped(x, step);
- }
+double VariantUtilityFunctions::snappedf(double x, double step) {
+ return Math::snapped(x, step);
+}
- static inline int64_t snappedi(double x, int64_t step) {
- return Math::snapped(x, step);
- }
+int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) {
+ return Math::snapped(x, step);
+}
- static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- if (from.get_type() != to.get_type()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = from.get_type();
- r_error.argument = 1;
+Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ if (from.get_type() != to.get_type()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected = from.get_type();
+ r_error.argument = 1;
+ return Variant();
+ }
+
+ switch (from.get_type()) {
+ case Variant::INT: {
+ return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight);
+ } break;
+ case Variant::FLOAT: {
+ return lerpf(VariantInternalAccessor<double>::get(&from), to, weight);
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight);
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight);
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight);
+ } break;
+ case Variant::QUATERNION: {
+ return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight);
+ } break;
+ case Variant::BASIS: {
+ return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight);
+ } break;
+ case Variant::COLOR: {
+ return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
-
- switch (from.get_type()) {
- case Variant::INT: {
- return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight);
- } break;
- case Variant::FLOAT: {
- return lerpf(VariantInternalAccessor<double>::get(&from), to, weight);
- } break;
- case Variant::VECTOR2: {
- return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight);
- } break;
- case Variant::VECTOR3: {
- return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight);
- } break;
- case Variant::VECTOR4: {
- return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight);
- } break;
- case Variant::QUATERNION: {
- return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight);
- } break;
- case Variant::BASIS: {
- return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight);
- } break;
- case Variant::COLOR: {
- return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
- }
}
+}
- static inline double lerpf(double from, double to, double weight) {
- return Math::lerp(from, to, weight);
- }
+double VariantUtilityFunctions::lerpf(double from, double to, double weight) {
+ return Math::lerp(from, to, weight);
+}
- static inline double cubic_interpolate(double from, double to, double pre, double post, double weight) {
- return Math::cubic_interpolate(from, to, pre, post, weight);
- }
+double VariantUtilityFunctions::cubic_interpolate(double from, double to, double pre, double post, double weight) {
+ return Math::cubic_interpolate(from, to, pre, post, weight);
+}
- static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) {
- return Math::cubic_interpolate_angle(from, to, pre, post, weight);
- }
+double VariantUtilityFunctions::cubic_interpolate_angle(double from, double to, double pre, double post, double weight) {
+ return Math::cubic_interpolate_angle(from, to, pre, post, weight);
+}
- static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight,
- double to_t, double pre_t, double post_t) {
- return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
- }
+double VariantUtilityFunctions::cubic_interpolate_in_time(double from, double to, double pre, double post, double weight,
+ double to_t, double pre_t, double post_t) {
+ return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
+}
- static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight,
- double to_t, double pre_t, double post_t) {
- return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
- }
+double VariantUtilityFunctions::cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight,
+ double to_t, double pre_t, double post_t) {
+ return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
+}
- static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
- return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t);
- }
+double VariantUtilityFunctions::bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
+ return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t);
+}
- static inline double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
- return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t);
- }
+double VariantUtilityFunctions::bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
+ return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t);
+}
- static inline double lerp_angle(double from, double to, double weight) {
- return Math::lerp_angle(from, to, weight);
- }
+double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) {
+ return Math::lerp_angle(from, to, weight);
+}
- static inline double inverse_lerp(double from, double to, double weight) {
- return Math::inverse_lerp(from, to, weight);
- }
+double VariantUtilityFunctions::inverse_lerp(double from, double to, double weight) {
+ return Math::inverse_lerp(from, to, weight);
+}
- static inline double remap(double value, double istart, double istop, double ostart, double ostop) {
- return Math::remap(value, istart, istop, ostart, ostop);
- }
+double VariantUtilityFunctions::remap(double value, double istart, double istop, double ostart, double ostop) {
+ return Math::remap(value, istart, istop, ostart, ostop);
+}
- static inline double smoothstep(double from, double to, double val) {
- return Math::smoothstep(from, to, val);
- }
+double VariantUtilityFunctions::smoothstep(double from, double to, double val) {
+ return Math::smoothstep(from, to, val);
+}
- static inline double move_toward(double from, double to, double delta) {
- return Math::move_toward(from, to, delta);
- }
+double VariantUtilityFunctions::move_toward(double from, double to, double delta) {
+ return Math::move_toward(from, to, delta);
+}
- static inline double deg_to_rad(double angle_deg) {
- return Math::deg_to_rad(angle_deg);
- }
+double VariantUtilityFunctions::deg_to_rad(double angle_deg) {
+ return Math::deg_to_rad(angle_deg);
+}
- static inline double rad_to_deg(double angle_rad) {
- return Math::rad_to_deg(angle_rad);
- }
+double VariantUtilityFunctions::rad_to_deg(double angle_rad) {
+ return Math::rad_to_deg(angle_rad);
+}
- static inline double linear_to_db(double linear) {
- return Math::linear_to_db(linear);
- }
+double VariantUtilityFunctions::linear_to_db(double linear) {
+ return Math::linear_to_db(linear);
+}
+
+double VariantUtilityFunctions::db_to_linear(double db) {
+ return Math::db_to_linear(db);
+}
- static inline double db_to_linear(double db) {
- return Math::db_to_linear(db);
+Variant VariantUtilityFunctions::wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) {
+ Variant::Type x_type = p_x.get_type();
+ if (x_type != Variant::INT && x_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = x_type;
+ return Variant();
}
- static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) {
- Variant::Type x_type = p_x.get_type();
- if (x_type != Variant::INT && x_type != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = x_type;
- return Variant();
- }
+ Variant::Type min_type = p_min.get_type();
+ if (min_type != Variant::INT && min_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = x_type;
+ return Variant();
+ }
- Variant::Type min_type = p_min.get_type();
- if (min_type != Variant::INT && min_type != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = x_type;
- return Variant();
- }
+ Variant::Type max_type = p_max.get_type();
+ if (max_type != Variant::INT && max_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 2;
+ r_error.expected = x_type;
+ return Variant();
+ }
- Variant::Type max_type = p_max.get_type();
- if (max_type != Variant::INT && max_type != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 2;
- r_error.expected = x_type;
- return Variant();
- }
+ Variant value;
- Variant value;
-
- switch (x_type) {
- case Variant::INT: {
- if (x_type != min_type || x_type != max_type) {
- value = wrapf((double)p_x, (double)p_min, (double)p_max);
- } else {
- value = wrapi((int)p_x, (int)p_min, (int)p_max);
- }
- } break;
- case Variant::FLOAT: {
+ switch (x_type) {
+ case Variant::INT: {
+ if (x_type != min_type || x_type != max_type) {
value = wrapf((double)p_x, (double)p_min, (double)p_max);
- } break;
- default:
- break;
- }
-
- r_error.error = Callable::CallError::CALL_OK;
- return value;
+ } else {
+ value = wrapi((int)p_x, (int)p_min, (int)p_max);
+ }
+ } break;
+ case Variant::FLOAT: {
+ value = wrapf((double)p_x, (double)p_min, (double)p_max);
+ } break;
+ default:
+ break;
}
- static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) {
- return Math::wrapi(value, min, max);
- }
+ r_error.error = Callable::CallError::CALL_OK;
+ return value;
+}
- static inline double wrapf(double value, double min, double max) {
- return Math::wrapf(value, min, max);
- }
+int64_t VariantUtilityFunctions::wrapi(int64_t value, int64_t min, int64_t max) {
+ return Math::wrapi(value, min, max);
+}
- static inline double pingpong(double value, double length) {
- return Math::pingpong(value, length);
+double VariantUtilityFunctions::wrapf(double value, double min, double max) {
+ return Math::wrapf(value, min, max);
+}
+
+double VariantUtilityFunctions::pingpong(double value, double length) {
+ return Math::pingpong(value, length);
+}
+
+Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 2) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.expected = 2;
+ return Variant();
}
+ Variant base = *p_args[0];
+ Variant ret;
- static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (p_argcount < 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.expected = 2;
+ for (int i = 0; i < p_argcount; i++) {
+ Variant::Type arg_type = p_args[i]->get_type();
+ if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected = Variant::FLOAT;
+ r_error.argument = i;
return Variant();
}
- Variant base = *p_args[0];
- Variant ret;
-
- for (int i = 0; i < p_argcount; i++) {
- Variant::Type arg_type = p_args[i]->get_type();
- if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = Variant::FLOAT;
- r_error.argument = i;
- return Variant();
- }
- if (i == 0) {
- continue;
- }
- bool valid;
- Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid);
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = base.get_type();
- r_error.argument = i;
- return Variant();
- }
- if (ret.booleanize()) {
- base = *p_args[i];
- }
+ if (i == 0) {
+ continue;
}
- r_error.error = Callable::CallError::CALL_OK;
- return base;
- }
-
- static inline double maxf(double x, double y) {
- return MAX(x, y);
- }
-
- static inline int64_t maxi(int64_t x, int64_t y) {
- return MAX(x, y);
- }
-
- static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (p_argcount < 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.expected = 2;
+ bool valid;
+ Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid);
+ if (!valid) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected = base.get_type();
+ r_error.argument = i;
return Variant();
}
- Variant base = *p_args[0];
- Variant ret;
-
- for (int i = 0; i < p_argcount; i++) {
- Variant::Type arg_type = p_args[i]->get_type();
- if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = Variant::FLOAT;
- r_error.argument = i;
- return Variant();
- }
- if (i == 0) {
- continue;
- }
- bool valid;
- Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid);
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = base.get_type();
- r_error.argument = i;
- return Variant();
- }
- if (ret.booleanize()) {
- base = *p_args[i];
- }
+ if (ret.booleanize()) {
+ base = *p_args[i];
}
- r_error.error = Callable::CallError::CALL_OK;
- return base;
- }
-
- static inline double minf(double x, double y) {
- return MIN(x, y);
}
+ r_error.error = Callable::CallError::CALL_OK;
+ return base;
+}
- static inline int64_t mini(int64_t x, int64_t y) {
- return MIN(x, y);
- }
+double VariantUtilityFunctions::maxf(double x, double y) {
+ return MAX(x, y);
+}
- static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) {
- Variant value = x;
+int64_t VariantUtilityFunctions::maxi(int64_t x, int64_t y) {
+ return MAX(x, y);
+}
- Variant ret;
+Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 2) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.expected = 2;
+ return Variant();
+ }
+ Variant base = *p_args[0];
+ Variant ret;
- bool valid;
- Variant::evaluate(Variant::OP_LESS, value, min, ret, valid);
- if (!valid) {
+ for (int i = 0; i < p_argcount; i++) {
+ Variant::Type arg_type = p_args[i]->get_type();
+ if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = value.get_type();
- r_error.argument = 1;
+ r_error.expected = Variant::FLOAT;
+ r_error.argument = i;
return Variant();
}
- if (ret.booleanize()) {
- value = min;
+ if (i == 0) {
+ continue;
}
- Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid);
+ bool valid;
+ Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = value.get_type();
- r_error.argument = 2;
+ r_error.expected = base.get_type();
+ r_error.argument = i;
return Variant();
}
if (ret.booleanize()) {
- value = max;
+ base = *p_args[i];
}
+ }
+ r_error.error = Callable::CallError::CALL_OK;
+ return base;
+}
- r_error.error = Callable::CallError::CALL_OK;
+double VariantUtilityFunctions::minf(double x, double y) {
+ return MIN(x, y);
+}
- return value;
- }
+int64_t VariantUtilityFunctions::mini(int64_t x, int64_t y) {
+ return MIN(x, y);
+}
- static inline double clampf(double x, double min, double max) {
- return CLAMP(x, min, max);
- }
+Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) {
+ Variant value = x;
- static inline int64_t clampi(int64_t x, int64_t min, int64_t max) {
- return CLAMP(x, min, max);
- }
+ Variant ret;
- static inline int64_t nearest_po2(int64_t x) {
- return nearest_power_of_2_templated(uint64_t(x));
+ bool valid;
+ Variant::evaluate(Variant::OP_LESS, value, min, ret, valid);
+ if (!valid) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected = value.get_type();
+ r_error.argument = 1;
+ return Variant();
+ }
+ if (ret.booleanize()) {
+ value = min;
+ }
+ Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid);
+ if (!valid) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected = value.get_type();
+ r_error.argument = 2;
+ return Variant();
+ }
+ if (ret.booleanize()) {
+ value = max;
}
- // Random
+ r_error.error = Callable::CallError::CALL_OK;
- static inline void randomize() {
- Math::randomize();
- }
+ return value;
+}
- static inline int64_t randi() {
- return Math::rand();
- }
+double VariantUtilityFunctions::clampf(double x, double min, double max) {
+ return CLAMP(x, min, max);
+}
- static inline double randf() {
- return Math::randf();
- }
+int64_t VariantUtilityFunctions::clampi(int64_t x, int64_t min, int64_t max) {
+ return CLAMP(x, min, max);
+}
- static inline double randfn(double mean, double deviation) {
- return Math::randfn(mean, deviation);
- }
+int64_t VariantUtilityFunctions::nearest_po2(int64_t x) {
+ return nearest_power_of_2_templated(uint64_t(x));
+}
- static inline int64_t randi_range(int64_t from, int64_t to) {
- return Math::random((int32_t)from, (int32_t)to);
- }
+// Random
- static inline double randf_range(double from, double to) {
- return Math::random(from, to);
- }
+void VariantUtilityFunctions::randomize() {
+ Math::randomize();
+}
- static inline void seed(int64_t s) {
- return Math::seed(s);
- }
+int64_t VariantUtilityFunctions::randi() {
+ return Math::rand();
+}
- static inline PackedInt64Array rand_from_seed(int64_t seed) {
- uint64_t s = seed;
- PackedInt64Array arr;
- arr.resize(2);
- arr.write[0] = Math::rand_from_seed(&s);
- arr.write[1] = s;
- return arr;
- }
+double VariantUtilityFunctions::randf() {
+ return Math::randf();
+}
- // Utility
+double VariantUtilityFunctions::randfn(double mean, double deviation) {
+ return Math::randfn(mean, deviation);
+}
- static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) {
- if (obj.get_type() == Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_OK;
- if (obj.is_ref_counted()) {
- Ref<WeakRef> wref = memnew(WeakRef);
- Ref<RefCounted> r = obj;
- if (r.is_valid()) {
- wref->set_ref(r);
- }
- return wref;
- } else {
- Ref<WeakRef> wref = memnew(WeakRef);
- Object *o = obj.get_validated_object();
- if (o) {
- wref->set_obj(o);
- }
- return wref;
- }
- } else if (obj.get_type() == Variant::NIL) {
- r_error.error = Callable::CallError::CALL_OK;
+int64_t VariantUtilityFunctions::randi_range(int64_t from, int64_t to) {
+ return Math::random((int32_t)from, (int32_t)to);
+}
+
+double VariantUtilityFunctions::randf_range(double from, double to) {
+ return Math::random(from, to);
+}
+
+void VariantUtilityFunctions::seed(int64_t s) {
+ return Math::seed(s);
+}
+
+PackedInt64Array VariantUtilityFunctions::rand_from_seed(int64_t seed) {
+ uint64_t s = seed;
+ PackedInt64Array arr;
+ arr.resize(2);
+ arr.write[0] = Math::rand_from_seed(&s);
+ arr.write[1] = s;
+ return arr;
+}
+
+// Utility
+
+Variant VariantUtilityFunctions::weakref(const Variant &obj, Callable::CallError &r_error) {
+ if (obj.get_type() == Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_OK;
+ if (obj.is_ref_counted()) {
Ref<WeakRef> wref = memnew(WeakRef);
+ Ref<RefCounted> r = obj;
+ if (r.is_valid()) {
+ wref->set_ref(r);
+ }
return wref;
} else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- return Variant();
+ Ref<WeakRef> wref = memnew(WeakRef);
+ Object *o = obj.get_validated_object();
+ if (o) {
+ wref->set_obj(o);
+ }
+ return wref;
}
+ } else if (obj.get_type() == Variant::NIL) {
+ r_error.error = Callable::CallError::CALL_OK;
+ Ref<WeakRef> wref = memnew(WeakRef);
+ return wref;
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ return Variant();
}
+}
- static inline int64_t _typeof(const Variant &obj) {
- return obj.get_type();
+int64_t VariantUtilityFunctions::_typeof(const Variant &obj) {
+ return obj.get_type();
+}
+
+String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ return String();
}
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
- static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- return String();
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
}
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ }
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
- }
+ r_error.error = Callable::CallError::CALL_OK;
- r_error.error = Callable::CallError::CALL_OK;
+ return s;
+}
- return s;
+String VariantUtilityFunctions::error_string(Error error) {
+ if (error < 0 || error >= ERR_MAX) {
+ return String("(invalid error code)");
}
- static inline String error_string(Error error) {
- if (error < 0 || error >= ERR_MAX) {
- return String("(invalid error code)");
- }
+ return String(error_names[error]);
+}
- return String(error_names[error]);
+void VariantUtilityFunctions::print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
+ }
}
- static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ print_line(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
- }
+void VariantUtilityFunctions::print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
- print_line(s);
- r_error.error = Callable::CallError::CALL_OK;
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
+ }
}
- static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ print_line_rich(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
+
+#undef print_verbose
+
+void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
String s;
for (int i = 0; i < p_arg_count; i++) {
String os = p_args[i]->operator String();
@@ -800,247 +817,227 @@ struct VariantUtilityFunctions {
}
}
- print_line_rich(s);
- r_error.error = Callable::CallError::CALL_OK;
+ // No need to use `print_verbose()` as this call already only happens
+ // when verbose mode is enabled. This avoids performing string argument concatenation
+ // when not needed.
+ print_line(s);
}
-#undef print_verbose
+ r_error.error = Callable::CallError::CALL_OK;
+}
- static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
-
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
- }
+void VariantUtilityFunctions::printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
- // No need to use `print_verbose()` as this call already only happens
- // when verbose mode is enabled. This avoids performing string argument concatenation
- // when not needed.
- print_line(s);
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
}
-
- r_error.error = Callable::CallError::CALL_OK;
}
- static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ print_error(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
+void VariantUtilityFunctions::printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ if (i) {
+ s += "\t";
}
-
- print_error(s);
- r_error.error = Callable::CallError::CALL_OK;
+ s += p_args[i]->operator String();
}
- static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- s += "\t";
- }
- s += p_args[i]->operator String();
- }
-
- print_line(s);
- r_error.error = Callable::CallError::CALL_OK;
- }
+ print_line(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- s += " ";
- }
- s += p_args[i]->operator String();
+void VariantUtilityFunctions::prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ if (i) {
+ s += " ";
}
-
- print_line(s);
- r_error.error = Callable::CallError::CALL_OK;
+ s += p_args[i]->operator String();
}
- static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ print_line(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
+void VariantUtilityFunctions::printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
}
+ }
- OS::get_singleton()->print("%s", s.utf8().get_data());
- r_error.error = Callable::CallError::CALL_OK;
+ OS::get_singleton()->print("%s", s.utf8().get_data());
+ r_error.error = Callable::CallError::CALL_OK;
+}
+
+void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
}
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
- static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
}
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ }
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
- }
+ ERR_PRINT(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- ERR_PRINT(s);
- r_error.error = Callable::CallError::CALL_OK;
+void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
}
+ String s;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
- static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ if (i == 0) {
+ s = os;
+ } else {
+ s += os;
}
- String s;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
+ }
- if (i == 0) {
- s = os;
- } else {
- s += os;
- }
- }
+ WARN_PRINT(s);
+ r_error.error = Callable::CallError::CALL_OK;
+}
- WARN_PRINT(s);
- r_error.error = Callable::CallError::CALL_OK;
- }
+String VariantUtilityFunctions::var_to_str(const Variant &p_var) {
+ String vars;
+ VariantWriter::write_to_string(p_var, vars);
+ return vars;
+}
- static inline String var_to_str(const Variant &p_var) {
- String vars;
- VariantWriter::write_to_string(p_var, vars);
- return vars;
- }
+Variant VariantUtilityFunctions::str_to_var(const String &p_var) {
+ VariantParser::StreamString ss;
+ ss.s = p_var;
- static inline Variant str_to_var(const String &p_var) {
- VariantParser::StreamString ss;
- ss.s = p_var;
+ String errs;
+ int line;
+ Variant ret;
+ (void)VariantParser::parse(&ss, ret, errs, line);
- String errs;
- int line;
- Variant ret;
- (void)VariantParser::parse(&ss, ret, errs, line);
+ return ret;
+}
- return ret;
+PackedByteArray VariantUtilityFunctions::var_to_bytes(const Variant &p_var) {
+ int len;
+ Error err = encode_variant(p_var, nullptr, len, false);
+ if (err != OK) {
+ return PackedByteArray();
}
- static inline PackedByteArray var_to_bytes(const Variant &p_var) {
- int len;
- Error err = encode_variant(p_var, nullptr, len, false);
+ PackedByteArray barr;
+ barr.resize(len);
+ {
+ uint8_t *w = barr.ptrw();
+ err = encode_variant(p_var, w, len, false);
if (err != OK) {
return PackedByteArray();
}
+ }
- PackedByteArray barr;
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- err = encode_variant(p_var, w, len, false);
- if (err != OK) {
- return PackedByteArray();
- }
- }
+ return barr;
+}
- return barr;
+PackedByteArray VariantUtilityFunctions::var_to_bytes_with_objects(const Variant &p_var) {
+ int len;
+ Error err = encode_variant(p_var, nullptr, len, true);
+ if (err != OK) {
+ return PackedByteArray();
}
- static inline PackedByteArray var_to_bytes_with_objects(const Variant &p_var) {
- int len;
- Error err = encode_variant(p_var, nullptr, len, true);
+ PackedByteArray barr;
+ barr.resize(len);
+ {
+ uint8_t *w = barr.ptrw();
+ err = encode_variant(p_var, w, len, true);
if (err != OK) {
return PackedByteArray();
}
-
- PackedByteArray barr;
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- err = encode_variant(p_var, w, len, true);
- if (err != OK) {
- return PackedByteArray();
- }
- }
-
- return barr;
}
- static inline Variant bytes_to_var(const PackedByteArray &p_arr) {
- Variant ret;
- {
- const uint8_t *r = p_arr.ptr();
- Error err = decode_variant(ret, r, p_arr.size(), nullptr, false);
- if (err != OK) {
- return Variant();
- }
+ return barr;
+}
+
+Variant VariantUtilityFunctions::bytes_to_var(const PackedByteArray &p_arr) {
+ Variant ret;
+ {
+ const uint8_t *r = p_arr.ptr();
+ Error err = decode_variant(ret, r, p_arr.size(), nullptr, false);
+ if (err != OK) {
+ return Variant();
}
- return ret;
}
+ return ret;
+}
- static inline Variant bytes_to_var_with_objects(const PackedByteArray &p_arr) {
- Variant ret;
- {
- const uint8_t *r = p_arr.ptr();
- Error err = decode_variant(ret, r, p_arr.size(), nullptr, true);
- if (err != OK) {
- return Variant();
- }
+Variant VariantUtilityFunctions::bytes_to_var_with_objects(const PackedByteArray &p_arr) {
+ Variant ret;
+ {
+ const uint8_t *r = p_arr.ptr();
+ Error err = decode_variant(ret, r, p_arr.size(), nullptr, true);
+ if (err != OK) {
+ return Variant();
}
- return ret;
}
+ return ret;
+}
- static inline int64_t hash(const Variant &p_arr) {
- return p_arr.hash();
- }
+int64_t VariantUtilityFunctions::hash(const Variant &p_arr) {
+ return p_arr.hash();
+}
- static inline Object *instance_from_id(int64_t p_id) {
- ObjectID id = ObjectID((uint64_t)p_id);
- Object *ret = ObjectDB::get_instance(id);
- return ret;
- }
+Object *VariantUtilityFunctions::instance_from_id(int64_t p_id) {
+ ObjectID id = ObjectID((uint64_t)p_id);
+ Object *ret = ObjectDB::get_instance(id);
+ return ret;
+}
- static inline bool is_instance_id_valid(int64_t p_id) {
- return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr;
- }
+bool VariantUtilityFunctions::is_instance_id_valid(int64_t p_id) {
+ return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr;
+}
- static inline bool is_instance_valid(const Variant &p_instance) {
- if (p_instance.get_type() != Variant::OBJECT) {
- return false;
- }
- return p_instance.get_validated_object() != nullptr;
+bool VariantUtilityFunctions::is_instance_valid(const Variant &p_instance) {
+ if (p_instance.get_type() != Variant::OBJECT) {
+ return false;
}
+ return p_instance.get_validated_object() != nullptr;
+}
- static inline uint64_t rid_allocate_id() {
- return RID_AllocBase::_gen_id();
- }
+uint64_t VariantUtilityFunctions::rid_allocate_id() {
+ return RID_AllocBase::_gen_id();
+}
- static inline RID rid_from_int64(uint64_t p_base) {
- return RID::from_uint64(p_base);
- }
+RID VariantUtilityFunctions::rid_from_int64(uint64_t p_base) {
+ return RID::from_uint64(p_base);
+}
- static inline bool is_same(const Variant &p_a, const Variant &p_b) {
- return p_a.identity_compare(p_b);
- }
-};
+bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) {
+ return p_a.identity_compare(p_b);
+}
#ifdef DEBUG_METHODS_ENABLED
#define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...)
diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h
new file mode 100644
index 0000000000..78f66987cb
--- /dev/null
+++ b/core/variant/variant_utility.h
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* variant_utility.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 VARIANT_UTILITY_H
+#define VARIANT_UTILITY_H
+
+#include "variant.h"
+
+struct VariantUtilityFunctions {
+ // Math
+ static double sin(double arg);
+ static double cos(double arg);
+ static double tan(double arg);
+ static double sinh(double arg);
+ static double cosh(double arg);
+ static double tanh(double arg);
+ static double asin(double arg);
+ static double acos(double arg);
+ static double atan(double arg);
+ static double atan2(double y, double x);
+ static double sqrt(double x);
+ static double fmod(double b, double r);
+ static double fposmod(double b, double r);
+ static int64_t posmod(int64_t b, int64_t r);
+ static Variant floor(Variant x, Callable::CallError &r_error);
+ static double floorf(double x);
+ static int64_t floori(double x);
+ static Variant ceil(Variant x, Callable::CallError &r_error);
+ static double ceilf(double x);
+ static int64_t ceili(double x);
+ static Variant round(Variant x, Callable::CallError &r_error);
+ static double roundf(double x);
+ static int64_t roundi(double x);
+ static Variant abs(const Variant &x, Callable::CallError &r_error);
+ static double absf(double x);
+ static int64_t absi(int64_t x);
+ static Variant sign(const Variant &x, Callable::CallError &r_error);
+ static double signf(double x);
+ static int64_t signi(int64_t x);
+ static double pow(double x, double y);
+ static double log(double x);
+ static double exp(double x);
+ static bool is_nan(double x);
+ static bool is_inf(double x);
+ static bool is_equal_approx(double x, double y);
+ static bool is_zero_approx(double x);
+ static bool is_finite(double x);
+ static double ease(float x, float curve);
+ static int step_decimals(float step);
+ static Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error);
+ static double snappedf(double x, double step);
+ static int64_t snappedi(double x, int64_t step);
+ static Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error);
+ static double lerpf(double from, double to, double weight);
+ static double cubic_interpolate(double from, double to, double pre, double post, double weight);
+ static double cubic_interpolate_angle(double from, double to, double pre, double post, double weight);
+ static double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight,
+ double to_t, double pre_t, double post_t);
+ static double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight,
+ double to_t, double pre_t, double post_t);
+ static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t);
+ static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t);
+ static double lerp_angle(double from, double to, double weight);
+ static double inverse_lerp(double from, double to, double weight);
+ static double remap(double value, double istart, double istop, double ostart, double ostop);
+ static double smoothstep(double from, double to, double val);
+ static double move_toward(double from, double to, double delta);
+ static double deg_to_rad(double angle_deg);
+ static double rad_to_deg(double angle_rad);
+ static double linear_to_db(double linear);
+ static double db_to_linear(double db);
+ static Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error);
+ static int64_t wrapi(int64_t value, int64_t min, int64_t max);
+ static double wrapf(double value, double min, double max);
+ static double pingpong(double value, double length);
+ static Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ static double maxf(double x, double y);
+ static int64_t maxi(int64_t x, int64_t y);
+ static Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ static double minf(double x, double y);
+ static int64_t mini(int64_t x, int64_t y);
+ static Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error);
+ static double clampf(double x, double min, double max);
+ static int64_t clampi(int64_t x, int64_t min, int64_t max);
+ static int64_t nearest_po2(int64_t x);
+ // Random
+ static void randomize();
+ static int64_t randi();
+ static double randf();
+ static double randfn(double mean, double deviation);
+ static int64_t randi_range(int64_t from, int64_t to);
+ static double randf_range(double from, double to);
+ static void seed(int64_t s);
+ static PackedInt64Array rand_from_seed(int64_t seed);
+ // Utility
+ static Variant weakref(const Variant &obj, Callable::CallError &r_error);
+ static int64_t _typeof(const Variant &obj);
+ static Variant type_convert(const Variant &p_variant, const Variant::Type p_type);
+ static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static String error_string(Error error);
+ static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+#undef print_verbose
+ static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+ static String var_to_str(const Variant &p_var);
+ static Variant str_to_var(const String &p_var);
+ static PackedByteArray var_to_bytes(const Variant &p_var);
+ static PackedByteArray var_to_bytes_with_objects(const Variant &p_var);
+ static Variant bytes_to_var(const PackedByteArray &p_arr);
+ static Variant bytes_to_var_with_objects(const PackedByteArray &p_arr);
+ static int64_t hash(const Variant &p_arr);
+ static Object *instance_from_id(int64_t p_id);
+ static bool is_instance_id_valid(int64_t p_id);
+ static bool is_instance_valid(const Variant &p_instance);
+ static uint64_t rid_allocate_id();
+ static RID rid_from_int64(uint64_t p_base);
+ static bool is_same(const Variant &p_a, const Variant &p_b);
+};
+
+#endif // VARIANT_UTILITY_H
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 3b7974f523..008452a92e 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">
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index 5d54af60ec..d64941cb9b 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -53,6 +53,24 @@
Clears the grid and sets the [member region] to [code]Rect2i(0, 0, 0, 0)[/code].
</description>
</method>
+ <method name="fill_solid_region">
+ <return type="void" />
+ <param index="0" name="region" type="Rect2i" />
+ <param index="1" name="solid" type="bool" default="true" />
+ <description>
+ Fills the given [param region] on the grid with the specified value for the solid flag.
+ [b]Note:[/b] Calling [method update] is not needed after the call of this function.
+ </description>
+ </method>
+ <method name="fill_weight_scale_region">
+ <return type="void" />
+ <param index="0" name="region" type="Rect2i" />
+ <param index="1" name="weight_scale" type="float" />
+ <description>
+ Fills the given [param region] on the grid with the specified value for the weight scale.
+ [b]Note:[/b] Calling [method update] is not needed after the call of this function.
+ </description>
+ </method>
<method name="get_id_path">
<return type="Vector2i[]" />
<param index="0" name="from_id" type="Vector2i" />
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 20a87aea7b..cb4fb8d5ca 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -132,7 +132,7 @@
<method name="get_output_latency" qualifiers="const">
<return type="float" />
<description>
- Returns the audio driver's output latency.
+ Returns the audio driver's output latency. This can be expensive, it is not recommended to call this every frame.
</description>
</method>
<method name="get_speaker_mode" qualifiers="const">
diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml
index b18ee95a43..c5998ad8a1 100644
--- a/doc/classes/BaseButton.xml
+++ b/doc/classes/BaseButton.xml
@@ -17,7 +17,7 @@
</method>
<method name="_toggled" qualifiers="virtual">
<return type="void" />
- <param index="0" name="button_pressed" type="bool" />
+ <param index="0" name="toggled_on" type="bool" />
<description>
Called when the button is toggled (only if [member toggle_mode] is active).
</description>
@@ -98,9 +98,9 @@
</description>
</signal>
<signal name="toggled">
- <param index="0" name="button_pressed" type="bool" />
+ <param index="0" name="toggled_on" type="bool" />
<description>
- Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param button_pressed] argument.
+ Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param toggled_on] argument.
</description>
</signal>
</signals>
diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml
index 605f217eff..fa8112de29 100644
--- a/doc/classes/Bone2D.xml
+++ b/doc/classes/Bone2D.xml
@@ -61,7 +61,7 @@
<param index="0" name="angle" type="float" />
<description>
Sets the bone angle for the [Bone2D]. This is typically set to the rotation from the [Bone2D] to a child [Bone2D] node.
- [b]Note:[/b] [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform].
+ [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform].
</description>
</method>
<method name="set_length">
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/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/Geometry3D.xml b/doc/classes/Geometry3D.xml
index 014ebd2248..85b8965faf 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -73,6 +73,17 @@
Given the two 3D segments ([param p1], [param p2]) and ([param q1], [param q2]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([param p1], [param p2]) as well the accompanying point on ([param q1], [param q2]).
</description>
</method>
+ <method name="get_triangle_barycentric_coords">
+ <return type="Vector3" />
+ <param index="0" name="point" type="Vector3" />
+ <param index="1" name="a" type="Vector3" />
+ <param index="2" name="b" type="Vector3" />
+ <param index="3" name="c" type="Vector3" />
+ <description>
+ Returns a [Vector3] containing weights based on how close a 3D position ([param point]) is to a triangle's different vertices ([param a], [param b] and [param c]). This is useful for interpolating between the data of different vertices in a triangle. One example use case is using this to smoothly rotate over a mesh instead of relying solely on face normals.
+ [url=https://en.wikipedia.org/wiki/Barycentric_coordinate_system]Here is a more detailed explanation of barycentric coordinates.[/url]
+ </description>
+ </method>
<method name="ray_intersects_triangle">
<return type="Variant" />
<param index="0" name="from" type="Vector3" />
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index c2a20827d2..6d907bbb28 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -121,6 +121,20 @@
Returns a SDL2-compatible device GUID on platforms that use gamepad remapping, e.g. [code]030000004c050000c405000000010000[/code]. Returns [code]"Default Gamepad"[/code] otherwise. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names and mappings based on this GUID.
</description>
</method>
+ <method name="get_joy_info" qualifiers="const">
+ <return type="Dictionary" />
+ <param index="0" name="device" type="int" />
+ <description>
+ Returns a dictionary with extra platform-specific information about the device, e.g. the raw gamepad name from the OS or the Steam Input index.
+ On Windows the dictionary contains the following fields:
+ [code]xinput_index[/code]: The index of the controller in the XInput system.
+ On Linux:
+ [code]raw_name[/code]: The name of the controller as it came from the OS, before getting renamed by the godot controller database.
+ [code]vendor_id[/code]: The USB vendor ID of the device.
+ [code]product_id[/code]: The USB product ID of the device.
+ [code]steam_input_index[/code]: The Steam Input gamepad index, if the device is not a Steam Input device this key won't be present.
+ </description>
+ </method>
<method name="get_joy_name">
<return type="String" />
<param index="0" name="device" type="int" />
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/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/Performance.xml b/doc/classes/Performance.xml
index 377dbba9fc..4d01fe1760 100644
--- a/doc/classes/Performance.xml
+++ b/doc/classes/Performance.xml
@@ -192,7 +192,7 @@
Number of islands in the 3D physics engine. [i]Lower is better.[/i]
</constant>
<constant name="AUDIO_OUTPUT_LATENCY" value="23" enum="Monitor">
- Output latency of the [AudioServer]. [i]Lower is better.[/i]
+ Output latency of the [AudioServer]. Equivalent to calling [method AudioServer.get_output_latency], it is not recommended to call this every frame.
</constant>
<constant name="NAVIGATION_ACTIVE_MAPS" value="24" enum="Monitor">
Number of active navigation maps in the [NavigationServer3D]. This also includes the two empty default navigation maps created by World2D and World3D.
diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml
index 17324e49c6..b00dc4236d 100644
--- a/doc/classes/PhysicsDirectSpaceState3D.xml
+++ b/doc/classes/PhysicsDirectSpaceState3D.xml
@@ -67,6 +67,8 @@
[code]collider_id[/code]: The colliding object's ID.
[code]normal[/code]: The object's surface normal at the intersection point, or [code]Vector3(0, 0, 0)[/code] if the ray starts inside the shape and [member PhysicsRayQueryParameters3D.hit_from_inside] is [code]true[/code].
[code]position[/code]: The intersection point.
+ [code]face_index[/code]: The face index at the intersection point.
+ [b]Note:[/b] Returns a valid number only if the intersected shape is a [ConcavePolygonShape3D]. Otherwise, [code]-1[/code] is returned.
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
If the ray did not intersect anything, then an empty dictionary is returned instead.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index bf1fd8bc36..40a475851d 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -297,6 +297,9 @@
[b]Note:[/b] Restart the application after changing this setting.
[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.
+ </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].
</member>
@@ -806,8 +809,8 @@
<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">
</member>
@@ -2620,7 +2623,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>
diff --git a/doc/classes/RDPipelineMultisampleState.xml b/doc/classes/RDPipelineMultisampleState.xml
index 8f43b3a2f7..94b8e3d137 100644
--- a/doc/classes/RDPipelineMultisampleState.xml
+++ b/doc/classes/RDPipelineMultisampleState.xml
@@ -25,7 +25,7 @@
The number of MSAA samples (or SSAA samples if [member enable_sample_shading] is [code]true[/code]) to perform. Higher values result in better antialiasing, at the cost of performance.
</member>
<member name="sample_masks" type="int[]" setter="set_sample_masks" getter="get_sample_masks" default="[]">
- The sampleSee the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-samplemask]sample mask Vulkan documentation[/url] for more details.
+ The sample mask array. See the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-samplemask]sample mask Vulkan documentation[/url] for more details.
</member>
</members>
</class>
diff --git a/doc/classes/RDPipelineRasterizationState.xml b/doc/classes/RDPipelineRasterizationState.xml
index 9da66f3d0d..e97113cce4 100644
--- a/doc/classes/RDPipelineRasterizationState.xml
+++ b/doc/classes/RDPipelineRasterizationState.xml
@@ -29,7 +29,7 @@
The winding order to use to determine which face of a triangle is considered its front face.
</member>
<member name="line_width" type="float" setter="set_line_width" getter="get_line_width" default="1.0">
- THe line width to use when drawing lines (in pixels). Thick lines may not be supported on all hardware.
+ The line width to use when drawing lines (in pixels). Thick lines may not be supported on all hardware.
</member>
<member name="patch_control_points" type="int" setter="set_patch_control_points" getter="get_patch_control_points" default="1">
The number of control points to use when drawing a patch with tessellation enabled. Higher values result in higher quality at the cost of performance.
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index 83476a6d48..4c4db36aca 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -59,6 +59,12 @@
Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]).
</description>
</method>
+ <method name="get_collision_face_index" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the collision object's face index at the collision point, or [code]-1[/code] if the shape intersecting the ray is not a [ConcavePolygonShape3D].
+ </description>
+ </method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index fdabb110c6..4f17e3cf2d 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -83,7 +83,7 @@
<return type="Rect2" />
<param index="0" name="to" type="Vector2" />
<description>
- Returns a copy of this rectangle expanded to include the given [param to] point, if necessary.
+ Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary.
[codeblocks]
[gdscript]
var rect = Rect2(0, 0, 5, 2)
diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml
index 79d9e3f6df..c021a1be26 100644
--- a/doc/classes/Rect2i.xml
+++ b/doc/classes/Rect2i.xml
@@ -82,7 +82,7 @@
<return type="Rect2i" />
<param index="0" name="to" type="Vector2i" />
<description>
- Returns a copy of this rectangle expanded to include the given [param to] point, if necessary.
+ Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary.
[codeblocks]
[gdscript]
var rect = Rect2i(0, 0, 5, 2)
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/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index e4e194adf0..14f6235d08 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -1786,7 +1786,7 @@
<constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true">
</constant>
<constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType">
- Sampler uniform. TODO: Difference between sampler and texture uniform
+ Sampler uniform.
</constant>
<constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE" value="1" enum="UniformType">
Sampler uniform with a texture.
@@ -1795,16 +1795,16 @@
Texture uniform.
</constant>
<constant name="UNIFORM_TYPE_IMAGE" value="3" enum="UniformType">
- Image uniform. TODO: Difference between texture and image uniform
+ Image uniform.
</constant>
<constant name="UNIFORM_TYPE_TEXTURE_BUFFER" value="4" enum="UniformType">
- Texture buffer uniform. TODO: Difference between texture and texture buffe uniformr
+ Texture buffer uniform.
</constant>
<constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER" value="5" enum="UniformType">
- Sampler uniform with a texture buffer. TODO: Difference between texture and texture buffer uniform
+ Sampler uniform with a texture buffer.
</constant>
<constant name="UNIFORM_TYPE_IMAGE_BUFFER" value="6" enum="UniformType">
- Image buffer uniform. TODO: Difference between texture and image uniforms
+ Image buffer uniform.
</constant>
<constant name="UNIFORM_TYPE_UNIFORM_BUFFER" value="7" enum="UniformType">
Uniform buffer uniform.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index e360bc2a5f..bb4a0f8a16 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1375,7 +1375,7 @@
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
- Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RendeeringServer directly.
+ Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingServer directly.
</description>
</method>
<method name="get_default_clear_color">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index c0d98ef921..7aed63a2fc 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -225,7 +225,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/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/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/Viewport.xml b/doc/classes/Viewport.xml
index 761506831f..98836db157 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -337,7 +337,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>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 5790fc347a..82498b9ba4 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -10,6 +10,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="_get_contents_minimum_size" qualifiers="virtual const">
+ <return type="Vector2" />
+ <description>
+ Virtual method to be implemented by the user. Overrides the value returned by [method get_contents_minimum_size].
+ </description>
+ </method>
<method name="add_theme_color_override">
<return type="void" />
<param index="0" name="name" type="StringName" />
@@ -92,6 +98,7 @@
<return type="Vector2" />
<description>
Returns the combined minimum size from the child [Control] nodes of the window. Use [method child_controls_changed] to update it when children nodes have changed.
+ The value returned by this method can be overridden with [method _get_contents_minimum_size].
</description>
</method>
<method name="get_flag" qualifiers="const">
diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml
index 9059a5a465..b933abd182 100644
--- a/doc/classes/XRInterfaceExtension.xml
+++ b/doc/classes/XRInterfaceExtension.xml
@@ -101,7 +101,7 @@
<method name="_get_system_info" qualifiers="virtual const">
<return type="Dictionary" />
<description>
- Returns a [Dictionary] with system informationr elated to this interface.
+ Returns a [Dictionary] with system information related to this interface.
</description>
</method>
<method name="_get_tracking_status" qualifiers="virtual const">
diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml
index 9e7d1fc965..647bd85d3d 100644
--- a/doc/classes/bool.xml
+++ b/doc/classes/bool.xml
@@ -4,42 +4,46 @@
A built-in boolean type.
</brief_description>
<description>
- A [bool] is always one of two values: [code]true[/code] or [code]false[/code], similar to a switch that is either on or off. Booleans are used in programming for logic in condition statements.
- Booleans can be directly used in [code]if[/code] and [code]elif[/code] statements. You don't need to add [code]== true[/code] or [code]== false[/code]:
+ The [bool] is a built-in [Variant] type that may only store one of two values: [code]true[/code] or [code]false[/code]. You can imagine it as a switch that can be either turned on or off, or as a binary digit that can either be 1 or 0.
+ Booleans can be directly used in [code]if[/code], and other conditional statements:
[codeblocks]
[gdscript]
+ var can_shoot = true
if can_shoot:
launch_bullet()
[/gdscript]
[csharp]
+ bool canShoot = true;
if (canShoot)
{
- launchBullet();
+ LaunchBullet();
}
[/csharp]
[/codeblocks]
- Many common methods and operations return [bool]s, for example, [code]shooting_cooldown &lt;= 0.0[/code] may evaluate to [code]true[/code] or [code]false[/code] depending on the number's value.
- [bool]s are usually used with the logical operators [code]and[/code], [code]or[/code], and [code]not[/code] to create complex conditions:
+ All comparison operators return booleans ([code]==[/code], [code]&gt;[/code], [code]&lt;=[/code], etc.). As such, it is not necessary to compare booleans themselves. You do not need to add [code]== true[/code] or [code]== false[/code].
+ Booleans can be combined with the logical operators [code]and[/code], [code]or[/code], [code]not[/code] to create complex conditions:
[codeblocks]
[gdscript]
- if bullets &gt; 0 and not is_reloading:
+ if bullets &gt; 0 and not is_reloading():
launch_bullet()
- if bullets == 0 or is_reloading:
+ if bullets == 0 or is_reloading():
play_clack_sound()
[/gdscript]
[csharp]
- if (bullets &gt; 0 &amp;&amp; !isReloading)
+ if (bullets &gt; 0 &amp;&amp; !IsReloading())
{
- launchBullet();
+ LaunchBullet();
}
- if (bullets == 0 || isReloading)
+ if (bullets == 0 || IsReloading())
{
- playClackSound();
+ PlayClackSound();
}
[/csharp]
[/codeblocks]
+ [b]Note:[/b] In modern programming languages, logical operators are evaluated in order. All remaining conditions are skipped if their result would have no effect on the final value. This concept is known as [url=https://en.wikipedia.org/wiki/Short-circuit_evaluation]short-circuit evaluation[/url] and can be useful to avoid evaluating expensive conditions in some performance-critical cases.
+ [b]Note:[/b] By convention, built-in methods and properties that return booleans are usually defined as yes-no questions, single adjectives, or similar ([method String.is_empty], [method Node.can_process], [member Camera2D.enabled], etc.).
</description>
<tutorials>
</tutorials>
@@ -47,7 +51,7 @@
<constructor name="bool">
<return type="bool" />
<description>
- Constructs a default-initialized [bool] set to [code]false[/code].
+ Constructs a [bool] set to [code]false[/code].
</description>
</constructor>
<constructor name="bool">
@@ -61,14 +65,14 @@
<return type="bool" />
<param index="0" name="from" type="float" />
<description>
- Cast a [float] value to a boolean value. This method will return [code]false[/code] if [code]0.0[/code] is passed in, and [code]true[/code] for all other values.
+ Cast a [float] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0.0[/code] (including [code]-0.0[/code]), and [code]true[/code] for all other values (including [constant @GDScript.INF] and [constant @GDScript.NAN]).
</description>
</constructor>
<constructor name="bool">
<return type="bool" />
<param index="0" name="from" type="int" />
<description>
- Cast an [int] value to a boolean value. This method will return [code]false[/code] if [code]0[/code] is passed in, and [code]true[/code] for all other values.
+ Cast an [int] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0[/code], and [code]true[/code] for all other values.
</description>
</constructor>
</constructors>
@@ -77,7 +81,7 @@
<return type="bool" />
<param index="0" name="right" type="bool" />
<description>
- Returns [code]true[/code] if two bools are different, i.e. one is [code]true[/code] and the other is [code]false[/code].
+ Returns [code]true[/code] if the two booleans are not equal. That is, one is [code]true[/code] and the other is [code]false[/code]. This operation can be seen as a logical XOR.
</description>
</operator>
<operator name="operator &lt;">
@@ -91,7 +95,7 @@
<return type="bool" />
<param index="0" name="right" type="bool" />
<description>
- Returns [code]true[/code] if two bools are equal, i.e. both are [code]true[/code] or both are [code]false[/code].
+ Returns [code]true[/code] if the two booleans are equal. That is, both are [code]true[/code] or both are [code]false[/code]. This operation can be seen as a logical EQ or XNOR.
</description>
</operator>
<operator name="operator &gt;">
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index 14e30b69a5..7d74e3d832 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -70,7 +70,7 @@
<description>
Multiplies each component of the [Color], including the alpha, by the given [float].
[codeblock]
- print(1.5 * Color(0.5, 0.5, 0.5)) # Color(0.75, 0.75, 0.75)
+ print(1.5 * Color(0.5, 0.5, 0.5)) # Prints "(0.75, 0.75, 0.75, 1.5)"
[/codeblock]
</description>
</operator>
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index fe8c8fa8cc..dd4777a19b 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -33,30 +33,30 @@ if env["builtin_libpng"]:
# Needed for drivers includes and in platform/web.
env.Prepend(CPPPATH=[thirdparty_dir])
- # Currently .ASM filter_neon.S does not compile on NT.
- import os
-
- # Enable ARM NEON instructions on 32-bit Android to compile more optimized code.
- use_neon = env["platform"] == "android" and env["arch"] == "arm32" and os.name != "nt"
- if use_neon:
- env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 2)])
- else:
- env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 0)])
-
env_thirdparty = env_png.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- if use_neon:
- env_neon = env_thirdparty.Clone()
- if "S_compiler" in env:
- env_neon["CC"] = env["S_compiler"]
- neon_sources = []
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
- thirdparty_obj += neon_sources
+ if env["arch"].startswith("arm"):
+ if env.msvc: # Can't compile assembly files with MSVC.
+ env_thirdparty.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT"), 0])
+ else:
+ env_neon = env_thirdparty.Clone()
+ if "S_compiler" in env:
+ env_neon["CC"] = env["S_compiler"]
+ neon_sources = []
+ neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
+ thirdparty_obj += neon_sources
+ elif env["arch"].startswith("x86"):
+ env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"])
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c")
+ elif env["arch"] == "ppc64":
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c")
env.drivers_sources += thirdparty_obj
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index ab94d8911f..8ca396af20 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -370,27 +370,24 @@ Error AudioDriverPulseAudio::init() {
}
float AudioDriverPulseAudio::get_latency() {
- if (latency == 0) { //only do this once since it's approximate anyway
- lock();
+ lock();
- pa_usec_t palat = 0;
- if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {
- int negative = 0;
+ pa_usec_t pa_lat = 0;
+ if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {
+ int negative = 0;
- if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) {
- if (negative) {
- palat = 0;
- }
+ if (pa_stream_get_latency(pa_str, &pa_lat, &negative) >= 0) {
+ if (negative) {
+ pa_lat = 0;
}
}
+ }
- if (palat > 0) {
- latency = double(palat) / 1000000.0;
- }
-
- unlock();
+ if (pa_lat > 0) {
+ latency = double(pa_lat) / 1000000.0;
}
+ unlock();
return latency;
}
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index d521f675fb..1ed6839dd7 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -5043,17 +5043,24 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
_THREAD_SAFE_METHOD_
- Shader shader;
+ RID id;
+ if (p_placeholder.is_null()) {
+ id = shader_owner.make_rid();
+ } else {
+ id = p_placeholder;
+ }
+
+ Shader *shader = shader_owner.get_or_null(id);
- shader.vertex_input_mask = vertex_input_mask;
- shader.fragment_output_mask = fragment_output_mask;
- shader.push_constant = push_constant;
- shader.is_compute = is_compute;
- shader.compute_local_size[0] = compute_local_size[0];
- shader.compute_local_size[1] = compute_local_size[1];
- shader.compute_local_size[2] = compute_local_size[2];
- shader.specialization_constants = specialization_constants;
- shader.name = name;
+ shader->vertex_input_mask = vertex_input_mask;
+ shader->fragment_output_mask = fragment_output_mask;
+ shader->push_constant = push_constant;
+ shader->is_compute = is_compute;
+ shader->compute_local_size[0] = compute_local_size[0];
+ shader->compute_local_size[1] = compute_local_size[1];
+ shader->compute_local_size[2] = compute_local_size[2];
+ shader->specialization_constants = specialization_constants;
+ shader->name = name;
String error_text;
@@ -5085,7 +5092,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
shader_stage.pName = "main";
shader_stage.pSpecializationInfo = nullptr;
- shader.pipeline_stages.push_back(shader_stage);
+ shader->pipeline_stages.push_back(shader_stage);
}
// Proceed to create descriptor sets.
@@ -5128,8 +5135,8 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
}
}
- shader.sets.push_back(set);
- shader.set_formats.push_back(format);
+ shader->sets.push_back(set);
+ shader->set_formats.push_back(format);
}
}
@@ -5139,13 +5146,13 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.pNext = nullptr;
pipeline_layout_create_info.flags = 0;
- pipeline_layout_create_info.setLayoutCount = shader.sets.size();
+ pipeline_layout_create_info.setLayoutCount = shader->sets.size();
Vector<VkDescriptorSetLayout> layouts;
- layouts.resize(shader.sets.size());
+ layouts.resize(shader->sets.size());
for (int i = 0; i < layouts.size(); i++) {
- layouts.write[i] = shader.sets[i].descriptor_set_layout;
+ layouts.write[i] = shader->sets[i].descriptor_set_layout;
}
pipeline_layout_create_info.pSetLayouts = layouts.ptr();
@@ -5164,7 +5171,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
pipeline_layout_create_info.pPushConstantRanges = nullptr;
}
- VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &shader.pipeline_layout);
+ VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &shader->pipeline_layout);
if (err) {
error_text = "Error (" + itos(err) + ") creating pipeline layout.";
@@ -5174,23 +5181,19 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
if (!success) {
// Clean up if failed.
- for (int i = 0; i < shader.pipeline_stages.size(); i++) {
- vkDestroyShaderModule(device, shader.pipeline_stages[i].module, nullptr);
+ for (int i = 0; i < shader->pipeline_stages.size(); i++) {
+ vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr);
}
- for (int i = 0; i < shader.sets.size(); i++) {
- vkDestroyDescriptorSetLayout(device, shader.sets[i].descriptor_set_layout, nullptr);
+ for (int i = 0; i < shader->sets.size(); i++) {
+ vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr);
}
+ shader_owner.free(id);
+
ERR_FAIL_V_MSG(RID(), error_text);
}
- RID id;
- if (p_placeholder.is_null()) {
- id = shader_owner.make_rid(shader);
- } else {
- shader_owner.initialize_rid(p_placeholder, shader);
- id = p_placeholder;
- }
+
#ifdef DEV_ENABLED
set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
@@ -5198,7 +5201,8 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
}
RID RenderingDeviceVulkan::shader_create_placeholder() {
- return shader_owner.allocate_rid();
+ Shader shader;
+ return shader_owner.make_rid(shader);
}
uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
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/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 2c40f0e120..afa6aaf395 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -569,8 +569,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
error->set_custom_color(1, color);
String error_title;
- if (oe.callstack.size() > 0) {
- // If available, use the script's stack in the error title.
+ if (!oe.source_func.is_empty() && source_is_project_file) {
+ // If source function is inside the project file.
+ error_title += oe.source_func + ": ";
+ } else if (oe.callstack.size() > 0) {
+ // Otherwise, if available, use the script's stack in the error title.
error_title = _format_frame_text(&oe.callstack[0]) + ": ";
} else if (!oe.source_func.is_empty()) {
// Otherwise try to use the C++ source function.
@@ -645,7 +648,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
if (i == 0) {
stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">");
stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
- error->set_metadata(0, meta);
+ if (!source_is_project_file) {
+ // Only override metadata if the source is not inside the project.
+ error->set_metadata(0, meta);
+ }
tooltip += TTR("Stack Trace:") + "\n";
}
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_node.cpp b/editor/editor_node.cpp
index 204eaaf12f..2efd3c031e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5009,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);
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 77d6ec9ab2..d6af7107a3 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2797,7 +2797,7 @@ void EditorPropertyNodePath::_node_assign() {
if (!scene_tree) {
scene_tree = memnew(SceneTreeDialog);
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
- scene_tree->get_scene_tree()->set_valid_types(valid_types);
+ scene_tree->set_valid_types(valid_types);
add_child(scene_tree);
scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected));
}
@@ -3145,7 +3145,7 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource)
Vector<StringName> valid_types;
valid_types.push_back("Viewport");
- scene_tree->get_scene_tree()->set_valid_types(valid_types);
+ scene_tree->set_valid_types(valid_types);
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
add_child(scene_tree);
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_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 38a78babfb..c6747c4481 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -35,7 +35,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/message_queue.h"
-#include "core/variant/variant_utility.cpp"
+#include "core/variant/variant_utility.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 3302b1dc41..7952efff97 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.
@@ -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")));
@@ -1944,7 +1970,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_preset.cpp b/editor/export/editor_export_preset.cpp
index dfc0c23afc..29a31c0470 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -30,6 +30,8 @@
#include "editor_export.h"
+#include "core/config/project_settings.h"
+
bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
values[p_name] = p_value;
EditorExport::singleton->save_presets();
@@ -332,4 +334,35 @@ Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p
return get(p_name, r_valid);
}
+String EditorExportPreset::get_version(const StringName &p_preset_string, bool p_windows_version) const {
+ String result = get(p_preset_string);
+ if (result.is_empty()) {
+ result = GLOBAL_GET("application/config/version");
+
+ if (p_windows_version) {
+ // Modify version number to match Windows constraints (version numbers must have 4 components).
+ const PackedStringArray result_split = result.split(".");
+ String windows_version;
+ if (result_split.is_empty()) {
+ // Use a valid fallback if the version string is empty, as a version number must be specified.
+ result = "1.0.0.0";
+ } else if (result_split.size() == 1) {
+ result = result + ".0.0.0";
+ } else if (result_split.size() == 2) {
+ result = result + ".0.0";
+ } else if (result_split.size() == 3) {
+ result = result + ".0";
+ } else {
+ // 4 components or more in the version string. Trim to contain only the first 4 components.
+ result = vformat("%s.%s.%s.%s", result_split[0] + result_split[1] + result_split[2] + result_split[3]);
+ }
+ } else if (result.is_empty()) {
+ // Use a valid fallback if the version string is empty, as a version number must be specified.
+ result = "1.0.0";
+ }
+ }
+
+ return result;
+}
+
EditorExportPreset::EditorExportPreset() {}
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index 8b59da06dd..025e7603f3 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -154,6 +154,13 @@ public:
Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
+ // Return the preset's version number, or fall back to the
+ // `application/config/version` project setting if set to an empty string.
+ // If `p_windows_version` is `true`, formats the returned version number to
+ // be compatible with Windows executable metadata (which requires a
+ // 4-component format).
+ String get_version(const StringName &p_name, bool p_windows_version = false) const;
+
const HashMap<StringName, PropertyInfo> &get_properties() const { return properties; }
const HashMap<StringName, Variant> &get_values() const { return values; }
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_run_bar.cpp b/editor/gui/editor_run_bar.cpp
index c226c1a2d6..e144d1d10d 100644
--- a/editor/gui/editor_run_bar.cpp
+++ b/editor/gui/editor_run_bar.cpp
@@ -348,6 +348,10 @@ bool EditorRunBar::is_movie_maker_enabled() const {
return write_movie_button->is_pressed();
}
+HBoxContainer *EditorRunBar::get_buttons_container() {
+ return main_hbox;
+}
+
void EditorRunBar::_bind_methods() {
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("stop_pressed"));
@@ -359,7 +363,7 @@ EditorRunBar::EditorRunBar() {
main_panel = memnew(PanelContainer);
add_child(main_panel);
- HBoxContainer *main_hbox = memnew(HBoxContainer);
+ main_hbox = memnew(HBoxContainer);
main_panel->add_child(main_hbox);
play_button = memnew(Button);
diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h
index b7e7db2bd6..1cb999612a 100644
--- a/editor/gui/editor_run_bar.h
+++ b/editor/gui/editor_run_bar.h
@@ -39,6 +39,7 @@ class Button;
class EditorRunNative;
class EditorQuickOpen;
class PanelContainer;
+class HBoxContainer;
class EditorRunBar : public MarginContainer {
GDCLASS(EditorRunBar, MarginContainer);
@@ -53,6 +54,7 @@ class EditorRunBar : public MarginContainer {
};
PanelContainer *main_panel = nullptr;
+ HBoxContainer *main_hbox = nullptr;
Button *play_button = nullptr;
Button *pause_button = nullptr;
@@ -109,6 +111,8 @@ public:
Button *get_pause_button() { return pause_button; }
+ HBoxContainer *get_buttons_container();
+
EditorRunBar();
};
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 fbe167814d..d07e5dfa1a 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -41,6 +41,7 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
+#include "scene/gui/flow_container.h"
#include "scene/gui/label.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/texture_rect.h"
@@ -293,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);
@@ -964,26 +965,55 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) {
String new_name = p_name.validate_node_name();
if (new_name != p_name) {
- error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters());
- error->popup_centered();
-
- if (new_name.is_empty()) {
- item->set_text(0, p_node->get_name());
- return;
+ String text = TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters();
+ if (error->is_visible()) {
+ if (!error->get_meta("invalid_character", false)) {
+ error->set_text(error->get_text() + "\n\n" + text);
+ error->set_meta("invalid_character", true);
+ }
+ } else {
+ error->set_text(text);
+ error->set_meta("invalid_character", true);
+ error->set_meta("same_unique_name", false);
+ error->popup_centered();
}
+ }
- item->set_text(0, new_name);
+ // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`.
+ new_name = new_name.strip_edges();
+ if (new_name.is_empty()) {
+ // If name is empty, fallback to class name.
+ if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
+ new_name = Node::adjust_name_casing(p_node->get_class());
+ } else {
+ new_name = p_node->get_class();
+ }
}
if (new_name == p_node->get_name()) {
if (item->get_text(0).is_empty()) {
item->set_text(0, new_name);
}
+ return;
+ }
+ // We previously made sure name is not the same as current name so that it won't complain about already used unique name when not changing name.
+ if (p_node->is_unique_name_in_owner() && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name)) {
+ String text = TTR("Another node already uses this unique name in the scene.");
+ if (error->is_visible()) {
+ if (!error->get_meta("same_unique_name", false)) {
+ error->set_text(error->get_text() + "\n\n" + text);
+ error->set_meta("same_unique_name", true);
+ }
+ } else {
+ error->set_text(text);
+ error->set_meta("same_unique_name", true);
+ error->set_meta("invalid_character", false);
+ error->popup_centered();
+ }
+ item->set_text(0, p_node->get_name());
return;
}
- // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`.
- new_name = new_name.strip_edges();
if (!is_scene_tree_dock) {
p_node->set_name(new_name);
@@ -999,7 +1029,7 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) {
undo_redo->add_undo_method(item, "set_metadata", 0, p_node->get_path());
undo_redo->add_undo_method(item, "set_text", 0, p_node->get_name());
- p_node->set_name(p_name);
+ p_node->set_name(new_name);
undo_redo->add_do_method(p_node, "set_name", new_name);
undo_redo->add_do_method(item, "set_metadata", 0, p_node->get_path());
undo_redo->add_do_method(item, "set_text", 0, new_name);
@@ -1017,28 +1047,6 @@ void SceneTreeEditor::_renamed() {
ERR_FAIL_COND(!n);
String new_name = which->get_text(0);
- if (new_name.strip_edges().is_empty()) {
- // If name is empty, fallback to class name.
- if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
- new_name = Node::adjust_name_casing(n->get_class());
- } else {
- new_name = n->get_class();
- }
- }
-
- if (n->is_unique_name_in_owner()) {
- Node *existing = get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name);
- if (existing == n) {
- which->set_text(0, n->get_name());
- return;
- }
- if (existing != nullptr) {
- error->set_text(TTR("Another node already uses this unique name in the scene."));
- error->popup_centered();
- which->set_text(0, n->get_name());
- return;
- }
- }
_rename_node(n, new_name);
}
@@ -1486,8 +1494,51 @@ void SceneTreeDialog::popup_scenetree_dialog() {
popup_centered_clamped(Size2(350, 700) * EDSCALE);
}
+void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) {
+ if (p_valid.is_empty()) {
+ return;
+ }
+
+ tree->set_valid_types(p_valid);
+
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ content->add_child(hbox);
+ content->move_child(hbox, 0);
+
+ {
+ Label *label = memnew(Label);
+ hbox->add_child(label);
+ label->set_text(TTR("Allowed:"));
+ }
+
+ HFlowContainer *hflow = memnew(HFlowContainer);
+ hbox->add_child(hflow);
+ hflow->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ for (const StringName &type : p_valid) {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hflow->add_child(hb);
+
+ TextureRect *trect = memnew(TextureRect);
+ hb->add_child(trect);
+ trect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
+ trect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ trect->set_meta("type", type);
+ valid_type_icons.push_back(trect);
+
+ Label *label = memnew(Label);
+ hb->add_child(label);
+ label->set_text(type);
+ label->set_auto_translate(false);
+ }
+}
+
void SceneTreeDialog::_update_theme() {
filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ for (TextureRect *trect : valid_type_icons) {
+ trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")), 0));
+ trect->set_texture(EditorNode::get_singleton()->get_class_icon(trect->get_meta("type")));
+ }
}
void SceneTreeDialog::_notification(int p_what) {
@@ -1544,8 +1595,8 @@ void SceneTreeDialog::_bind_methods() {
SceneTreeDialog::SceneTreeDialog() {
set_title(TTR("Select a Node"));
- VBoxContainer *vbc = memnew(VBoxContainer);
- add_child(vbc);
+ content = memnew(VBoxContainer);
+ add_child(content);
filter = memnew(LineEdit);
filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -1553,12 +1604,12 @@ SceneTreeDialog::SceneTreeDialog() {
filter->set_clear_button_enabled(true);
filter->add_theme_constant_override("minimum_character_width", 0);
filter->connect("text_changed", callable_mp(this, &SceneTreeDialog::_filter_changed));
- vbc->add_child(filter);
+ content->add_child(filter);
tree = memnew(SceneTreeEditor(false, false, true));
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tree->get_scene_tree()->connect("item_activated", callable_mp(this, &SceneTreeDialog::_select));
- vbc->add_child(tree);
+ content->add_child(tree);
// Disable the OK button when no node is selected.
get_ok_button()->set_disabled(!tree->get_selected());
diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h
index 8878256009..c99f84912b 100644
--- a/editor/gui/scene_tree_editor.h
+++ b/editor/gui/scene_tree_editor.h
@@ -35,6 +35,7 @@
#include "scene/gui/tree.h"
class EditorSelection;
+class TextureRect;
class SceneTreeEditor : public Control {
GDCLASS(SceneTreeEditor, Control);
@@ -172,10 +173,10 @@ public:
class SceneTreeDialog : public ConfirmationDialog {
GDCLASS(SceneTreeDialog, ConfirmationDialog);
+ VBoxContainer *content = nullptr;
SceneTreeEditor *tree = nullptr;
- //Button *select;
- //Button *cancel;
LineEdit *filter = nullptr;
+ LocalVector<TextureRect *> valid_type_icons;
void _select();
void _cancel();
@@ -189,8 +190,11 @@ protected:
public:
void popup_scenetree_dialog();
+ void set_valid_types(const Vector<StringName> &p_valid);
+
SceneTreeEditor *get_scene_tree() { return tree; }
LineEdit *get_filter_line_edit() { return filter; }
+
SceneTreeDialog();
~SceneTreeDialog();
};
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/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/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/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/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_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/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..75bb6eefe8 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();
@@ -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/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 6228faaa72..6b5b0f9214 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -105,7 +105,7 @@ void CurveEdit::set_snap_count(int p_snap_count) {
}
Size2 CurveEdit::get_minimum_size() const {
- return Vector2(64, 135) * EDSCALE;
+ return Vector2(64, MAX(135, get_size().x * ASPECT_RATIO)) * EDSCALE;
}
void CurveEdit::_notification(int p_what) {
@@ -986,6 +986,9 @@ void CurveEditor::_notification(int p_what) {
snap_count_edit->set_value(curve->get_meta("_snap_count", DEFAULT_SNAP));
}
} break;
+ case NOTIFICATION_RESIZED:
+ curve_editor_rect->update_minimum_size();
+ break;
}
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index b8d24c5cbc..b6a74d9b93 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -104,6 +104,8 @@ private:
void _redraw();
private:
+ const float ASPECT_RATIO = 6.f / 13.f;
+
Transform2D _world_to_view;
Ref<Curve> curve;
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 79f46f7bf7..b967cb7ccd 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
@@ -134,6 +134,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p
return Variant();
}
+void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
+ initial_transform = p_gizmo->get_node_3d()->get_global_transform();
+ initial_value = get_handle_value(p_gizmo, p_id, p_secondary);
+}
+
void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
@@ -142,7 +147,7 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
return;
}
- Transform3D gt = cs->get_global_transform();
+ Transform3D gt = initial_transform;
Transform3D gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
@@ -184,22 +189,37 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
if (Object::cast_to<BoxShape3D>(*s)) {
Vector3 axis;
- axis[p_id] = 1.0;
+ axis[p_id / 2] = 1.0;
Ref<BoxShape3D> bs = s;
Vector3 ra, rb;
- Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
- float d = ra[p_id] * 2;
- if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
- }
+ int sign = p_id % 2 * -2 + 1;
+ Vector3 initial_size = initial_value;
- if (d < 0.001) {
- d = 0.001;
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * sign, sg[0], sg[1], ra, rb);
+ if (ra[p_id / 2] == 0) {
+ // Point before half of the shape. Needs to be calculated in opposite direction.
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * -sign, sg[0], sg[1], ra, rb);
}
+ float d = ra[p_id / 2] * sign;
+
Vector3 he = bs->get_size();
- he[p_id] = d;
- bs->set_size(he);
+ he[p_id / 2] = d * 2;
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ he[p_id / 2] = Math::snapped(he[p_id / 2], Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
+ he[p_id / 2] = MAX(he[p_id / 2], 0.001);
+ bs->set_size(he);
+ cs->set_global_position(initial_transform.get_origin());
+ } else {
+ he[p_id / 2] = MAX(he[p_id / 2], -initial_size[p_id / 2] + 0.002);
+ bs->set_size((initial_size + (he - initial_size) * 0.5).abs());
+ Vector3 pos = initial_transform.affine_inverse().xform(initial_transform.get_origin());
+ pos += (bs->get_size() - initial_size) * 0.5 * sign;
+ cs->set_global_position(initial_transform.xform(pos));
+ }
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
@@ -273,6 +293,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
if (Object::cast_to<BoxShape3D>(*s)) {
Ref<BoxShape3D> ss = s;
if (p_cancel) {
+ cs->set_global_position(initial_transform.get_origin());
ss->set_size(p_restore);
return;
}
@@ -280,7 +301,9 @@ 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_undo_method(ss.ptr(), "set_size", p_restore);
+ ur->add_undo_method(cs, "set_global_position", initial_transform.get_origin());
ur->commit_action();
}
@@ -429,6 +452,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector3 ax;
ax[i] = bs->get_size()[i] / 2;
handles.push_back(ax);
+ handles.push_back(-ax);
}
p_gizmo->add_lines(lines, material);
diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
index 520295a522..6b7740de2f 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h
@@ -36,6 +36,9 @@
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
+ Transform3D initial_transform;
+ Variant initial_value;
+
public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
@@ -44,6 +47,7 @@ public:
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
+ void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 03d3ac1732..78bbc1484b 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -116,11 +116,8 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
- ur->add_do_reference(cpu_particles);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false);
- ur->add_undo_reference(particles);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(particles, cpu_particles);
+ ur->commit_action(false);
} break;
case MENU_RESTART: {
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index f0b2e32c72..108f85152f 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -277,11 +277,8 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles3D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
- ur->add_do_reference(cpu_particles);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, cpu_particles);
+ ur->commit_action(false);
} break;
case MENU_OPTION_RESTART: {
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_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 0da6fd3c41..2a91d9f108 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -115,6 +115,15 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const {
return gizmo_plugin->get_handle_value(this, p_id, p_secondary);
}
+void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) {
+ if (GDVIRTUAL_CALL(_begin_handle_action, p_id, p_secondary)) {
+ return;
+ }
+
+ ERR_FAIL_COND(!gizmo_plugin);
+ gizmo_plugin->begin_handle_action(this, p_id, p_secondary);
+}
+
void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
if (GDVIRTUAL_CALL(_set_handle, p_id, p_secondary, p_camera, p_point)) {
return;
@@ -1095,6 +1104,10 @@ Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_giz
return ret;
}
+void EditorNode3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
+ GDVIRTUAL_CALL(_begin_handle_action, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary);
+}
+
void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_camera, p_point);
}
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index ae2214de98..d7c368d5d0 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -83,6 +83,7 @@ protected:
GDVIRTUAL2RC(String, _get_handle_name, int, bool)
GDVIRTUAL2RC(bool, _is_handle_highlighted, int, bool)
GDVIRTUAL2RC(Variant, _get_handle_value, int, bool)
+ GDVIRTUAL2(_begin_handle_action, int, bool)
GDVIRTUAL4(_set_handle, int, bool, const Camera3D *, Vector2)
GDVIRTUAL4(_commit_handle, int, bool, Variant, bool)
@@ -104,6 +105,7 @@ public:
virtual bool is_handle_highlighted(int p_id, bool p_secondary) const;
virtual String get_handle_name(int p_id, bool p_secondary) const;
virtual Variant get_handle_value(int p_id, bool p_secondary) const;
+ virtual void begin_handle_action(int p_id, bool p_secondary);
virtual void set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point);
virtual void commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false);
@@ -170,6 +172,7 @@ protected:
GDVIRTUAL3RC(bool, _is_handle_highlighted, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL3RC(Variant, _get_handle_value, Ref<EditorNode3DGizmo>, int, bool)
+ GDVIRTUAL3(_begin_handle_action, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL5(_set_handle, Ref<EditorNode3DGizmo>, int, bool, const Camera3D *, Vector2)
GDVIRTUAL5(_commit_handle, Ref<EditorNode3DGizmo>, int, bool, Variant, bool)
@@ -196,6 +199,7 @@ public:
virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
+ virtual void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary);
virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point);
virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 151f11d9cb..d05db7aa63 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1775,6 +1775,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
seg->handles_intersect_ray(camera, _edit.mouse_pos, b->is_shift_pressed(), gizmo_handle, gizmo_secondary);
if (gizmo_handle != -1) {
_edit.gizmo = seg;
+ seg->begin_handle_action(gizmo_handle, gizmo_secondary);
_edit.gizmo_handle = gizmo_handle;
_edit.gizmo_handle_secondary = gizmo_secondary;
_edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle, gizmo_secondary);
@@ -2152,7 +2153,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (_edit.mode == TRANSFORM_NONE) {
- if (_edit.gizmo.is_valid()) {
+ if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) {
// Restore.
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
_edit.gizmo = Ref<EditorNode3DGizmo>();
@@ -2824,7 +2825,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));
@@ -4917,7 +4918,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
}
}
-// Perform cleanup after a transform operation is committed or cancelled.
+// Perform cleanup after a transform operation is committed or canceled.
void Node3DEditorViewport::finish_transform() {
spatial_editor->set_local_coords_enabled(_edit.original_local);
_edit.mode = TRANSFORM_NONE;
@@ -8128,7 +8129,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/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 0d00bfa8fd..f647bded95 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -346,11 +346,8 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to MeshInstance2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
- ur->add_do_reference(mesh_instance);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, mesh_instance);
+ ur->commit_action(false);
}
void Sprite2DEditor::_convert_to_polygon_2d_node() {
@@ -404,11 +401,8 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Polygon2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
- ur->add_do_reference(polygon_2d_instance);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, polygon_2d_instance);
+ ur->commit_action(false);
}
void Sprite2DEditor::_create_collision_polygon_2d_node() {
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/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/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 6adfbef06f..e8db6ed679 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)) {
@@ -511,7 +513,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) {
@@ -2363,16 +2368,12 @@ void SceneTreeDock::_create() {
Variant c = create_dialog->instantiate_selected();
ERR_FAIL_COND(!c);
- Node *newnode = Object::cast_to<Node>(c);
- ERR_FAIL_COND(!newnode);
-
- ur->add_do_method(this, "replace_node", n, newnode, true, false);
- ur->add_do_reference(newnode);
- ur->add_undo_method(this, "replace_node", newnode, n, false, false);
- ur->add_undo_reference(n);
+ Node *new_node = Object::cast_to<Node>(c);
+ ERR_FAIL_COND(!new_node);
+ replace_node(n, new_node);
}
- ur->commit_action();
+ ur->commit_action(false);
} else if (current_option == TOOL_REPARENT_TO_NEW_NODE) {
List<Node *> selection = editor_selection->get_selected_node_list();
ERR_FAIL_COND(selection.size() <= 0);
@@ -2426,7 +2427,25 @@ void SceneTreeDock::_create() {
scene_tree->get_scene_tree()->grab_focus();
}
-void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
+void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) {
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, p_node);
+
+ ur->add_do_method(this, "replace_node", p_node, p_by_node, true, false);
+ ur->add_do_reference(p_by_node);
+
+ _replace_node(p_node, p_by_node, true, false);
+
+ ur->add_undo_method(this, "replace_node", p_by_node, p_node, false, false);
+ ur->add_undo_reference(p_node);
+
+ perform_node_replace(nullptr, p_node, p_by_node);
+
+ ur->commit_action(false);
+}
+
+void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
+ ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "_replace_node() can't be called on a node outside of tree. You might have called it twice.");
Node *n = p_node;
Node *newnode = p_by_node;
@@ -2499,6 +2518,84 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
}
}
+void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node) {
+ if (!p_base) {
+ p_base = edited_scene;
+ }
+
+ if (!p_base) {
+ return;
+ }
+
+ // Renaming node used in node properties.
+ List<PropertyInfo> properties;
+ p_base->get_property_list(&properties);
+
+ for (const PropertyInfo &E : properties) {
+ if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
+ continue;
+ }
+ String propertyname = E.name;
+ Variant old_variant = p_base->get(propertyname);
+ Variant updated_variant = old_variant;
+ String warn_message;
+
+ if (_check_node_recursive(updated_variant, p_node, p_by_node, E.hint_string, warn_message)) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_property(p_base, propertyname, updated_variant);
+ undo_redo->add_undo_property(p_base, propertyname, old_variant);
+ p_base->set(propertyname, updated_variant);
+ if (!warn_message.is_empty()) {
+ String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/.");
+ WARN_PRINT(warn_message + vformat(TTR("Removing the node from variable \"%s\" on node \"%s\"."), propertyname, node_path));
+ }
+ }
+ }
+
+ for (int i = 0; i < p_base->get_child_count(); i++) {
+ perform_node_replace(p_base->get_child(i), p_node, p_by_node);
+ }
+}
+
+bool SceneTreeDock::_check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message) {
+ switch (r_variant.get_type()) {
+ case Variant::OBJECT: {
+ if (p_node == r_variant) {
+ if (p_by_node->is_class(type_hint) || EditorNode::get_singleton()->is_object_of_custom_type(p_by_node, type_hint)) {
+ r_variant = p_by_node;
+ } else {
+ r_variant = memnew(Object);
+ r_warn_message = vformat(TTR("The node's new type is incompatible with an exported variable (expected %s, but type is %s)."), type_hint, p_by_node->get_class());
+ }
+ return true;
+ }
+ } break;
+
+ case Variant::ARRAY: {
+ Array a = r_variant;
+ bool updated = false;
+ for (int i = 0; i < a.size(); i++) {
+ Variant value = a[i];
+ if (_check_node_recursive(value, p_node, p_by_node, type_hint.get_slice(":", 1), r_warn_message)) {
+ if (!updated) {
+ a = a.duplicate(); // Need to duplicate for undo-redo to work.
+ updated = true;
+ }
+ a[i] = value;
+ }
+ }
+ if (updated) {
+ r_variant = a;
+ return true;
+ }
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
@@ -2865,6 +2962,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();
}
@@ -3238,7 +3336,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()) {
@@ -3268,6 +3366,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();
@@ -3277,7 +3379,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;
@@ -3635,7 +3737,7 @@ void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate);
ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor);
- ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node);
+ ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::_replace_node);
ADD_SIGNAL(MethodInfo("remote_tree_selected"));
ADD_SIGNAL(MethodInfo("add_node_used"));
@@ -3675,6 +3777,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
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/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"));
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 1db73df4c7..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,
@@ -281,6 +282,8 @@ class SceneTreeDock : public VBoxContainer {
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const;
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames) const;
+ bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message);
+ void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
private:
static SceneTreeDock *singleton;
@@ -306,6 +309,7 @@ public:
void set_selected(Node *p_node, bool p_emit_selected = false);
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
+ void perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node);
SceneTreeEditor *get_tree_editor() { return scene_tree; }
EditorData *get_editor_data() { return editor_data; }
@@ -315,7 +319,7 @@ public:
void show_tab_buttons();
void hide_tab_buttons();
- void replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
+ void replace_node(Node *p_node, Node *p_by_node);
void attach_script_to_selected(bool p_extend);
void open_script_dialog(Node *p_for_node, bool p_extend);
@@ -326,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 7e2741648d..4bb5e7bf13 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;
}
@@ -581,6 +586,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 +646,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();
@@ -857,6 +866,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get());
}
}
+ // If gpu is specified, both editor and debug instances started from editor will inherit.
+ if (I->get() == "--gpu-index") {
+ if (I->next()) {
+ forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get());
+ forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get());
+ forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get());
+ forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->next()->get());
+ }
+ }
#endif
if (adding_user_args) {
diff --git a/misc/error_suppressions/tsan.txt b/misc/error_suppressions/tsan.txt
new file mode 100644
index 0000000000..7c3d836f6c
--- /dev/null
+++ b/misc/error_suppressions/tsan.txt
@@ -0,0 +1,8 @@
+# See the below link for an explanation of this file's format
+# https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
+
+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 380c1d5193..823af03c8e 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -4,8 +4,21 @@ This file contains the expected output of --validate-extension-api when run agai
Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. They
should instead be used to justify these changes and describe how users should work around these changes.
+Add new entries at the end of the file.
+
========================================================================================================================
+Misc
+----
+Validate extension JSON: API was removed: classes/FramebufferCacheRD
+Validate extension JSON: API was removed: classes/UniformSetCacheRD
+
+FIXME: These aren't written when dumping the interface with a headless build
+(since there's no RD backend in use). We need to fix this inconsistency somehow.
+
+
+## Changes between 4.0-stable and 4.1-stable
+
GH-78517
--------
Validate extension JSON: Error: Field 'classes/DisplayServer/methods/global_menu_add_check_item/arguments/2': default_value changed value in new API, from "" to "Callable()".
@@ -36,10 +49,12 @@ Validate extension JSON: Error: Field 'classes/RenderingServer/methods/instances
The previous argument was a serialization bug, there's no actual API change.
+
GH-78237
--------
Validate extension JSON: Error: Field 'classes/WebRTCPeerConnectionExtension/methods/_create_data_channel/return_value': type changed value in new API, from "Object" to "WebRTCDataChannel".
+
GH-77757
--------
Validate extension JSON: Error: Field 'classes/Viewport/methods/gui_get_focus_owner': is_const changed value in new API, from false to true.
@@ -136,13 +151,6 @@ Navigation avoidance was reworked entirely.
Migration: TODO
-GH-?????
---------
-Validate extension JSON: API was removed: classes/FramebufferCacheRD
-Validate extension JSON: API was removed: classes/UniformSetCacheRD
-
-Unsure where these come from; when dumping the interface, these do actually still exist
-
GH-76176
--------
Validate extension JSON: Error: Hash changed for 'classes/EditorInterface/methods/get_base_control', from 31757941 to A5E188F5. This means that the function has changed and no compatibility function was provided.
@@ -260,7 +268,6 @@ Validate extension JSON: Error: Field 'classes/SyntaxHighlighter/methods/get_tex
Function was made `const`. No adjustments should be necessary.
-
GH-75250 & GH-76401
-------------------
Validate extension JSON: Error: Hash changed for 'classes/RichTextLabel/methods/push_paragraph', from 3DD1D1C2 to BFDC71FE. This means that the function has changed and no compatibility function was provided.
@@ -359,6 +366,9 @@ Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/creat
Added a optional parameters with default values. No adjustments should be necessary.
+
+## Changes between 4.1-stable and 4.2-stable
+
GH-79911
--------
Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9.
@@ -379,3 +389,57 @@ Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/a
Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided.
Raster barrier was split into vertex and fragment barriers for use in mobile renderer.
+
+
+GH-79308
+--------
+Validate extension JSON: API was removed: classes/GraphEdit/methods/get_scroll_ofs
+Validate extension JSON: API was removed: classes/GraphEdit/methods/get_snap
+Validate extension JSON: API was removed: classes/GraphEdit/methods/get_zoom_hbox
+Validate extension JSON: API was removed: classes/GraphEdit/methods/is_using_snap
+Validate extension JSON: API was removed: classes/GraphEdit/methods/set_scroll_ofs
+Validate extension JSON: API was removed: classes/GraphEdit/methods/set_snap
+Validate extension JSON: API was removed: classes/GraphEdit/methods/set_use_snap
+Validate extension JSON: API was removed: classes/GraphEdit/properties/snap_distance
+Validate extension JSON: API was removed: classes/GraphEdit/properties/use_snap
+Validate extension JSON: API was removed: classes/GraphNode/methods/is_comment
+Validate extension JSON: API was removed: classes/GraphNode/methods/set_comment
+Validate extension JSON: API was removed: classes/GraphNode/properties/comment
+Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': getter changed value in new API, from "get_scroll_ofs" to &"get_scroll_offset".
+Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': setter changed value in new API, from "set_scroll_ofs" to &"set_scroll_offset".
+
+Intentional compatibility breakage during refactoring of API marked as experimental.
+
+FIXME: Still a WIP, review this list once the work is completed, especially if compatibility
+code is added.
+
+
+GH-73196
+--------
+Validate extension JSON: Error: Field 'classes/CodeEdit/methods/get_text_for_symbol_lookup': is_const changed value in new API, from false to true.
+
+Function was made `const`. No adjustments should be necessary.
+
+
+GH-78328
+--------
+Validate extension JSON: Error: Field 'classes/TileMap/methods/get_used_rect': is_const changed value in new API, from false to true.
+
+Function was made `const`. No adjustments should be necessary.
+
+
+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.
diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh
index 74a8e1a8a3..40d94d4276 100755
--- a/misc/scripts/clang_format.sh
+++ b/misc/scripts/clang_format.sh
@@ -21,7 +21,7 @@ fi
# Fix copyright headers, but not all files get them.
for f in $files; do
- if [[ "$f" == *"inc" ]]; then
+ if [[ "$f" == *"inc" && "$f" != *"compat.inc" ]]; then
continue
elif [[ "$f" == *"glsl" ]]; then
continue
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.cpp b/modules/gdscript/gdscript.cpp
index 0bf9f72a2c..42b08f8a68 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2695,6 +2695,11 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str
Error err;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE);
+ if (err && scr.is_valid()) {
+ // If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.
+ ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_path, error_names[err]));
+ }
+
if (r_error) {
// Don't fail loading because of parsing error.
*r_error = scr.is_valid() ? OK : err;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 2a52db4158..3366fa2eec 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -245,6 +245,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// MEMBERS.
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {
// Try class members.
if (_is_class_member_property(codegen, identifier)) {
@@ -271,45 +273,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
}
}
- } break;
- case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
- case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: {
- // Try methods and signals (can be Callable and Signal).
- // Search upwards through parent classes:
- const GDScriptParser::ClassNode *base_class = codegen.class_node;
- while (base_class != nullptr) {
- if (base_class->has_member(identifier)) {
- const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
-
- gen->write_get_named(temp, identifier, self);
- return temp;
+ // Try methods and signals (can be Callable and Signal).
+ {
+ // Search upwards through parent classes:
+ const GDScriptParser::ClassNode *base_class = codegen.class_node;
+ while (base_class != nullptr) {
+ if (base_class->has_member(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
}
+ base_class = base_class->base_type.class_type;
}
- base_class = base_class->base_type.class_type;
- }
- // Try in native base.
- GDScript *scr = codegen.script;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+ // Try in native base.
+ GDScript *scr = codegen.script;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
}
- scr = scr->_base;
- }
- if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- gen->write_get_named(temp, identifier, self);
- return temp;
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
}
} break;
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
@@ -319,6 +320,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
while (owner) {
GDScript *scr = owner;
GDScriptNativeClass *nc = nullptr;
+
while (scr) {
if (scr->constants.has(identifier)) {
return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
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/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/runtime/features/first_class_callable_and_signal.gd b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd
new file mode 100644
index 0000000000..f17fb9823d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd
@@ -0,0 +1,14 @@
+# GH-80157
+
+extends Node
+
+func f():
+ pass
+
+signal s()
+
+func test():
+ print(f)
+ print(s)
+ print(get_child)
+ print(ready)
diff --git a/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out
new file mode 100644
index 0000000000..e5e9ff7043
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+Node::f
+Node::[signal]s
+Node::get_child
+Node::[signal]ready
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 5bc6081803..9b760a997a 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -1,11 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFDocument" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ Class for importing and exporting glTF files in and out of Godot.
</brief_description>
<description>
- Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene.
+ GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene.
+ All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself.
+ GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported.
</description>
<tutorials>
+ <link title="glTF 'What the duck?' guide">https://www.khronos.org/files/gltf20-reference-guide.pdf</link>
+ <link title="Khronos glTF specification">https://registry.khronos.org/glTF/</link>
</tutorials>
<methods>
<method name="append_from_buffer">
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 927ffb6aae..bae980fb56 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -17,7 +17,7 @@
<param index="1" name="gltf_node" type="GLTFNode" />
<param index="2" name="scene_node" type="Node" />
<description>
- Part of the export process. This method is run after [method _export_preflight] and before [method _export_node].
+ Part of the export process. This method is run after [method _export_preflight] and before [method _export_preserialize].
Runs when converting the data from a Godot scene node. This method can be used to process the Godot scene node data into a format that can be used by [method _export_node].
</description>
</method>
@@ -28,7 +28,7 @@
<param index="2" name="json" type="Dictionary" />
<param index="3" name="node" type="Node" />
<description>
- Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_post].
+ Part of the export process. This method is run after [method _export_preserialize] and before [method _export_post].
This method can be used to modify the final JSON of each node.
</description>
</method>
@@ -49,6 +49,14 @@
The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
</description>
</method>
+ <method name="_export_preserialize" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="state" type="GLTFState" />
+ <description>
+ Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_node].
+ This method can be used to alter the state before performing serialization. It runs every time when generating a buffer with [method GLTFDocument.generate_buffer] or writing to the file system with [method GLTFDocument.write_to_filesystem].
+ </description>
+ </method>
<method name="_generate_scene_node" qualifiers="virtual">
<return type="Node3D" />
<param index="0" name="state" type="GLTFState" />
@@ -59,6 +67,12 @@
Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node.
</description>
</method>
+ <method name="_get_image_file_extension" qualifiers="virtual">
+ <return type="String" />
+ <description>
+ Returns the file extension to use for saving image data into, for example, [code]".png"[/code]. If defined, when this extension is used to handle images, and the images are saved to a separate file, the image bytes will be copied to a file with this extension. If this is set, there should be a [ResourceImporter] class able to import the file. If not defined or empty, Godot will save the image into a PNG file.
+ </description>
+ </method>
<method name="_get_supported_extensions" qualifiers="virtual">
<return type="PackedStringArray" />
<description>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 44b0d124e7..ba1c531283 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -267,6 +267,7 @@
</methods>
<members>
<member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
+ The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
</member>
<member name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]">
</member>
@@ -275,6 +276,9 @@
</member>
<member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true">
</member>
+ <member name="filename" type="String" setter="set_filename" getter="get_filename" default="&quot;&quot;">
+ The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
+ </member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
</member>
<member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}">
diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp
index 2804a8b0a2..11718ba78a 100644
--- a/modules/gltf/extensions/gltf_document_extension.cpp
+++ b/modules/gltf/extensions/gltf_document_extension.cpp
@@ -36,6 +36,7 @@ void GLTFDocumentExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_supported_extensions);
GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions");
GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image");
+ GDVIRTUAL_BIND(_get_image_file_extension);
GDVIRTUAL_BIND(_parse_texture_json, "state", "texture_json", "ret_gltf_texture");
GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent");
GDVIRTUAL_BIND(_import_post_parse, "state");
@@ -44,6 +45,7 @@ void GLTFDocumentExtension::_bind_methods() {
// Export process.
GDVIRTUAL_BIND(_export_preflight, "state", "root");
GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node");
+ GDVIRTUAL_BIND(_export_preserialize, "state");
GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node");
GDVIRTUAL_BIND(_export_post, "state");
}
@@ -78,6 +80,12 @@ Error GLTFDocumentExtension::parse_image_data(Ref<GLTFState> p_state, const Pack
return err;
}
+String GLTFDocumentExtension::get_image_file_extension() {
+ String ret;
+ GDVIRTUAL_CALL(_get_image_file_extension, ret);
+ return ret;
+}
+
Error GLTFDocumentExtension::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) {
ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
ERR_FAIL_NULL_V(r_gltf_texture, ERR_INVALID_PARAMETER);
@@ -134,6 +142,13 @@ void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFN
GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node);
}
+Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ Error err = OK;
+ GDVIRTUAL_CALL(_export_preserialize, p_state, err);
+ return err;
+}
+
Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h
index d922588a29..0a631bb6c5 100644
--- a/modules/gltf/extensions/gltf_document_extension.h
+++ b/modules/gltf/extensions/gltf_document_extension.h
@@ -45,6 +45,7 @@ public:
virtual Vector<String> get_supported_extensions();
virtual Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions);
virtual Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image);
+ virtual String get_image_file_extension();
virtual Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture);
virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
virtual Error import_post_parse(Ref<GLTFState> p_state);
@@ -53,6 +54,7 @@ public:
// Export process.
virtual Error export_preflight(Ref<GLTFState> p_state, Node *p_root);
virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node);
+ virtual Error export_preserialize(Ref<GLTFState> p_state);
virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
virtual Error export_post(Ref<GLTFState> p_state);
@@ -61,6 +63,7 @@ public:
GDVIRTUAL0R(Vector<String>, _get_supported_extensions);
GDVIRTUAL3R(Error, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary);
GDVIRTUAL4R(Error, _parse_image_data, Ref<GLTFState>, PackedByteArray, String, Ref<Image>);
+ GDVIRTUAL0R(String, _get_image_file_extension);
GDVIRTUAL3R(Error, _parse_texture_json, Ref<GLTFState>, Dictionary, Ref<GLTFTexture>);
GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>);
@@ -69,6 +72,7 @@ public:
// Export process.
GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *);
GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
+ GDVIRTUAL1R(Error, _export_preserialize, Ref<GLTFState>);
GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>);
};
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
index ded4970968..73c869be3b 100644
--- a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
@@ -30,8 +30,6 @@
#include "gltf_document_extension_texture_webp.h"
-#include "scene/3d/area_3d.h"
-
// Import process.
Error GLTFDocumentExtensionTextureWebP::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) {
if (!p_extensions.has("EXT_texture_webp")) {
@@ -53,6 +51,10 @@ Error GLTFDocumentExtensionTextureWebP::parse_image_data(Ref<GLTFState> p_state,
return OK;
}
+String GLTFDocumentExtensionTextureWebP::get_image_file_extension() {
+ return ".webp";
+}
+
Error GLTFDocumentExtensionTextureWebP::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) {
if (!p_texture_json.has("extensions")) {
return OK;
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.h b/modules/gltf/extensions/gltf_document_extension_texture_webp.h
index 9abf09a41f..d2654aae8c 100644
--- a/modules/gltf/extensions/gltf_document_extension_texture_webp.h
+++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.h
@@ -41,6 +41,7 @@ public:
Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override;
Vector<String> get_supported_extensions() override;
Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override;
+ String get_image_file_extension() override;
Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override;
};
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 4381a0e00d..9e7611bc5e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -110,11 +110,17 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
return importer_mesh;
}
-Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) {
+Error GLTFDocument::_serialize(Ref<GLTFState> p_state) {
if (!p_state->buffers.size()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ Error err = ext->export_preserialize(p_state);
+ ERR_CONTINUE(err != OK);
+ }
+
/* STEP CONVERT MESH INSTANCES */
_convert_mesh_instances(p_state);
@@ -161,7 +167,7 @@ Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) {
}
/* STEP SERIALIZE IMAGES */
- err = _serialize_images(p_state, p_path);
+ err = _serialize_images(p_state);
if (err != OK) {
return Error::FAILED;
}
@@ -2995,17 +3001,32 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
return OK;
}
-Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_path) {
+Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
Array images;
for (int i = 0; i < p_state->images.size(); i++) {
- Dictionary d;
+ Dictionary image_dict;
ERR_CONTINUE(p_state->images[i].is_null());
Ref<Image> image = p_state->images[i]->get_image();
ERR_CONTINUE(image.is_null());
- if (p_path.to_lower().ends_with("glb") || p_path.is_empty()) {
+ if (p_state->filename.to_lower().ends_with("gltf")) {
+ String img_name = p_state->images[i]->get_name();
+ if (img_name.is_empty()) {
+ img_name = itos(i);
+ }
+ img_name = _gen_unique_name(p_state, img_name);
+ img_name = img_name.pad_zeros(3) + ".png";
+ String relative_texture_dir = "textures";
+ String full_texture_dir = p_state->base_path.path_join(relative_texture_dir);
+ Ref<DirAccess> da = DirAccess::open(p_state->base_path);
+ if (!da->dir_exists(full_texture_dir)) {
+ da->make_dir(full_texture_dir);
+ }
+ image->save_png(full_texture_dir.path_join(img_name));
+ image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode();
+ } else {
GLTFBufferViewIndex bvi;
Ref<GLTFBufferView> bv;
@@ -3031,27 +3052,10 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa
p_state->buffer_views.push_back(bv);
bvi = p_state->buffer_views.size() - 1;
- d["bufferView"] = bvi;
- d["mimeType"] = "image/png";
- } else {
- ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
- String img_name = p_state->images[i]->get_name();
- if (img_name.is_empty()) {
- img_name = itos(i);
- }
- img_name = _gen_unique_name(p_state, img_name);
- img_name = img_name.pad_zeros(3) + ".png";
- String texture_dir = "textures";
- String path = p_path.get_base_dir();
- String new_texture_dir = path + "/" + texture_dir;
- Ref<DirAccess> da = DirAccess::open(path);
- if (!da->dir_exists(new_texture_dir)) {
- da->make_dir(new_texture_dir);
- }
- image->save_png(new_texture_dir.path_join(img_name));
- d["uri"] = texture_dir.path_join(img_name).uri_encode();
+ image_dict["bufferView"] = bvi;
+ image_dict["mimeType"] = "image/png";
}
- images.push_back(d);
+ images.push_back(image_dict);
}
print_verbose("Total images: " + itos(p_state->images.size()));
@@ -3064,7 +3068,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa
return OK;
}
-Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index) {
+Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension) {
Ref<Image> r_image;
r_image.instantiate();
// Check if any GLTFDocumentExtensions want to import this data as an image.
@@ -3073,6 +3077,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image);
ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing.");
if (!r_image->is_empty()) {
+ r_file_extension = ext->get_image_file_extension();
return r_image;
}
}
@@ -3080,8 +3085,10 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
// First we honor the mime types if they were defined.
if (p_mime_type == "image/png") { // Load buffer as PNG.
r_image->load_png_from_buffer(p_bytes);
+ r_file_extension = ".png";
} else if (p_mime_type == "image/jpeg") { // Loader buffer as JPEG.
r_image->load_jpg_from_buffer(p_bytes);
+ r_file_extension = ".jpg";
}
// If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly.
// This covers URIs with base64-encoded data with application/* type but
@@ -3102,7 +3109,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
return r_image;
}
-void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image) {
+void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image) {
GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image);
if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) {
p_state->images.push_back(Ref<Texture2D>());
@@ -3119,11 +3126,11 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String
p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>());
} else {
- Error err = OK;
bool must_import = true;
Vector<uint8_t> img_data = p_image->get_data();
Dictionary generator_parameters;
- String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name() + ".png";
+ String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name();
+ file_path += p_file_extension.is_empty() ? ".png" : p_file_extension;
if (FileAccess::exists(file_path + ".import")) {
Ref<ConfigFile> config;
config.instantiate();
@@ -3144,8 +3151,18 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String
}
}
if (must_import) {
- err = p_image->save_png(file_path);
- ERR_FAIL_COND(err != OK);
+ Error err = OK;
+ if (p_file_extension.is_empty()) {
+ // If a file extension was not specified, save the image data to a PNG file.
+ err = p_image->save_png(file_path);
+ ERR_FAIL_COND(err != OK);
+ } else {
+ // If a file extension was specified, save the original bytes to a file with that extension.
+ Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND(err != OK);
+ file->store_buffer(p_bytes);
+ file->close();
+ }
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
HashMap<StringName, Variant> custom_options;
custom_options[SNAME("mipmaps/generate")] = true;
@@ -3295,9 +3312,10 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
continue;
}
// Parse the image data from bytes into an Image resource and save if needed.
- Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i);
+ String file_extension;
+ Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension);
img->set_name(image_name);
- _parse_image_save_image(p_state, mime_type, i, img);
+ _parse_image_save_image(p_state, data, file_extension, i, img);
}
print_verbose("glTF: Total images: " + itos(p_state->images.size()));
@@ -3312,16 +3330,16 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) {
Array textures;
for (int32_t i = 0; i < p_state->textures.size(); i++) {
- Dictionary d;
- Ref<GLTFTexture> t = p_state->textures[i];
- ERR_CONTINUE(t->get_src_image() == -1);
- d["source"] = t->get_src_image();
+ Dictionary texture_dict;
+ Ref<GLTFTexture> gltf_texture = p_state->textures[i];
+ ERR_CONTINUE(gltf_texture->get_src_image() == -1);
+ texture_dict["source"] = gltf_texture->get_src_image();
- GLTFTextureSamplerIndex sampler_index = t->get_sampler();
+ GLTFTextureSamplerIndex sampler_index = gltf_texture->get_sampler();
if (sampler_index != -1) {
- d["sampler"] = sampler_index;
+ texture_dict["sampler"] = sampler_index;
}
- textures.push_back(d);
+ textures.push_back(texture_dict);
}
p_state->json["textures"] = textures;
@@ -3335,28 +3353,28 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) {
const Array &textures = p_state->json["textures"];
for (GLTFTextureIndex i = 0; i < textures.size(); i++) {
- const Dictionary &dict = textures[i];
- Ref<GLTFTexture> texture;
- texture.instantiate();
+ const Dictionary &texture_dict = textures[i];
+ Ref<GLTFTexture> gltf_texture;
+ gltf_texture.instantiate();
// Check if any GLTFDocumentExtensions want to handle this texture JSON.
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- Error err = ext->parse_texture_json(p_state, dict, texture);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(dict)) + " in file " + p_state->filename + ". Continuing.");
- if (texture->get_src_image() != -1) {
+ Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture);
+ ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing.");
+ if (gltf_texture->get_src_image() != -1) {
break;
}
}
- if (texture->get_src_image() == -1) {
+ if (gltf_texture->get_src_image() == -1) {
// No extensions handled it, so use the base GLTF source.
// This may be the fallback, or the only option anyway.
- ERR_FAIL_COND_V(!dict.has("source"), ERR_PARSE_ERROR);
- texture->set_src_image(dict["source"]);
+ ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR);
+ gltf_texture->set_src_image(texture_dict["source"]);
}
- if (texture->get_sampler() == -1 && dict.has("sampler")) {
- texture->set_sampler(dict["sampler"]);
+ if (gltf_texture->get_sampler() == -1 && texture_dict.has("sampler")) {
+ gltf_texture->set_sampler(texture_dict["sampler"]);
}
- p_state->textures.push_back(texture);
+ p_state->textures.push_back(gltf_texture);
}
return OK;
@@ -3382,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) {
@@ -7221,7 +7240,10 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Erro
PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) {
ERR_FAIL_NULL_V(p_state, PackedByteArray());
- Error err = _serialize(p_state, "");
+ // For buffers, set the state filename to an empty string, but
+ // don't touch the base path, in case the user set it manually.
+ p_state->filename = "";
+ Error err = _serialize(p_state);
ERR_FAIL_COND_V(err != OK, PackedByteArray());
PackedByteArray bytes = _serialize_glb_buffer(p_state, &err);
return bytes;
@@ -7229,7 +7251,9 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) {
Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) {
ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
- Error err = _serialize(p_state, p_path);
+ p_state->base_path = p_path.get_base_dir();
+ p_state->filename = p_path.get_file();
+ Error err = _serialize(p_state);
if (err != OK) {
return err;
}
@@ -7280,44 +7304,44 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
return root;
}
-Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags) {
- ERR_FAIL_COND_V(r_state.is_null(), FAILED);
- r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
- r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
- if (!r_state->buffers.size()) {
- r_state->buffers.push_back(Vector<uint8_t>());
+Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags) {
+ ERR_FAIL_COND_V(p_state.is_null(), FAILED);
+ p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
+ p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ if (!p_state->buffers.size()) {
+ p_state->buffers.push_back(Vector<uint8_t>());
}
// Perform export preflight for document extensions. Only extensions that
// return OK will be used for the rest of the export steps.
document_extensions.clear();
for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
ERR_CONTINUE(ext.is_null());
- Error err = ext->export_preflight(r_state, p_node);
+ Error err = ext->export_preflight(p_state, p_node);
if (err == OK) {
document_extensions.push_back(ext);
}
}
// Add the root node(s) and their descendants to the state.
- _convert_scene_node(r_state, p_node, -1, -1);
+ _convert_scene_node(p_state, p_node, -1, -1);
return OK;
}
-Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags) {
- ERR_FAIL_COND_V(r_state.is_null(), FAILED);
+Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags) {
+ ERR_FAIL_COND_V(p_state.is_null(), FAILED);
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
Error err = FAILED;
- r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
- r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
+ p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
- r_state->base_path = p_base_path.get_base_dir();
- err = _parse(r_state, r_state->base_path, file_access);
+ p_state->base_path = p_base_path.get_base_dir();
+ err = _parse(p_state, p_state->base_path, file_access);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->import_post_parse(r_state);
+ err = ext->import_post_parse(p_state);
ERR_FAIL_COND_V(err != OK, err);
}
return OK;
@@ -7436,14 +7460,14 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
return OK;
}
-Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, String p_base_path) {
+Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags, String p_base_path) {
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
- if (r_state == Ref<GLTFState>()) {
- r_state.instantiate();
+ if (p_state == Ref<GLTFState>()) {
+ p_state.instantiate();
}
- r_state->filename = p_path.get_file().get_basename();
- r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
- r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ p_state->filename = p_path.get_file().get_basename();
+ p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
+ p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN);
@@ -7452,12 +7476,12 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
if (base_path.is_empty()) {
base_path = p_path.get_base_dir();
}
- r_state->base_path = base_path;
- err = _parse(r_state, base_path, file);
+ p_state->base_path = base_path;
+ err = _parse(p_state, base_path, file);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->import_post_parse(r_state);
+ err = ext->import_post_parse(p_state);
ERR_FAIL_COND_V(err != OK, err);
}
return OK;
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index dfde53c9fb..f2e36a0457 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -149,10 +149,10 @@ private:
Error _parse_meshes(Ref<GLTFState> p_state);
Error _serialize_textures(Ref<GLTFState> p_state);
Error _serialize_texture_samplers(Ref<GLTFState> p_state);
- Error _serialize_images(Ref<GLTFState> p_state, const String &p_path);
+ Error _serialize_images(Ref<GLTFState> p_state);
Error _serialize_lights(Ref<GLTFState> p_state);
- Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index);
- void _parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image);
+ Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension);
+ void _parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image);
Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path);
Error _parse_textures(Ref<GLTFState> p_state);
Error _parse_texture_samplers(Ref<GLTFState> p_state);
@@ -293,9 +293,9 @@ private:
static float get_max_component(const Color &p_color);
public:
- Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, String p_base_path = String());
- Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0);
- Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0);
+ Error append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags = 0, String p_base_path = String());
+ Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags = 0);
+ Error append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags = 0);
public:
Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false, bool p_remove_immutable_tracks = true);
@@ -366,7 +366,7 @@ public:
GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> p_state,
MeshInstance3D *p_mesh_instance);
void _convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, String p_animation_track_name);
- Error _serialize(Ref<GLTFState> p_state, const String &p_path);
+ Error _serialize(Ref<GLTFState> p_state);
Error _parse(Ref<GLTFState> p_state, String p_path, Ref<FileAccess> p_file);
};
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index 87d15066f0..c0ec004fd6 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -64,6 +64,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name);
ClassDB::bind_method(D_METHOD("get_base_path"), &GLTFState::get_base_path);
ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path);
+ ClassDB::bind_method(D_METHOD("get_filename"), &GLTFState::get_filename);
+ ClassDB::bind_method(D_METHOD("set_filename", "filename"), &GLTFState::set_filename);
ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes);
ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
@@ -109,6 +111,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material>
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename"), "set_filename", "get_filename"); // String
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "texture_samplers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_texture_samplers", "get_texture_samplers"); //Vector<Ref<GLTFTextureSampler>>
@@ -164,11 +167,11 @@ void GLTFState::set_minor_version(int p_minor_version) {
minor_version = p_minor_version;
}
-String GLTFState::get_copyright() {
+String GLTFState::get_copyright() const {
return copyright;
}
-void GLTFState::set_copyright(String p_copyright) {
+void GLTFState::set_copyright(const String &p_copyright) {
copyright = p_copyright;
}
@@ -381,6 +384,14 @@ void GLTFState::set_base_path(String p_base_path) {
base_path = p_base_path;
}
+String GLTFState::get_filename() const {
+ return filename;
+}
+
+void GLTFState::set_filename(const String &p_filename) {
+ filename = p_filename;
+}
+
Variant GLTFState::get_additional_data(const StringName &p_extension_name) {
return additional_data[p_extension_name];
}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index d122049c0b..91af8f91a4 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -47,8 +47,8 @@ class GLTFState : public Resource {
GDCLASS(GLTFState, Resource);
friend class GLTFDocument;
- String filename;
String base_path;
+ String filename;
Dictionary json;
int major_version = 0;
int minor_version = 0;
@@ -126,8 +126,8 @@ public:
int get_minor_version();
void set_minor_version(int p_minor_version);
- String get_copyright();
- void set_copyright(String p_copyright);
+ String get_copyright() const;
+ void set_copyright(const String &p_copyright);
Vector<uint8_t> get_glb_data();
void set_glb_data(Vector<uint8_t> p_glb_data);
@@ -171,6 +171,9 @@ public:
String get_base_path();
void set_base_path(String p_base_path);
+ String get_filename() const;
+ void set_filename(const String &p_filename);
+
PackedInt32Array get_root_nodes();
void set_root_nodes(PackedInt32Array p_root_nodes);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 1ed495943f..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() {
@@ -1197,8 +1197,6 @@ void CSharpLanguage::_editor_init_callback() {
// Add plugin to EditorNode and enable it
EditorNode::add_editor_plugin(godotsharp_editor);
- ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B);
- ED_SHORTCUT_OVERRIDE("mono/build_solution", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::B);
godotsharp_editor->enable_plugin();
get_singleton()->godotsharp_editor = godotsharp_editor;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 1bb1b3227e..cc11132a55 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -114,7 +114,7 @@ namespace GodotTools.Build
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(toolBarHBox);
- _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
+ _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("BuildCSharp", "EditorIcons") };
toolBarHBox.AddChild(_buildMenuBtn);
var buildMenu = _buildMenuBtn.GetPopup();
@@ -184,7 +184,7 @@ namespace GodotTools.Build
if (what == NotificationThemeChanged)
{
if (_buildMenuBtn != null)
- _buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons");
+ _buildMenuBtn.Icon = GetThemeIcon("BuildCSharp", "EditorIcons");
if (_errorsBtn != null)
_errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons");
if (_warningsBtn != null)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 4d61372ab0..b98df190ca 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -170,7 +170,7 @@ namespace GodotTools.Export
string ridOS = DetermineRuntimeIdentifierOS(platform);
string ridArch = DetermineRuntimeIdentifierArch(arch);
string runtimeIdentifier = $"{ridOS}-{ridArch}";
- string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{arch}";
+ string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{platform}_{arch}";
if (platform == OS.Platforms.MacOS)
{
projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 622a155d37..618d255938 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -280,7 +280,7 @@ namespace GodotTools
case ExternalEditorId.Rider:
{
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
- RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line);
+ RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line + 1, col);
return Error.Ok;
}
case ExternalEditorId.MonoDevelop:
@@ -497,18 +497,20 @@ namespace GodotTools
AddToolSubmenuItem("C#", _menuPopup);
- var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution");
-
_toolBarBuildButton = new Button
{
- Text = "Build",
- TooltipText = "Build Solution".TTR(),
+ Flat = true,
+ Icon = editorBaseControl.GetThemeIcon("BuildCSharp", "EditorIcons"),
FocusMode = Control.FocusModeEnum.None,
- Shortcut = buildSolutionShortcut,
- ShortcutInTooltip = true
+ Shortcut = EditorDefShortcut("mono/build_solution", "Build Project".TTR(), (Key)KeyModifierMask.MaskAlt | Key.B),
+ ShortcutInTooltip = true,
};
+ EditorShortcutOverride("mono/build_solution", "macos", (Key)KeyModifierMask.MaskMeta | (Key)KeyModifierMask.MaskCtrl | Key.B);
+
_toolBarBuildButton.Pressed += BuildProjectPressed;
- AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
+ Internal.EditorPlugin_AddControlToEditorRunBar(_toolBarBuildButton);
+ // Move Build button so it appears to the left of the Play button.
+ _toolBarBuildButton.GetParent().MoveChild(_toolBarBuildButton, 0);
if (File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
@@ -538,7 +540,7 @@ namespace GodotTools
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
- $",JetBrains Rider:{(int)ExternalEditorId.Rider}" +
+ $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
else if (OS.IsMacOS)
@@ -546,14 +548,14 @@ namespace GodotTools
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
- $",JetBrains Rider:{(int)ExternalEditorId.Rider}" +
+ $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
else if (OS.IsUnixLike)
{
settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
- $",JetBrains Rider:{(int)ExternalEditorId.Rider}" +
+ $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 4a0b7f9bed..56ae37b4dd 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -28,7 +28,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
- <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.1" />
+ <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.4" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<Reference Include="GodotSharp">
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs
index 7e08d8c01d..61c1581281 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs
@@ -48,4 +48,9 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment
else
GD.PushError(message, e);
}
+
+ public void Verbose(string message, Exception e = null)
+ {
+ // do nothing, since IDK how to write only to the log, without spamming the output
+ }
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 5c09f1f83a..0d77b8999a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using Godot;
@@ -11,10 +10,13 @@ namespace GodotTools.Ides.Rider
public static class RiderPathManager
{
private static readonly RiderPathLocator RiderPathLocator;
+ private static readonly RiderFileOpener RiderFileOpener;
static RiderPathManager()
{
- RiderPathLocator = new RiderPathLocator(new RiderLocatorEnvironment());
+ var riderLocatorEnvironment = new RiderLocatorEnvironment();
+ RiderPathLocator = new RiderPathLocator(riderLocatorEnvironment);
+ RiderFileOpener = new RiderFileOpener(riderLocatorEnvironment);
}
public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional";
@@ -46,7 +48,7 @@ namespace GodotTools.Ides.Rider
}
var riderPath = (string)editorSettings.GetSetting(EditorPathSettingName);
- if (IsRiderAndExists(riderPath))
+ if (File.Exists(riderPath))
{
Globals.EditorDef(EditorPathSettingName, riderPath);
return;
@@ -63,12 +65,6 @@ namespace GodotTools.Ides.Rider
}
}
- public static bool IsExternalEditorSetToRider(EditorSettings editorSettings)
- {
- return editorSettings.HasSetting(EditorPathSettingName) &&
- IsRider((string)editorSettings.GetSetting(EditorPathSettingName));
- }
-
public static bool IsRider(string path)
{
if (string.IsNullOrEmpty(path))
@@ -84,49 +80,29 @@ namespace GodotTools.Ides.Rider
private static string CheckAndUpdatePath(string riderPath)
{
- if (IsRiderAndExists(riderPath))
+ if (File.Exists(riderPath))
{
return riderPath;
}
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var paths = RiderPathLocator.GetAllRiderPaths();
-
- if (!paths.Any())
+ var allInfos = RiderPathLocator.GetAllRiderPaths();
+ if (allInfos.Length == 0)
return null;
-
- string newPath = paths.Last().Path;
+ var riderInfos = allInfos.Where(info => IsRider(info.Path)).ToArray();
+ string newPath = riderInfos.Length > 0
+ ? riderInfos[riderInfos.Length - 1].Path
+ : allInfos[allInfos.Length - 1].Path;
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
editorSettings.SetSetting(EditorPathSettingName, newPath);
Globals.EditorDef(EditorPathSettingName, newPath);
return newPath;
}
- private static bool IsRiderAndExists(string riderPath)
- {
- return !string.IsNullOrEmpty(riderPath) && IsRider(riderPath) && new FileInfo(riderPath).Exists;
- }
-
- public static void OpenFile(string slnPath, string scriptPath, int line)
+ public static void OpenFile(string slnPath, string scriptPath, int line, int column)
{
string pathFromSettings = GetRiderPathFromSettings();
string path = CheckAndUpdatePath(pathFromSettings);
-
- var args = new List<string>();
- args.Add(slnPath);
- if (line >= 0)
- {
- args.Add("--line");
- args.Add((line + 1).ToString()); // https://github.com/JetBrains/godot-support/issues/61
- }
- args.Add(scriptPath);
- try
- {
- Utils.OS.RunProcess(path, args);
- }
- catch (Exception e)
- {
- GD.PushError($"Error when trying to run code editor: JetBrains Rider. Exception message: '{e.Message}'");
- }
+ RiderFileOpener.OpenFile(path, slnPath, scriptPath, line, column);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 45ae7eb86b..a6718e8fd5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -29,11 +29,26 @@ namespace GodotTools.Internals
return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
- public static Variant EditorShortcut(string setting)
+ public static Shortcut EditorDefShortcut(string setting, string name, Key keycode = Key.None, bool physical = false)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
- Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
- return Variant.CreateTakingOwnershipOfDisposableValue(result);
+ using godot_string nameIn = Marshaling.ConvertStringToNative(name);
+ Internal.godot_icall_Globals_EditorDefShortcut(settingIn, nameIn, keycode, physical.ToGodotBool(), out godot_variant result);
+ return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result);
+ }
+
+ public static Shortcut EditorGetShortcut(string setting)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ Internal.godot_icall_Globals_EditorGetShortcut(settingIn, out godot_variant result);
+ return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result);
+ }
+
+ public static void EditorShortcutOverride(string setting, string feature, Key keycode = Key.None, bool physical = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_string featureIn = Marshaling.ConvertStringToNative(feature);
+ Internal.godot_icall_Globals_EditorShortcutOverride(settingIn, featureIn, keycode, physical.ToGodotBool());
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 3ea11750b7..90c443ebb8 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -54,6 +54,9 @@ namespace GodotTools.Internals
public static void EditorRunStop() => godot_icall_Internal_EditorRunStop();
+ public static void EditorPlugin_AddControlToEditorRunBar(Control control) =>
+ godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(control.NativeInstance);
+
public static void ScriptEditorDebugger_ReloadScripts() =>
godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
@@ -137,6 +140,8 @@ namespace GodotTools.Internals
private static partial void godot_icall_Internal_EditorRunStop();
+ private static partial void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(IntPtr p_control);
+
private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
@@ -151,7 +156,13 @@ namespace GodotTools.Internals
bool restartIfChanged, out godot_variant result);
public static partial void
- godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);
+ godot_icall_Globals_EditorDefShortcut(in godot_string setting, in godot_string name, Key keycode, godot_bool physical, out godot_variant result);
+
+ public static partial void
+ godot_icall_Globals_EditorGetShortcut(in godot_string setting, out godot_variant result);
+
+ public static partial void
+ godot_icall_Globals_EditorShortcutOverride(in godot_string setting, in godot_string feature, Key keycode, godot_bool physical);
public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index bed93cd69e..1554b89c33 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
@@ -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) {
@@ -1403,8 +1409,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;
@@ -1504,37 +1515,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.
- output.append(MEMBER_BEGIN "private static GodotObject singleton;\n");
+ if (is_derived_type && !itype.is_singleton) {
+ output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\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";
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(itype.name);
+ output.append("\";\n");
- output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
- output.append(itype.name);
- output.append("\";\n");
- } else {
+ if (itype.is_singleton || itype.is_compat_singleton) {
+ // Add the Singleton static property.
+
+ 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 +1767,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 +1912,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 +2001,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 +2022,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 +2040,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 +2298,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,
@@ -2702,6 +2725,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 +2752,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 +2969,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);";
@@ -3346,6 +3384,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 +3872,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 +3899,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));";
@@ -3962,6 +4013,10 @@ void BindingsGenerator::_initialize_blacklisted_methods() {
blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
}
+void BindingsGenerator::_initialize_compat_singletons() {
+ // No compat singletons yet.
+}
+
void BindingsGenerator::_log(const char *p_format, ...) {
if (log_print_enabled) {
va_list list;
@@ -3981,6 +4036,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..d4c7a59e74 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -227,9 +227,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 +623,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");
@@ -756,6 +767,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/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index ba6b91b704..fc99f3ceda 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -168,6 +168,10 @@ void godot_icall_Internal_EditorRunStop() {
EditorRunBar::get_singleton()->stop_playing();
}
+void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(Control *p_control) {
+ EditorRunBar::get_singleton()->get_buttons_container()->add_child(p_control);
+}
+
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
if (ed) {
@@ -199,12 +203,25 @@ void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_va
memnew_placement(r_result, Variant(result));
}
-void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) {
+void godot_icall_Globals_EditorDefShortcut(const godot_string *p_setting, const godot_string *p_name, Key p_keycode, bool p_physical, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ String name = *reinterpret_cast<const String *>(p_name);
+ Ref<Shortcut> result = ED_SHORTCUT(setting, name, p_keycode, p_physical);
+ memnew_placement(r_result, Variant(result));
+}
+
+void godot_icall_Globals_EditorGetShortcut(const godot_string *p_setting, Ref<Shortcut> *r_result) {
String setting = *reinterpret_cast<const String *>(p_setting);
Ref<Shortcut> result = ED_GET_SHORTCUT(setting);
memnew_placement(r_result, Variant(result));
}
+void godot_icall_Globals_EditorShortcutOverride(const godot_string *p_setting, const godot_string *p_feature, Key p_keycode, bool p_physical) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ String feature = *reinterpret_cast<const String *>(p_feature);
+ ED_SHORTCUT_OVERRIDE(setting, feature, p_keycode, p_physical);
+}
+
void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) {
String text = *reinterpret_cast<const String *>(p_text);
memnew_placement(r_dest, String(TTR(text)));
@@ -251,12 +268,15 @@ static const void *unmanaged_callbacks[]{
(void *)godot_icall_Internal_EditorNodeShowScriptScreen,
(void *)godot_icall_Internal_EditorRunPlay,
(void *)godot_icall_Internal_EditorRunStop,
+ (void *)godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar,
(void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts,
(void *)godot_icall_Internal_CodeCompletionRequest,
(void *)godot_icall_Globals_EditorScale,
(void *)godot_icall_Globals_GlobalDef,
(void *)godot_icall_Globals_EditorDef,
- (void *)godot_icall_Globals_EditorShortcut,
+ (void *)godot_icall_Globals_EditorDefShortcut,
+ (void *)godot_icall_Globals_EditorGetShortcut,
+ (void *)godot_icall_Globals_EditorShortcutOverride,
(void *)godot_icall_Globals_TTR,
(void *)godot_icall_Utils_OS_GetPlatformName,
(void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess,
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/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
index c4161d2ded..57b292793a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
@@ -14,14 +14,46 @@ namespace Godot
{
private static void AppendTypeName(this StringBuilder sb, Type type)
{
- if (type.IsPrimitive)
- sb.Append(type.Name);
- else if (type == typeof(void))
+ // Use the C# type keyword for built-in types.
+ // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types
+ if (type == typeof(void))
sb.Append("void");
+ else if (type == typeof(bool))
+ sb.Append("bool");
+ else if (type == typeof(byte))
+ sb.Append("byte");
+ else if (type == typeof(sbyte))
+ sb.Append("sbyte");
+ else if (type == typeof(char))
+ sb.Append("char");
+ else if (type == typeof(decimal))
+ sb.Append("decimal");
+ else if (type == typeof(double))
+ sb.Append("double");
+ else if (type == typeof(float))
+ sb.Append("float");
+ else if (type == typeof(int))
+ sb.Append("int");
+ else if (type == typeof(uint))
+ sb.Append("uint");
+ else if (type == typeof(nint))
+ sb.Append("nint");
+ else if (type == typeof(nuint))
+ sb.Append("nuint");
+ else if (type == typeof(long))
+ sb.Append("long");
+ else if (type == typeof(ulong))
+ sb.Append("ulong");
+ else if (type == typeof(short))
+ sb.Append("short");
+ else if (type == typeof(ushort))
+ sb.Append("ushort");
+ else if (type == typeof(object))
+ sb.Append("object");
+ else if (type == typeof(string))
+ sb.Append("string");
else
sb.Append(type);
-
- sb.Append(' ');
}
internal static void InstallTraceListener()
@@ -70,13 +102,26 @@ namespace Godot
}
}
+ internal static unsafe StackFrame? GetCurrentStackFrame(int skipFrames = 0)
+ {
+ // We skip 2 frames:
+ // The first skipped frame is the current method.
+ // The second skipped frame is a method in NativeInterop.NativeFuncs.
+ var stackTrace = new StackTrace(skipFrames: 2 + skipFrames, fNeedFileInfo: true);
+ return stackTrace.GetFrame(0);
+ }
+
[UnmanagedCallersOnly]
internal static unsafe void GetCurrentStackInfo(void* destVector)
{
try
{
var vector = (godot_stack_info_vector*)destVector;
- var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true);
+
+ // We skip 2 frames:
+ // The first skipped frame is the current method.
+ // The second skipped frame is a method in NativeInterop.NativeFuncs.
+ var stackTrace = new StackTrace(skipFrames: 2, fNeedFileInfo: true);
int frameCount = stackTrace.FrameCount;
if (frameCount == 0)
@@ -87,6 +132,14 @@ namespace Godot
int i = 0;
foreach (StackFrame frame in stackTrace.GetFrames())
{
+ var method = frame.GetMethod();
+
+ if (method is MethodInfo methodInfo && methodInfo.IsDefined(typeof(StackTraceHiddenAttribute)))
+ {
+ // Skip methods marked hidden from the stack trace.
+ continue;
+ }
+
string? fileName = frame.GetFileName();
int fileLineNumber = frame.GetFileLineNumber();
@@ -102,6 +155,9 @@ namespace Godot
i++;
}
+
+ // Resize the vector again in case we skipped some frames.
+ vector->Resize(i);
}
catch (Exception e)
{
@@ -122,7 +178,10 @@ namespace Godot
var sb = new StringBuilder();
if (methodBase is MethodInfo methodInfo)
+ {
sb.AppendTypeName(methodInfo.ReturnType);
+ sb.Append(' ');
+ }
sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>");
sb.Append('.');
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 9425b7424c..33ebb8171e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Text;
using Godot.NativeInterop;
@@ -334,6 +335,21 @@ namespace Godot
NativeFuncs.godotsharp_printt(godotStr);
}
+ [StackTraceHidden]
+ private static void ErrPrintError(string message, godot_error_handler_type type = godot_error_handler_type.ERR_HANDLER_ERROR)
+ {
+ // Skip 1 frame to avoid current method.
+ var stackFrame = DebuggingUtils.GetCurrentStackFrame(skipFrames: 1);
+ string callerFilePath = ProjectSettings.LocalizePath(stackFrame.GetFileName());
+ DebuggingUtils.GetStackFrameMethodDecl(stackFrame, out string callerName);
+ int callerLineNumber = stackFrame.GetFileLineNumber();
+
+ using godot_string messageStr = Marshaling.ConvertStringToNative(message);
+ using godot_string callerNameStr = Marshaling.ConvertStringToNative(callerName);
+ using godot_string callerFilePathStr = Marshaling.ConvertStringToNative(callerFilePath);
+ NativeFuncs.godotsharp_err_print_error(callerNameStr, callerFilePathStr, callerLineNumber, messageStr, p_type: type);
+ }
+
/// <summary>
/// Pushes an error message to Godot's built-in debugger and to the OS terminal.
///
@@ -347,8 +363,7 @@ namespace Godot
/// <param name="message">Error message.</param>
public static void PushError(string message)
{
- using var godotStr = Marshaling.ConvertStringToNative(message);
- NativeFuncs.godotsharp_pusherror(godotStr);
+ ErrPrintError(message);
}
/// <summary>
@@ -364,7 +379,7 @@ namespace Godot
/// <param name="what">Arguments that form the error message.</param>
public static void PushError(params object[] what)
{
- PushError(AppendPrintParams(what));
+ ErrPrintError(AppendPrintParams(what));
}
/// <summary>
@@ -378,8 +393,7 @@ namespace Godot
/// <param name="message">Warning message.</param>
public static void PushWarning(string message)
{
- using var godotStr = Marshaling.ConvertStringToNative(message);
- NativeFuncs.godotsharp_pushwarning(godotStr);
+ ErrPrintError(message, type: godot_error_handler_type.ERR_HANDLER_WARNING);
}
/// <summary>
@@ -393,7 +407,7 @@ namespace Godot
/// <param name="what">Arguments that form the warning message.</param>
public static void PushWarning(params object[] what)
{
- PushWarning(AppendPrintParams(what));
+ ErrPrintError(AppendPrintParams(what), type: godot_error_handler_type.ERR_HANDLER_WARNING);
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
index ba2c232580..a656c5de90 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -95,7 +95,7 @@ namespace Godot.NativeInterop
}
NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line,
- nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector);
+ nErrorMsg, nExcMsg, godot_error_handler_type.ERR_HANDLER_ERROR, stackInfoVector);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index 1dddc82e85..d5d9404ed1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -1134,4 +1134,13 @@ namespace Godot.NativeInterop
get => _ptr != null ? *((int*)_ptr - 1) : 0;
}
}
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum godot_error_handler_type
+ {
+ ERR_HANDLER_ERROR = 0,
+ ERR_HANDLER_WARNING,
+ ERR_HANDLER_SCRIPT,
+ ERR_HANDLER_SHADER,
+ }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 3ec3d1e530..d42ee15657 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -58,7 +58,7 @@ namespace Godot.NativeInterop
internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
- godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+ godot_error_handler_type p_type, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
internal static partial godot_bool godotsharp_internal_script_debugger_is_active();
@@ -540,9 +540,7 @@ namespace Godot.NativeInterop
internal static partial void godotsharp_var_to_str(in godot_variant p_var, out godot_string r_ret);
- internal static partial void godotsharp_pusherror(in godot_string p_str);
-
- internal static partial void godotsharp_pushwarning(in godot_string p_str);
+ internal static partial void godotsharp_err_print_error(in godot_string p_function, in godot_string p_file, int p_line, in godot_string p_error, in godot_string p_message = default, godot_bool p_editor_notify = godot_bool.False, godot_error_handler_type p_type = godot_error_handler_type.ERR_HANDLER_ERROR);
// Object
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index ee4de4e9f5..24a9d4030a 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -92,10 +92,10 @@ void godotsharp_stack_info_vector_destroy(
void godotsharp_internal_script_debugger_send_error(const String *p_func,
const String *p_file, int32_t p_line, const String *p_err, const String *p_descr,
- bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
+ ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
const String file = ProjectSettings::get_singleton()->localize_path(p_file->simplify_path());
EngineDebugger::get_script_debugger()->send_error(*p_func, file, p_line, *p_err, *p_descr,
- true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector);
+ true, p_type, *p_stack_info_vector);
}
bool godotsharp_internal_script_debugger_is_active() {
@@ -1320,12 +1320,14 @@ void godotsharp_printraw(const godot_string *p_what) {
OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data());
}
-void godotsharp_pusherror(const godot_string *p_str) {
- ERR_PRINT(*reinterpret_cast<const String *>(p_str));
-}
-
-void godotsharp_pushwarning(const godot_string *p_str) {
- WARN_PRINT(*reinterpret_cast<const String *>(p_str));
+void godotsharp_err_print_error(const godot_string *p_function, const godot_string *p_file, int32_t p_line, const godot_string *p_error, const godot_string *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
+ _err_print_error(
+ reinterpret_cast<const String *>(p_function)->utf8().get_data(),
+ reinterpret_cast<const String *>(p_file)->utf8().get_data(),
+ p_line,
+ reinterpret_cast<const String *>(p_error)->utf8().get_data(),
+ reinterpret_cast<const String *>(p_message)->utf8().get_data(),
+ p_editor_notify, p_type);
}
void godotsharp_var_to_str(const godot_variant *p_var, godot_string *r_ret) {
@@ -1611,8 +1613,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_str_to_var,
(void *)godotsharp_var_to_bytes,
(void *)godotsharp_var_to_str,
- (void *)godotsharp_pusherror,
- (void *)godotsharp_pushwarning,
+ (void *)godotsharp_err_print_error,
(void *)godotsharp_object_to_string,
};
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 279b5cfed2..00ef4ccdde 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -95,6 +95,38 @@ String _get_mono_user_dir() {
#endif
}
+#if !TOOLS_ENABLED
+// This should be the equivalent of GodotTools.Utils.OS.PlatformNameMap.
+static const char *platform_name_map[][2] = {
+ { "Windows", "windows" },
+ { "macOS", "macos" },
+ { "Linux", "linuxbsd" },
+ { "FreeBSD", "linuxbsd" },
+ { "NetBSD", "linuxbsd" },
+ { "BSD", "linuxbsd" },
+ { "UWP", "uwp" },
+ { "Haiku", "haiku" },
+ { "Android", "android" },
+ { "iOS", "ios" },
+ { "Web", "web" },
+ { nullptr, nullptr }
+};
+
+String _get_platform_name() {
+ String platform_name = OS::get_singleton()->get_name();
+
+ int idx = 0;
+ while (platform_name_map[idx][0] != nullptr) {
+ if (platform_name_map[idx][0] == platform_name) {
+ return platform_name_map[idx][1];
+ }
+ idx++;
+ }
+
+ return "";
+}
+#endif
+
class _GodotSharpDirs {
public:
String res_metadata_dir;
@@ -139,12 +171,13 @@ private:
#endif
api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
#else // TOOLS_ENABLED
+ String platform = _get_platform_name();
String arch = Engine::get_singleton()->get_architecture_name();
String appname_safe = path::get_csharp_project_name();
String packed_path = "res://.godot/mono/publish/" + arch;
if (DirAccess::exists(packed_path)) {
// The dotnet publish data is packed in the pck/zip.
- String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + arch);
+ String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + platform + "_" + arch);
bool has_data = false;
if (!has_data) {
// 1. Try to access the data directly.
@@ -173,16 +206,10 @@ private:
api_assemblies_dir = data_dir_root;
} else {
// The dotnet publish data is in a directory next to the executable.
- String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = exe_dir.path_join("data_Godot_" + arch);
- }
+ String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + platform + "_" + arch);
#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
- }
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_Godot_" + arch);
+ data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + platform + "_" + arch);
}
#endif
api_assemblies_dir = data_dir_root;
diff --git a/modules/mono/icons/BuildCSharp.svg b/modules/mono/icons/BuildCSharp.svg
new file mode 100644
index 0000000000..9d0102c35d
--- /dev/null
+++ b/modules/mono/icons/BuildCSharp.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M 9.6060193,0.78346667 C 8.6741914,0.96303367 7.6708299,1.5334576 6.9028943,1.9768256 l -2.1523438,1.244141 0.082031,0.138672 -0.3105469,-0.00781 -2.5839844,1.492188 1.9101563,3.308593 2.5820312,-1.490234 0.1425781,-0.255859 4.1875002,7.2539054 0.5,0.867187 c 0.415803,0.720194 1.331398,0.964165 2.050782,0.548829 0.719286,-0.415279 0.963839,-1.33001 0.548828,-2.048829 l -2,-3.4648424 -2.8808602,-4.990235 3.7070322,-2.101562 -0.265626,-0.439453 C 11.697382,0.83561667 10.650124,0.58226267 9.6060193,0.78346667 Z" fill="#e0e0e0"/></svg>
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 5e52f25cf4..247968e251 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -376,6 +376,12 @@ void GDMono::initialize() {
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
+ // Check that the .NET assemblies directory exists before trying to use it.
+ if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) {
+ OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found"));
+ ERR_FAIL_MSG(".NET: Assemblies not found");
+ }
+
if (!load_hostfxr(hostfxr_dll_handle)) {
#if !defined(TOOLS_ENABLED)
godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle);
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..8f56bf0f1d 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
@@ -932,14 +932,34 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, 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) {
#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) {
#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.");
+
+ if (!p_source_geometry_data->has_data()) {
+ p_navigation_mesh->clear();
+ if (p_callback.is_valid()) {
+ Callable::CallError ce;
+ Variant result;
+ p_callback.callp(nullptr, 0, result, ce);
+ }
+ return;
+ }
+
+ 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
}
COMMAND_1(free, RID, p_object) {
@@ -1117,6 +1137,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..8b91ca36bd 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;
@@ -234,6 +237,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_map.cpp b/modules/navigation/nav_map.cpp
index 8b7274d673..737ccaf3cd 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -1060,7 +1060,8 @@ void NavMap::sync() {
}
// Update the update ID.
- map_update_id = (map_update_id + 1) % 9999999;
+ // Some code treats 0 as a failure case, so we avoid returning 0.
+ map_update_id = map_update_id % 9999999 + 1;
}
// Do we have modified obstacle positions?
diff --git a/modules/navigation/nav_mesh_generator_3d.cpp b/modules/navigation/nav_mesh_generator_3d.cpp
new file mode 100644
index 0000000000..ab25a18d28
--- /dev/null
+++ b/modules/navigation/nav_mesh_generator_3d.cpp
@@ -0,0 +1,728 @@
+/**************************************************************************/
+/* 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/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"
+
+#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;
+HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
+
+NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
+ return singleton;
+}
+
+NavMeshGenerator3D::NavMeshGenerator3D() {
+ ERR_FAIL_COND(singleton != nullptr);
+ singleton = this;
+}
+
+NavMeshGenerator3D::~NavMeshGenerator3D() {
+ cleanup();
+}
+
+void NavMeshGenerator3D::cleanup() {
+ baking_navmesh_mutex.lock();
+ baking_navmeshes.clear();
+ baking_navmesh_mutex.unlock();
+}
+
+void NavMeshGenerator3D::finish() {
+ cleanup();
+}
+
+void NavMeshGenerator3D::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(!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, const 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());
+ ERR_FAIL_COND(!p_source_geometry_data->has_data());
+
+ 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.");
+ } else {
+ 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::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..dc844c2595
--- /dev/null
+++ b/modules/navigation/nav_mesh_generator_3d.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* 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 "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 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 cleanup();
+ static void finish();
+
+ static 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());
+ static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const 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/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/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/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index af9dae84e3..4b12a4e41d 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -262,7 +262,6 @@ if env["freetype_enabled"]:
CPPDEFINES=[
"FT2_BUILD_LIBRARY",
"FT_CONFIG_OPTION_USE_PNG",
- ("PNG_ARM_NEON_OPT", 0),
"FT_CONFIG_OPTION_SYSTEM_ZLIB",
]
)
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 13d8a2c17a..043a33ab35 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4771,7 +4771,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_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 51a6ee06be..fcf8976019 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -257,7 +257,6 @@ if env["freetype_enabled"]:
CPPDEFINES=[
"FT2_BUILD_LIBRARY",
"FT_CONFIG_OPTION_USE_PNG",
- ("PNG_ARM_NEON_OPT", 0),
"FT_CONFIG_OPTION_SYSTEM_ZLIB",
]
)
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/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index d61d63d242..c0c57cafbe 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -150,7 +150,7 @@
Allows an application to act as an AccountAuthenticator for the AccountManager.
</member>
<member name="permissions/battery_stats" type="bool" setter="" getter="">
- Allows an application to collect battery statistics. Sett [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url].
+ Allows an application to collect battery statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url].
</member>
<member name="permissions/bind_accessibility_service" type="bool" setter="" getter="">
Must be required by an AccessibilityService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_ACCESSIBILITY_SERVICE]BIND_ACCESSIBILITY_SERVICE[/url].
@@ -579,10 +579,10 @@
If [code]true[/code], allows the application to participate in the backup and restore infrastructure.
</member>
<member name="version/code" type="int" setter="" getter="">
- Machine-readable application version.
+ Machine-readable application version. This must be incremented for every new release pushed to the Play Store.
</member>
<member name="version/name" type="String" setter="" getter="">
- Application version visible to the user.
+ Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="xr_features/xr_mode" type="int" setter="" getter="">
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 4eb516fb63..9e46085b2a 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -904,7 +904,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
uint32_t string_table_ends = 0;
Vector<uint8_t> stable_extra;
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
int version_code = p_preset->get("version/code");
String package_name = p_preset->get("package/unique_name");
@@ -1829,7 +1829,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
@@ -1980,7 +1980,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
String output;
bool remove_prev = EDITOR_GET("export/android/one_click_deploy_clear_previous_install");
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String package_name = p_preset->get("package/unique_name");
if (remove_prev) {
@@ -2893,7 +2893,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String package_name = get_package_name(p_preset->get("package/unique_name"));
String version_code = itos(p_preset->get("version/code"));
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String min_sdk_version = p_preset->get("gradle_build/min_sdk");
if (!min_sdk_version.is_valid_int()) {
min_sdk_version = itos(VULKAN_MIN_SDK_VERSION);
@@ -3111,7 +3111,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String cmdline = p_preset->get("command_line/extra_args");
- String version_name = p_preset->get("version/name");
+ String version_name = p_preset->get_version("version/name");
String package_name = p_preset->get("package/unique_name");
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
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 f5501e3d85..a01f4b55db 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -85,19 +85,18 @@ def configure(env: "Environment"):
env["ENV"]["PATH"] = env["IOS_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}"
- s_compiler_path = "$IOS_TOOLCHAIN_PATH/Developer/usr/bin/"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
env["CC"] = compiler_path + "clang"
env["CXX"] = compiler_path + "clang++"
- env["S_compiler"] = s_compiler_path + "gcc"
+ env["S_compiler"] = compiler_path + "clang"
else:
# there aren't any ccache wrappers available for iOS,
# to enable caching we need to prepend the path to the ccache binary
env["CC"] = ccache_path + " " + compiler_path + "clang"
env["CXX"] = ccache_path + " " + compiler_path + "clang++"
- env["S_compiler"] = ccache_path + " " + s_compiler_path + "gcc"
+ env["S_compiler"] = ccache_path + " " + compiler_path + "clang"
env["AR"] = compiler_path + "ar"
env["RANLIB"] = compiler_path + "ranlib"
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 84bc0e1277..563c057266 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -45,7 +45,7 @@
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
- Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="application/signature" type="String" setter="" getter="">
A four-character creator code that is specific to the bundle. Optional.
@@ -54,7 +54,7 @@
Supported device family.
</member>
<member name="application/version" type="String" setter="" getter="">
- Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store.
</member>
<member name="architectures/arm64" type="bool" setter="" getter="">
If [code]true[/code], [code]arm64[/code] binaries are included into exported project.
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 544bfb71e0..3d63b7bb55 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -177,7 +177,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
@@ -284,7 +284,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
- strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
} else if (lines[i].find("$team_id") != -1) {
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 342cff82e9..5d636240b7 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -56,6 +56,14 @@
static const char *ignore_str = "/dev/input/js";
#endif
+// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is
+// the Steam Input gamepad index
+#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad "
+// IDs used by Steam Input virtual controllers.
+// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
+#define VALVE_GAMEPAD_VID 0x28DE
+#define VALVE_GAMEPAD_PID 0x11FF
+
JoypadLinux::Joypad::~Joypad() {
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
@@ -411,8 +419,23 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joypad);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
+ Dictionary joypad_info;
+ joypad_info["vendor_id"] = inpid.vendor;
+ joypad_info["product_id"] = inpid.product;
+ joypad_info["raw_name"] = name;
+
sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
- input->joy_connection_changed(joy_num, true, name, uid);
+
+ if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) {
+ if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) {
+ String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX));
+ if (idx_str.is_valid_int()) {
+ joypad_info["steam_input_index"] = idx_str.to_int();
+ }
+ }
+ }
+
+ input->joy_connection_changed(joy_num, true, name, uid, joypad_info);
} else {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 2643cd3b1a..e634f28e76 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -744,7 +744,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 +756,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 +809,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 +1244,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) {
@@ -1586,6 +1586,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;
@@ -3957,7 +3958,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)) {
@@ -5461,13 +5462,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 +5536,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 +5631,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..74aa0b6e75 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -322,7 +322,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;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index d64bb5211e..de191827f5 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -4026,7 +4026,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 6af816989d..c24ff4cb73 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -23,7 +23,7 @@
Copyright notice for the bundle visible to the user (localized).
</member>
<member name="application/icon" type="String" setter="" getter="">
- Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon].
+ Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon].
</member>
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
@@ -32,13 +32,13 @@
Minimum version of macOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
</member>
<member name="application/short_version" type="String" setter="" getter="">
- Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
</member>
<member name="application/signature" type="String" setter="" getter="">
A four-character creator code that is specific to the bundle. Optional.
</member>
<member name="application/version" type="String" setter="" getter="">
- Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
+ Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store.
</member>
<member name="binary_format/architecture" type="String" setter="" getter="">
Application executable architecture.
@@ -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 1afcedc576..e788ff1eec 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -378,7 +378,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12"));
@@ -672,7 +672,7 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
- strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
} else if (lines[i].find("$app_category") != -1) {
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/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/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
index 80928d7418..fc068efc75 100644
--- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml
+++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
@@ -22,10 +22,10 @@
File description to be presented to users. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/file_version" type="String" setter="" getter="">
- Version number of the file. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
+ Version number of the file. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/icon" type="String" setter="" getter="">
- Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon].
+ Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon].
</member>
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
@@ -37,7 +37,7 @@
Name of the application. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/product_version" type="String" setter="" getter="">
- Application version visible to the user. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
+ Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
</member>
<member name="application/trademarks" type="String" setter="" getter="">
Trademarks and registered trademarks that apply to the file. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 0ef07c3275..07c6a8d6e4 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -342,8 +342,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
@@ -425,8 +425,8 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset
}
}
- String file_verion = p_preset->get("application/file_version");
- String product_version = p_preset->get("application/product_version");
+ String file_verion = p_preset->get_version("application/file_version", true);
+ String product_version = p_preset->get_version("application/product_version", true);
String company_name = p_preset->get("application/company_name");
String product_name = p_preset->get("application/product_name");
String file_description = p_preset->get("application/file_description");
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 487cb56ba0..0ac6c2c8b0 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -318,7 +318,9 @@ void JoypadWindows::probe_joypads() {
x_joypads[i].ff_end_timestamp = 0;
x_joypads[i].vibrating = false;
attached_joypads[id] = true;
- input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__");
+ Dictionary joypad_info;
+ joypad_info["xinput_index"] = (int)i;
+ input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__", joypad_info);
}
} else if (x_joypads[i].attached) {
x_joypads[i].attached = false;
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/tile_map.compat.inc b/scene/2d/tile_map.compat.inc
index 49e2bf6f0b..c7786ecced 100644
--- a/scene/2d/tile_map.compat.inc
+++ b/scene/2d/tile_map.compat.inc
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* object.compat.inc */
+/* tile_map.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,10 +30,6 @@
#ifndef DISABLE_DEPRECATED
-#include "core/object/object.h"
-
-#include "core/object/class_db.h"
-
Rect2i TileMap::_get_used_rect_bind_compat_78328() {
return get_used_rect();
}
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..367a1d445f 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));
}
@@ -249,7 +245,7 @@ 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<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh();
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 7b04669dad..13519ecec7 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -104,6 +104,10 @@ Vector3 RayCast3D::get_collision_normal() const {
return collision_normal;
}
+int RayCast3D::get_collision_face_index() const {
+ return collision_face_index;
+}
+
void RayCast3D::set_enabled(bool p_enabled) {
enabled = p_enabled;
update_gizmos();
@@ -232,6 +236,7 @@ void RayCast3D::_update_raycast_state() {
against_rid = rr.rid;
collision_point = rr.position;
collision_normal = rr.normal;
+ collision_face_index = rr.face_index;
against_shape = rr.shape;
} else {
collided = false;
@@ -321,6 +326,7 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast3D::get_collider_shape);
ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast3D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast3D::get_collision_normal);
+ ClassDB::bind_method(D_METHOD("get_collision_face_index"), &RayCast3D::get_collision_face_index);
ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &RayCast3D::add_exception_rid);
ClassDB::bind_method(D_METHOD("add_exception", "node"), &RayCast3D::add_exception);
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index 1def7a0eca..7b7f698114 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -45,6 +45,7 @@ class RayCast3D : public Node3D {
int against_shape = 0;
Vector3 collision_point;
Vector3 collision_normal;
+ int collision_face_index = -1;
Vector3 target_position = Vector3(0, -1, 0);
HashSet<RID> exclude;
@@ -122,6 +123,7 @@ public:
int get_collider_shape() const;
Vector3 get_collision_point() const;
Vector3 get_collision_normal() const;
+ int get_collision_face_index() const;
void add_exception_rid(const RID &p_rid);
void add_exception(const CollisionObject3D *p_node);
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 3ac3ca7363..f57afb66b3 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -468,12 +468,12 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group);
GDVIRTUAL_BIND(_pressed);
- GDVIRTUAL_BIND(_toggled, "button_pressed");
+ GDVIRTUAL_BIND(_toggled, "toggled_on");
ADD_SIGNAL(MethodInfo("pressed"));
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
- ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
+ ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "toggled_on")));
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
diff --git a/scene/gui/code_edit.compat.inc b/scene/gui/code_edit.compat.inc
new file mode 100644
index 0000000000..9107d6523f
--- /dev/null
+++ b/scene/gui/code_edit.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* code_edit.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
+
+String CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196() {
+ return get_text_for_symbol_lookup();
+}
+
+void CodeEdit::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196);
+}
+
+#endif
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index eee59606e3..68241337c9 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "code_edit.h"
+#include "code_edit.compat.inc"
#include "core/os/keyboard.h"
#include "core/string/string_builder.h"
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index a3c968da60..addbb6e468 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -287,6 +287,11 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ String _get_text_for_symbol_lookup_bind_compat_73196();
+ static void _bind_compatibility_methods();
+#endif
+
virtual void _update_theme_item_cache() override;
/* Text manipulation */
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/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/tree.cpp b/scene/gui/tree.cpp
index 7038145c80..433ae656ba 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1923,6 +1923,7 @@ void Tree::update_column(int p_col) {
}
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].cached_minimum_width_dirty = true;
}
void Tree::update_item_cell(TreeItem *p_item, int p_col) {
@@ -2015,7 +2016,7 @@ void Tree::update_item_cache(TreeItem *p_item) {
}
}
-int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height) {
+int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height) {
if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) {
return -1; //draw no more!
}
@@ -2097,11 +2098,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
p_item->cells.write[i].text_buf->set_width(item_width);
- label_h = compute_item_height(p_item);
- if (r_self_height != nullptr) {
- *r_self_height = label_h;
+ r_self_height = compute_item_height(p_item);
+ label_h = r_self_height + theme_cache.v_separation;
+
+ if (p_pos.y + label_h - theme_cache.offset.y < 0) {
+ continue; // No need to draw.
}
- label_h += theme_cache.v_separation;
Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(item_width, label_h));
Rect2i cell_rect = item_rect;
@@ -2450,7 +2452,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int child_h = -1;
int child_self_height = 0;
if (htotal >= 0) {
- child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, &child_self_height);
+ child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, child_self_height);
child_self_height += theme_cache.v_separation;
}
@@ -4232,7 +4234,8 @@ void Tree::_notification(int p_what) {
cache.rtl = is_layout_rtl();
if (root && get_size().x > 0 && get_size().y > 0) {
- draw_item(Point2(), draw_ofs, draw_size, root);
+ int self_height = 0; // Just to pass a reference, we don't need the root's `self_height`.
+ draw_item(Point2(), draw_ofs, draw_size, root, self_height);
}
if (show_column_titles) {
@@ -4250,6 +4253,7 @@ void Tree::_notification(int p_what) {
//text
int clip_w = tbrect.size.width - sb->get_minimum_size().width;
columns.write[i].text_buf->set_width(clip_w);
+ columns.write[i].cached_minimum_width_dirty = true;
Vector2 text_pos = Point2i(tbrect.position.x, tbrect.position.y + (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
switch (columns[i].title_alignment) {
@@ -4387,6 +4391,7 @@ void Tree::item_edited(int p_column, TreeItem *p_item, MouseButton p_custom_mous
void Tree::item_changed(int p_column, TreeItem *p_item) {
if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) {
p_item->cells.write[p_column].dirty = true;
+ columns.write[p_column].cached_minimum_width_dirty = true;
}
queue_redraw();
}
@@ -4518,6 +4523,7 @@ void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) {
return;
}
columns.write[p_column].custom_min_width = p_min_width;
+ columns.write[p_column].cached_minimum_width_dirty = true;
queue_redraw();
}
@@ -4529,6 +4535,7 @@ void Tree::set_column_expand(int p_column, bool p_expand) {
}
columns.write[p_column].expand = p_expand;
+ columns.write[p_column].cached_minimum_width_dirty = true;
queue_redraw();
}
@@ -4540,6 +4547,7 @@ void Tree::set_column_expand_ratio(int p_column, int p_ratio) {
}
columns.write[p_column].expand_ratio = p_ratio;
+ columns.write[p_column].cached_minimum_width_dirty = true;
queue_redraw();
}
@@ -4551,6 +4559,7 @@ void Tree::set_column_clip_content(int p_column, bool p_fit) {
}
columns.write[p_column].clip_content = p_fit;
+ columns.write[p_column].cached_minimum_width_dirty = true;
queue_redraw();
}
@@ -4633,46 +4642,51 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) {
int Tree::get_column_minimum_width(int p_column) const {
ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
- // Use the custom minimum width.
- int min_width = columns[p_column].custom_min_width;
+ if (columns[p_column].cached_minimum_width_dirty) {
+ // Use the custom minimum width.
+ int min_width = columns[p_column].custom_min_width;
- // Check if the visible title of the column is wider.
- if (show_column_titles) {
- min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width);
- }
-
- if (!columns[p_column].clip_content) {
- int depth = 0;
- TreeItem *next;
- for (TreeItem *item = get_root(); item; item = next) {
- next = item->get_next_visible();
- // Compute the depth in tree.
- if (next && p_column == 0) {
- if (next->get_parent() == item) {
- depth += 1;
- } else {
- TreeItem *common_parent = item->get_parent();
- while (common_parent != next->get_parent() && common_parent) {
- common_parent = common_parent->get_parent();
- depth -= 1;
+ // Check if the visible title of the column is wider.
+ if (show_column_titles) {
+ min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width);
+ }
+
+ if (!columns[p_column].clip_content) {
+ int depth = 0;
+ TreeItem *next;
+ for (TreeItem *item = get_root(); item; item = next) {
+ next = item->get_next_visible();
+ // Compute the depth in tree.
+ if (next && p_column == 0) {
+ if (next->get_parent() == item) {
+ depth += 1;
+ } else {
+ TreeItem *common_parent = item->get_parent();
+ while (common_parent != next->get_parent() && common_parent) {
+ common_parent = common_parent->get_parent();
+ depth -= 1;
+ }
}
}
- }
- // Get the item minimum size.
- Size2 item_size = item->get_minimum_size(p_column);
- if (p_column == 0) {
- item_size.width += theme_cache.item_margin * depth;
- } else {
- item_size.width += theme_cache.h_separation;
- }
+ // Get the item minimum size.
+ Size2 item_size = item->get_minimum_size(p_column);
+ if (p_column == 0) {
+ item_size.width += theme_cache.item_margin * depth;
+ } else {
+ item_size.width += theme_cache.h_separation;
+ }
- // Check if the item is wider.
- min_width = MAX(min_width, item_size.width);
+ // Check if the item is wider.
+ min_width = MAX(min_width, item_size.width);
+ }
}
+
+ columns.get(p_column).cached_minimum_width = min_width;
+ columns.get(p_column).cached_minimum_width_dirty = false;
}
- return min_width;
+ return columns[p_column].cached_minimum_width;
}
int Tree::get_column_width(int p_column) const {
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index ce4379d7fe..cb00889cb9 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -452,6 +452,10 @@ private:
Ref<TextParagraph> text_buf;
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
+
+ mutable int cached_minimum_width = 0;
+ mutable bool cached_minimum_width_dirty = true;
+
ColumnInfo() {
text_buf.instantiate();
}
@@ -484,7 +488,7 @@ private:
void update_item_cache(TreeItem *p_item);
//void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color);
void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color);
- int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height = nullptr);
+ int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height);
void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod);
void _line_editor_submit(String p_text);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 405b221636..ea09b15c67 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;
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 3ff4003b3e..331ce98cdd 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -508,7 +508,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;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index bd51f8eeaf..875b53203a 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -670,9 +670,14 @@ 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);
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
@@ -681,7 +686,12 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
} 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;
+ }
root->gui.windowmanager_window_over->_mouse_leave_viewport();
root->gui.windowmanager_window_over = nullptr;
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
@@ -1748,6 +1758,10 @@ Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) co
Size2 Window::get_contents_minimum_size() const {
ERR_READ_THREAD_GUARD_V(Size2());
+ Vector2 ms;
+ if (GDVIRTUAL_CALL(_get_contents_minimum_size, ms)) {
+ return ms;
+ }
return _get_contents_minimum_size();
}
@@ -2760,6 +2774,8 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN);
BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS);
BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS);
+
+ GDVIRTUAL_BIND(_get_contents_minimum_size);
}
Window::Window() {
diff --git a/scene/main/window.h b/scene/main/window.h
index 24142b8a91..18ddd89662 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -205,7 +205,6 @@ protected:
virtual void _update_theme_item_cache();
virtual void _post_popup() {}
- virtual Size2 _get_contents_minimum_size() const;
static void _bind_methods();
void _notification(int p_what);
@@ -217,6 +216,8 @@ protected:
virtual void add_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ GDVIRTUAL0RC(Vector2, _get_contents_minimum_size)
+
public:
enum {
NOTIFICATION_VISIBILITY_CHANGED = 30,
@@ -409,6 +410,8 @@ public:
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
+ virtual Size2 _get_contents_minimum_size() const;
+
Window();
~Window();
};
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/scene_string_names.cpp b/scene/scene_string_names.cpp
index 87835a9522..f0971f1a34 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -199,6 +199,7 @@ SceneStringNames::SceneStringNames() {
_window_input = StaticCString::create("_window_input");
window_input = StaticCString::create("window_input");
_window_unhandled_input = StaticCString::create("_window_unhandled_input");
+ _get_contents_minimum_size = StaticCString::create("_get_contents_minimum_size");
theme_changed = StaticCString::create("theme_changed");
parameters_base_path = "parameters/";
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index ad1135e24c..f31cf7b881 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -210,6 +210,7 @@ public:
StringName _window_input;
StringName _window_unhandled_input;
StringName window_input;
+ StringName _get_contents_minimum_size;
StringName theme_changed;
StringName shader_overrides_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/navigation_server_3d.h b/servers/navigation_server_3d.h
index 391730e18f..4bf25f7a33 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,6 +301,8 @@ 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;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index b2d452f67a..a5c9fc57f2 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -150,6 +150,8 @@ public:
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/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp
index a78bd0f888..84fae73616 100644
--- a/servers/physics_3d/godot_body_pair_3d.cpp
+++ b/servers/physics_3d/godot_body_pair_3d.cpp
@@ -168,7 +168,6 @@ void GodotBodyPair3D::validate_contacts() {
// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) {
GodotShape3D *shape_A_ptr = p_A->get_shape(p_shape_A);
- GodotShape3D *shape_B_ptr = p_B->get_shape(p_shape_B);
Vector3 motion = p_A->get_linear_velocity() * p_step;
real_t mlen = motion.length();
@@ -221,7 +220,8 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A,
Vector3 local_to = from_inv.xform(to);
Vector3 rpos, rnorm;
- if (shape_B_ptr->intersect_segment(local_from, local_to, rpos, rnorm, true)) {
+ int fi = -1;
+ if (p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm, fi, true)) {
float hit_length = local_from.distance_to(rpos);
if (hit_length < segment_hit_length) {
segment_support_idx = i;
diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp
index 2de1d86de1..db48111eea 100644
--- a/servers/physics_3d/godot_collision_solver_3d.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d.cpp
@@ -106,7 +106,8 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A,
to = ai.xform(to);
Vector3 p, n;
- if (!p_shape_B->intersect_segment(from, to, p, n, true)) {
+ int fi = -1;
+ if (!p_shape_B->intersect_segment(from, to, p, n, fi, true)) {
return false;
}
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index a2276d8dec..872d26aff6 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -126,7 +126,7 @@ Vector3 GodotWorldBoundaryShape3D::get_support(const Vector3 &p_normal) const {
return p_normal * 1e15;
}
-bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
bool inters = plane.intersects_segment(p_begin, p_end, &r_result);
if (inters) {
r_normal = plane.normal;
@@ -207,7 +207,7 @@ void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max,
}
}
-bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return false; //simply not possible
}
@@ -275,7 +275,7 @@ void GodotSphereShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector
r_type = FEATURE_POINT;
}
-bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal);
}
@@ -417,7 +417,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
r_supports[0] = point;
}
-bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
AABB aabb_ext(-half_extents, half_extents * 2.0);
return aabb_ext.intersects_segment(p_begin, p_end, &r_result, &r_normal);
@@ -551,7 +551,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto
}
}
-bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
Vector3 norm = (p_end - p_begin).normalized();
real_t min_d = 1e20;
@@ -743,7 +743,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect
}
}
-bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &r_result, &r_normal, 1);
}
@@ -975,7 +975,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
r_type = FEATURE_POINT;
}
-bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();
int fc = mesh.faces.size();
@@ -1252,7 +1252,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
r_supports[0] = vertex[vert_support_idx];
}
-bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result);
if (c) {
r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal;
@@ -1362,12 +1362,14 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
Vector3 res;
Vector3 normal;
- if (face->intersect_segment(p_params->from, p_params->to, res, normal, true)) {
+ int face_index = params_bvh->face_index;
+ if (face->intersect_segment(p_params->from, p_params->to, res, normal, face_index, true)) {
real_t d = p_params->dir.dot(res) - p_params->dir.dot(p_params->from);
if ((d > 0) && (d < p_params->min_d)) {
p_params->min_d = d;
p_params->result = res;
p_params->normal = normal;
+ p_params->face_index = face_index;
p_params->collisions++;
}
}
@@ -1381,7 +1383,7 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
}
}
-bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
if (faces.size() == 0) {
return false;
}
@@ -1411,6 +1413,7 @@ bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const
if (params.collisions > 0) {
r_result = params.result;
r_normal = params.normal;
+ r_face_index = params.face_index;
return true;
} else {
return false;
@@ -1734,9 +1737,11 @@ struct _HeightmapGridCullState {
_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {
Vector3 res;
Vector3 normal;
- if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, true)) {
+ int fi = -1;
+ if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, fi, true)) {
p_params.result = res;
p_params.normal = normal;
+
return true;
}
@@ -1940,7 +1945,7 @@ bool GodotHeightMapShape3D::_intersect_grid_segment(ProcessFunction &p_process,
return false;
}
-bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
if (heights.is_empty()) {
return false;
}
diff --git a/servers/physics_3d/godot_shape_3d.h b/servers/physics_3d/godot_shape_3d.h
index acc42f2fcb..dbd58ead68 100644
--- a/servers/physics_3d/godot_shape_3d.h
+++ b/servers/physics_3d/godot_shape_3d.h
@@ -80,7 +80,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const = 0;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const = 0;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const = 0;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const = 0;
virtual bool intersect_point(const Vector3 &p_point) const = 0;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const = 0;
@@ -126,7 +126,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const override;
@@ -153,7 +153,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -180,7 +180,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -205,7 +205,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -234,7 +234,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -263,7 +263,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -290,7 +290,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -347,6 +347,7 @@ struct GodotConcavePolygonShape3D : public GodotConcaveShape3D {
Vector3 result;
Vector3 normal;
+ int face_index = -1;
real_t min_d = 1e20;
int collisions = 0;
};
@@ -368,7 +369,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -431,7 +432,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -459,7 +460,7 @@ struct GodotFaceShape3D : public GodotShape3D {
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -498,7 +499,7 @@ struct GodotMotionShape3D : public GodotShape3D {
}
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override { return false; }
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override { return false; }
virtual bool intersect_point(const Vector3 &p_point) const override { return false; }
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override { return p_point; }
diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp
index 5c67807c48..4b35dd1500 100644
--- a/servers/physics_3d/godot_soft_body_3d.cpp
+++ b/servers/physics_3d/godot_soft_body_3d.cpp
@@ -1266,7 +1266,7 @@ struct _SoftBodyIntersectSegmentInfo {
}
};
-bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
_SoftBodyIntersectSegmentInfo query_info;
query_info.soft_body = soft_body;
query_info.from = p_begin;
diff --git a/servers/physics_3d/godot_soft_body_3d.h b/servers/physics_3d/godot_soft_body_3d.h
index 20592f3411..e23f4bb9f5 100644
--- a/servers/physics_3d/godot_soft_body_3d.h
+++ b/servers/physics_3d/godot_soft_body_3d.h
@@ -259,7 +259,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override { return Vector3(); }
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const override { return Vector3(); }
diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp
index 35f6fa023d..92b5f31e41 100644
--- a/servers/physics_3d/godot_space_3d.cpp
+++ b/servers/physics_3d/godot_space_3d.cpp
@@ -120,6 +120,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
bool collided = false;
Vector3 res_point, res_normal;
+ int res_face_index = -1;
int res_shape = -1;
const GodotCollisionObject3D *res_obj = nullptr;
real_t min_d = 1e10;
@@ -148,6 +149,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
const GodotShape3D *shape = col_obj->get_shape(shape_idx);
Vector3 shape_point, shape_normal;
+ int shape_face_index = -1;
if (shape->intersect_point(local_from)) {
if (p_parameters.hit_from_inside) {
@@ -165,7 +167,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
}
}
- if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, p_parameters.hit_back_faces)) {
+ if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, shape_face_index, p_parameters.hit_back_faces)) {
Transform3D xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
shape_point = xform.xform(shape_point);
@@ -175,6 +177,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
min_d = ld;
res_point = shape_point;
res_normal = inv_xform.basis.xform_inv(shape_normal).normalized();
+ res_face_index = shape_face_index;
res_shape = shape_idx;
res_obj = col_obj;
collided = true;
@@ -194,6 +197,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
r_result.collider = nullptr;
}
r_result.normal = res_normal;
+ r_result.face_index = res_face_index;
r_result.position = res_point;
r_result.rid = res_obj->get_self();
r_result.shape = res_shape;
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 6b8d3d1af6..8497bc78e2 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -362,6 +362,7 @@ Dictionary PhysicsDirectSpaceState3D::_intersect_ray(const Ref<PhysicsRayQueryPa
Dictionary d;
d["position"] = result.position;
d["normal"] = result.normal;
+ d["face_index"] = result.face_index;
d["collider_id"] = result.collider_id;
d["collider"] = result.collider;
d["shape"] = result.shape;
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 33e9f8a7c9..cd96bf15fd 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -151,6 +151,7 @@ public:
struct RayResult {
Vector3 position;
Vector3 normal;
+ int face_index = -1;
RID rid;
ObjectID collider_id;
Object *collider = nullptr;
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 9c8e8dd231..2c537c3a1c 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -172,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();
}
@@ -254,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, 0u);
}
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/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 431bf9cb84..f94c701ada 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -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 9b05bf0c64..9c7990d679 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1435,7 +1435,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());
}
@@ -1848,7 +1848,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/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 07947f29e5..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);
@@ -701,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/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index d3814f175f..46ae2d9551 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -161,7 +161,7 @@ void ShaderRD::_clear_version(Version *p_version) {
// Clear versions if they exist.
if (p_version->variants) {
for (int i = 0; i < variant_defines.size(); i++) {
- if (variants_enabled[i] && group_enabled[variant_defines[i].group]) {
+ if (p_version->variants[i].is_valid()) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
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/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 29abff2911..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]
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/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/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_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/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc
new file mode 100644
index 0000000000..dc7817e66b
--- /dev/null
+++ b/servers/rendering/rendering_device.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* rendering_device.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
+
+RID RenderingDevice::_shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary) {
+ return shader_create_from_bytecode(p_shader_binary, RID());
+}
+
+void RenderingDevice::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::_shader_create_from_bytecode_bind_compat_79606);
+}
+
+#endif
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 1b0a3e9d0f..3da69b9c3c 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "rendering_device.h"
+#include "rendering_device.compat.inc"
#include "rendering_device_binds.h"
@@ -537,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 58da3df577..30d9b1c7b7 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -141,6 +141,11 @@ private:
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ RID _shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary);
+ static void _bind_compatibility_methods();
+#endif
+
Capabilities device_capabilities;
public:
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/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp
index ffb89fc385..2dacabe588 100644
--- a/servers/xr/xr_positional_tracker.cpp
+++ b/servers/xr/xr_positional_tracker.cpp
@@ -153,7 +153,13 @@ void XRPositionalTracker::invalidate_pose(const StringName &p_action_name) {
void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence) {
Ref<XRPose> new_pose;
- new_pose.instantiate();
+ if (poses.has(p_action_name)) {
+ new_pose = poses[p_action_name];
+ } else {
+ new_pose.instantiate();
+ poses[p_action_name] = new_pose;
+ }
+
new_pose->set_name(p_action_name);
new_pose->set_has_tracking_data(true);
new_pose->set_transform(p_transform);
@@ -161,7 +167,6 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf
new_pose->set_angular_velocity(p_angular_velocity);
new_pose->set_tracking_confidence(p_tracking_confidence);
- poses[p_action_name] = new_pose;
emit_signal(SNAME("pose_changed"), new_pose);
// TODO discuss whether we also want to create and emit an InputEventXRPose event
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/string/test_string.h b/tests/core/string/test_string.h
index 93b237788d..fc1e0590fc 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();
@@ -1700,7 +1700,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..9e525ab5bf
--- /dev/null
+++ b/tests/scene/test_packed_scene.h
@@ -0,0 +1,114 @@
+/**************************************************************************/
+/* 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);
+}
+
+} // 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..ab17459a41 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"
@@ -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/servers/rendering/test_shader_preprocessor.h b/tests/servers/rendering/test_shader_preprocessor.h
new file mode 100644
index 0000000000..41a009ccf5
--- /dev/null
+++ b/tests/servers/rendering/test_shader_preprocessor.h
@@ -0,0 +1,334 @@
+/**************************************************************************/
+/* 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) {
+ static const std::string operators = "<>=+-*/";
+ return operators.find(c) != std::string::npos;
+}
+
+// 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/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..5ca03a20af 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,7 @@
#include "tests/scene/test_theme.h"
#include "tests/scene/test_viewport.h"
#include "tests/scene/test_visual_shader.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 a918acbe77..4bb1e8e1d1 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -315,14 +315,14 @@ Files extracted from upstream source:
## libpng
- Upstream: http://libpng.org/pub/png/libpng.html
-- Version: 1.6.38 (0a158f3506502dfa23edfc42790dfaed82efba17, 2022)
+- Version: 1.6.40 (f135775ad4e5d4408d2e12ffcc71bb36e6b48551, 2023)
- License: libpng/zlib
Files extracted from upstream source:
- all .c and .h files of the main directory, except from
`example.c` and `pngtest.c`
-- the arm/ folder
+- `arm/`, `intel/` and `powerpc/` folders
- `scripts/pnglibconf.h.prebuilt` as `pnglibconf.h`
- `LICENSE`
@@ -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:
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/intel/filter_sse2_intrinsics.c b/thirdparty/libpng/intel/filter_sse2_intrinsics.c
new file mode 100644
index 0000000000..d3c0fe9e2d
--- /dev/null
+++ b/thirdparty/libpng/intel/filter_sse2_intrinsics.c
@@ -0,0 +1,391 @@
+
+/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2016-2017 Glenn Randers-Pehrson
+ * Written by Mike Klein and Matt Sarett
+ * Derived from arm/filter_neon_intrinsics.c
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+#if PNG_INTEL_SSE_IMPLEMENTATION > 0
+
+#include <immintrin.h>
+
+/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
+ * They're positioned like this:
+ * prev: c b
+ * row: a d
+ * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
+ * whichever of a, b, or c is closest to p=a+b-c.
+ */
+
+static __m128i load4(const void* p) {
+ int tmp;
+ memcpy(&tmp, p, sizeof(tmp));
+ return _mm_cvtsi32_si128(tmp);
+}
+
+static void store4(void* p, __m128i v) {
+ int tmp = _mm_cvtsi128_si32(v);
+ memcpy(p, &tmp, sizeof(int));
+}
+
+static __m128i load3(const void* p) {
+ png_uint_32 tmp = 0;
+ memcpy(&tmp, p, 3);
+ return _mm_cvtsi32_si128(tmp);
+}
+
+static void store3(void* p, __m128i v) {
+ int tmp = _mm_cvtsi128_si32(v);
+ memcpy(p, &tmp, 3);
+}
+
+void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* The Sub filter predicts each pixel as the previous pixel, a.
+ * There is no pixel to the left of the first pixel. It's encoded directly.
+ * That works with our main loop if we just say that left pixel was zero.
+ */
+ size_t rb;
+
+ __m128i a, d = _mm_setzero_si128();
+
+ png_debug(1, "in png_read_filter_row_sub3_sse2");
+
+ rb = row_info->rowbytes;
+ while (rb >= 4) {
+ a = d; d = load4(row);
+ d = _mm_add_epi8(d, a);
+ store3(row, d);
+
+ row += 3;
+ rb -= 3;
+ }
+ if (rb > 0) {
+ a = d; d = load3(row);
+ d = _mm_add_epi8(d, a);
+ store3(row, d);
+
+ row += 3;
+ rb -= 3;
+ }
+ PNG_UNUSED(prev)
+}
+
+void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* The Sub filter predicts each pixel as the previous pixel, a.
+ * There is no pixel to the left of the first pixel. It's encoded directly.
+ * That works with our main loop if we just say that left pixel was zero.
+ */
+ size_t rb;
+
+ __m128i a, d = _mm_setzero_si128();
+
+ png_debug(1, "in png_read_filter_row_sub4_sse2");
+
+ rb = row_info->rowbytes+4;
+ while (rb > 4) {
+ a = d; d = load4(row);
+ d = _mm_add_epi8(d, a);
+ store4(row, d);
+
+ row += 4;
+ rb -= 4;
+ }
+ PNG_UNUSED(prev)
+}
+
+void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* The Avg filter predicts each pixel as the (truncated) average of a and b.
+ * There's no pixel to the left of the first pixel. Luckily, it's
+ * predicted to be half of the pixel above it. So again, this works
+ * perfectly with our loop if we make sure a starts at zero.
+ */
+
+ size_t rb;
+
+ const __m128i zero = _mm_setzero_si128();
+
+ __m128i b;
+ __m128i a, d = zero;
+
+ png_debug(1, "in png_read_filter_row_avg3_sse2");
+ rb = row_info->rowbytes;
+ while (rb >= 4) {
+ __m128i avg;
+ b = load4(prev);
+ a = d; d = load4(row );
+
+ /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+ avg = _mm_avg_epu8(a,b);
+ /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+ _mm_set1_epi8(1)));
+ d = _mm_add_epi8(d, avg);
+ store3(row, d);
+
+ prev += 3;
+ row += 3;
+ rb -= 3;
+ }
+ if (rb > 0) {
+ __m128i avg;
+ b = load3(prev);
+ a = d; d = load3(row );
+
+ /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+ avg = _mm_avg_epu8(a,b);
+ /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+ _mm_set1_epi8(1)));
+
+ d = _mm_add_epi8(d, avg);
+ store3(row, d);
+
+ prev += 3;
+ row += 3;
+ rb -= 3;
+ }
+}
+
+void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* The Avg filter predicts each pixel as the (truncated) average of a and b.
+ * There's no pixel to the left of the first pixel. Luckily, it's
+ * predicted to be half of the pixel above it. So again, this works
+ * perfectly with our loop if we make sure a starts at zero.
+ */
+ size_t rb;
+ const __m128i zero = _mm_setzero_si128();
+ __m128i b;
+ __m128i a, d = zero;
+
+ png_debug(1, "in png_read_filter_row_avg4_sse2");
+
+ rb = row_info->rowbytes+4;
+ while (rb > 4) {
+ __m128i avg;
+ b = load4(prev);
+ a = d; d = load4(row );
+
+ /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
+ avg = _mm_avg_epu8(a,b);
+ /* ...but we can fix it up by subtracting off 1 if it rounded up. */
+ avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
+ _mm_set1_epi8(1)));
+
+ d = _mm_add_epi8(d, avg);
+ store4(row, d);
+
+ prev += 4;
+ row += 4;
+ rb -= 4;
+ }
+}
+
+/* Returns |x| for 16-bit lanes. */
+static __m128i abs_i16(__m128i x) {
+#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
+ return _mm_abs_epi16(x);
+#else
+ /* Read this all as, return x<0 ? -x : x.
+ * To negate two's complement, you flip all the bits then add 1.
+ */
+ __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
+
+ /* Flip negative lanes. */
+ x = _mm_xor_si128(x, is_negative);
+
+ /* +1 to negative lanes, else +0. */
+ x = _mm_sub_epi16(x, is_negative);
+ return x;
+#endif
+}
+
+/* Bytewise c ? t : e. */
+static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
+#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
+ return _mm_blendv_epi8(e,t,c);
+#else
+ return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
+#endif
+}
+
+void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* Paeth tries to predict pixel d using the pixel to the left of it, a,
+ * and two pixels from the previous row, b and c:
+ * prev: c b
+ * row: a d
+ * The Paeth function predicts d to be whichever of a, b, or c is nearest to
+ * p=a+b-c.
+ *
+ * The first pixel has no left context, and so uses an Up filter, p = b.
+ * This works naturally with our main loop's p = a+b-c if we force a and c
+ * to zero.
+ * Here we zero b and d, which become c and a respectively at the start of
+ * the loop.
+ */
+ size_t rb;
+ const __m128i zero = _mm_setzero_si128();
+ __m128i c, b = zero,
+ a, d = zero;
+
+ png_debug(1, "in png_read_filter_row_paeth3_sse2");
+
+ rb = row_info->rowbytes;
+ while (rb >= 4) {
+ /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+ * intermediates.
+ */
+ __m128i pa,pb,pc,smallest,nearest;
+ c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
+ a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
+
+ /* (p-a) == (a+b-c - a) == (b-c) */
+
+ pa = _mm_sub_epi16(b,c);
+
+ /* (p-b) == (a+b-c - b) == (a-c) */
+ pb = _mm_sub_epi16(a,c);
+
+ /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+ pc = _mm_add_epi16(pa,pb);
+
+ pa = abs_i16(pa); /* |p-a| */
+ pb = abs_i16(pb); /* |p-b| */
+ pc = abs_i16(pc); /* |p-c| */
+
+ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+ /* Paeth breaks ties favoring a over b over c. */
+ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+ if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+ c));
+
+ /* Note `_epi8`: we need addition to wrap modulo 255. */
+ d = _mm_add_epi8(d, nearest);
+ store3(row, _mm_packus_epi16(d,d));
+
+ prev += 3;
+ row += 3;
+ rb -= 3;
+ }
+ if (rb > 0) {
+ /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+ * intermediates.
+ */
+ __m128i pa,pb,pc,smallest,nearest;
+ c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
+ a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
+
+ /* (p-a) == (a+b-c - a) == (b-c) */
+ pa = _mm_sub_epi16(b,c);
+
+ /* (p-b) == (a+b-c - b) == (a-c) */
+ pb = _mm_sub_epi16(a,c);
+
+ /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+ pc = _mm_add_epi16(pa,pb);
+
+ pa = abs_i16(pa); /* |p-a| */
+ pb = abs_i16(pb); /* |p-b| */
+ pc = abs_i16(pc); /* |p-c| */
+
+ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+ /* Paeth breaks ties favoring a over b over c. */
+ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+ if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+ c));
+
+ /* Note `_epi8`: we need addition to wrap modulo 255. */
+ d = _mm_add_epi8(d, nearest);
+ store3(row, _mm_packus_epi16(d,d));
+
+ prev += 3;
+ row += 3;
+ rb -= 3;
+ }
+}
+
+void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev)
+{
+ /* Paeth tries to predict pixel d using the pixel to the left of it, a,
+ * and two pixels from the previous row, b and c:
+ * prev: c b
+ * row: a d
+ * The Paeth function predicts d to be whichever of a, b, or c is nearest to
+ * p=a+b-c.
+ *
+ * The first pixel has no left context, and so uses an Up filter, p = b.
+ * This works naturally with our main loop's p = a+b-c if we force a and c
+ * to zero.
+ * Here we zero b and d, which become c and a respectively at the start of
+ * the loop.
+ */
+ size_t rb;
+ const __m128i zero = _mm_setzero_si128();
+ __m128i pa,pb,pc,smallest,nearest;
+ __m128i c, b = zero,
+ a, d = zero;
+
+ png_debug(1, "in png_read_filter_row_paeth4_sse2");
+
+ rb = row_info->rowbytes+4;
+ while (rb > 4) {
+ /* It's easiest to do this math (particularly, deal with pc) with 16-bit
+ * intermediates.
+ */
+ c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
+ a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
+
+ /* (p-a) == (a+b-c - a) == (b-c) */
+ pa = _mm_sub_epi16(b,c);
+
+ /* (p-b) == (a+b-c - b) == (a-c) */
+ pb = _mm_sub_epi16(a,c);
+
+ /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
+ pc = _mm_add_epi16(pa,pb);
+
+ pa = abs_i16(pa); /* |p-a| */
+ pb = abs_i16(pb); /* |p-b| */
+ pc = abs_i16(pc); /* |p-c| */
+
+ smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
+
+ /* Paeth breaks ties favoring a over b over c. */
+ nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
+ if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
+ c));
+
+ /* Note `_epi8`: we need addition to wrap modulo 255. */
+ d = _mm_add_epi8(d, nearest);
+ store4(row, _mm_packus_epi16(d,d));
+
+ prev += 4;
+ row += 4;
+ rb -= 4;
+ }
+}
+
+#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
+#endif /* READ */
diff --git a/thirdparty/libpng/intel/intel_init.c b/thirdparty/libpng/intel/intel_init.c
new file mode 100644
index 0000000000..2f8168b7c4
--- /dev/null
+++ b/thirdparty/libpng/intel/intel_init.c
@@ -0,0 +1,52 @@
+
+/* intel_init.c - SSE2 optimized filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2016-2017 Glenn Randers-Pehrson
+ * Written by Mike Klein and Matt Sarett, Google, Inc.
+ * Derived from arm/arm_init.c
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+#if PNG_INTEL_SSE_IMPLEMENTATION > 0
+
+void
+png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
+{
+ /* The techniques used to implement each of these filters in SSE operate on
+ * one pixel at a time.
+ * So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
+ * They can scale up to 6 and 8 bpp images and down to 2 bpp images,
+ * but they'd not likely have any benefit for 1bpp images.
+ * Most of these can be implemented using only MMX and 64-bit registers,
+ * but they end up a bit slower than using the equally-ubiquitous SSE2.
+ */
+ png_debug(1, "in png_init_filter_functions_sse2");
+ if (bpp == 3)
+ {
+ pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
+ pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
+ pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+ png_read_filter_row_paeth3_sse2;
+ }
+ else if (bpp == 4)
+ {
+ pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
+ pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
+ pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+ png_read_filter_row_paeth4_sse2;
+ }
+
+ /* No need optimize PNG_FILTER_VALUE_UP. The compiler should
+ * autovectorize.
+ */
+}
+
+#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
+#endif /* PNG_READ_SUPPORTED */
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/libpng/powerpc/filter_vsx_intrinsics.c b/thirdparty/libpng/powerpc/filter_vsx_intrinsics.c
new file mode 100644
index 0000000000..01cf8800dc
--- /dev/null
+++ b/thirdparty/libpng/powerpc/filter_vsx_intrinsics.c
@@ -0,0 +1,768 @@
+/* filter_vsx_intrinsics.c - PowerPC optimised filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2017 Glenn Randers-Pehrson
+ * Written by Vadim Barkov, 2017.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* This code requires -maltivec and -mvsx on the command line: */
+#if PNG_POWERPC_VSX_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
+
+#include <altivec.h>
+
+#if PNG_POWERPC_VSX_OPT > 0
+
+#ifndef __VSX__
+# error "This code requires VSX support (POWER7 and later). Please provide -mvsx compiler flag."
+#endif
+
+#define vec_ld_unaligned(vec,data) vec = vec_vsx_ld(0,data)
+#define vec_st_unaligned(vec,data) vec_vsx_st(vec,0,data)
+
+
+/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
+ * They're positioned like this:
+ * prev: c b
+ * row: a d
+ * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
+ * whichever of a, b, or c is closest to p=a+b-c.
+ * ( this is taken from ../intel/filter_sse2_intrinsics.c )
+ */
+
+#define vsx_declare_common_vars(row_info,row,prev_row,offset) \
+ png_byte i;\
+ png_bytep rp = row + offset;\
+ png_const_bytep pp = prev_row;\
+ size_t unaligned_top = 16 - (((size_t)rp % 16));\
+ size_t istop;\
+ if(unaligned_top == 16)\
+ unaligned_top = 0;\
+ istop = row_info->rowbytes;\
+ if((unaligned_top < istop))\
+ istop -= unaligned_top;\
+ else{\
+ unaligned_top = istop;\
+ istop = 0;\
+ }
+
+void png_read_filter_row_up_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ vector unsigned char rp_vec;
+ vector unsigned char pp_vec;
+ vsx_declare_common_vars(row_info,row,prev_row,0)
+
+ /* Altivec operations require 16-byte aligned data
+ * but input can be unaligned. So we calculate
+ * unaligned part as usual.
+ */
+ for (i = 0; i < unaligned_top; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+ rp++;
+ }
+
+ /* Using SIMD while we can */
+ while( istop >= 16 )
+ {
+ rp_vec = vec_ld(0,rp);
+ vec_ld_unaligned(pp_vec,pp);
+
+ rp_vec = vec_add(rp_vec,pp_vec);
+
+ vec_st(rp_vec,0,rp);
+
+ pp += 16;
+ rp += 16;
+ istop -= 16;
+ }
+
+ if(istop > 0)
+ {
+ /* If byte count of row is not divisible by 16
+ * we will process remaining part as usual
+ */
+ for (i = 0; i < istop; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+ rp++;
+ }
+}
+
+}
+
+static const vector unsigned char VSX_LEFTSHIFTED1_4 = {16,16,16,16, 0, 1, 2, 3,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_LEFTSHIFTED2_4 = {16,16,16,16,16,16,16,16, 4, 5, 6, 7,16,16,16,16};
+static const vector unsigned char VSX_LEFTSHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 8, 9,10,11};
+
+static const vector unsigned char VSX_LEFTSHIFTED1_3 = {16,16,16, 0, 1, 2,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_LEFTSHIFTED2_3 = {16,16,16,16,16,16, 3, 4, 5,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_LEFTSHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 6, 7, 8,16,16,16,16};
+static const vector unsigned char VSX_LEFTSHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 9,10,11,16};
+
+static const vector unsigned char VSX_NOT_SHIFTED1_4 = {16,16,16,16, 4, 5, 6, 7,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_NOT_SHIFTED2_4 = {16,16,16,16,16,16,16,16, 8, 9,10,11,16,16,16,16};
+static const vector unsigned char VSX_NOT_SHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,15};
+
+static const vector unsigned char VSX_NOT_SHIFTED1_3 = {16,16,16, 3, 4, 5,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_NOT_SHIFTED2_3 = {16,16,16,16,16,16, 6, 7, 8,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_NOT_SHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 9,10,11,16,16,16,16};
+static const vector unsigned char VSX_NOT_SHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,16};
+
+static const vector unsigned char VSX_CHAR_ZERO = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+#ifdef __LITTLE_ENDIAN__
+
+static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = { 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = { 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {12,16,13,16,14,16,15,16,16,16,16,16,16,16,16,16};
+
+static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 0, 2, 4, 6,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 0, 2, 4, 6,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4, 6};
+
+static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = { 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = { 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = { 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {12,16,13,16,14,16,16,16,16,16,16,16,16,16,16,16};
+
+static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 0, 2, 4,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 0, 2, 4,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 0, 2, 4,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4,16};
+
+#elif defined(__BIG_ENDIAN__)
+
+static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = {16, 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = {16, 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {16,12,16,13,16,14,16,15,16,16,16,16,16,16,16,16};
+
+static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 1, 3, 5, 7,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 1, 3, 5, 7,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5, 7};
+
+static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = {16, 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = {16, 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = {16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {16,12,16,13,16,14,16,16,16,16,16,16,16,16,16,16};
+
+static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 1, 3, 5,16,16,16,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 1, 3, 5,16,16,16,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 1, 3, 5,16,16,16,16};
+static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5,16};
+
+#endif
+
+#define vsx_char_to_short(vec,offset,bpp) (vector unsigned short)vec_perm((vec),VSX_CHAR_ZERO,VSX_CHAR_TO_SHORT##offset##_##bpp)
+#define vsx_short_to_char(vec,offset,bpp) vec_perm(((vector unsigned char)(vec)),VSX_CHAR_ZERO,VSX_SHORT_TO_CHAR##offset##_##bpp)
+
+#ifdef PNG_USE_ABS
+# define vsx_abs(number) abs(number)
+#else
+# define vsx_abs(number) (number > 0) ? (number) : -(number)
+#endif
+
+void png_read_filter_row_sub4_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 4;
+
+ vector unsigned char rp_vec;
+ vector unsigned char part_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+
+ PNG_UNUSED(pp)
+
+ /* Altivec operations require 16-byte aligned data
+ * but input can be unaligned. So we calculate
+ * unaligned part as usual.
+ */
+ for (i = 0; i < unaligned_top; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+
+ /* Using SIMD while we can */
+ while( istop >= 16 )
+ {
+ for(i=0;i < bpp ; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+ rp -= bpp;
+
+ rp_vec = vec_ld(0,rp);
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ vec_st(rp_vec,0,rp);
+
+ rp += 16;
+ istop -= 16;
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp - bpp))) & 0xff);
+ rp++;
+ }
+
+}
+
+void png_read_filter_row_sub3_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 3;
+
+ vector unsigned char rp_vec;
+ vector unsigned char part_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+
+ PNG_UNUSED(pp)
+
+ /* Altivec operations require 16-byte aligned data
+ * but input can be unaligned. So we calculate
+ * unaligned part as usual.
+ */
+ for (i = 0; i < unaligned_top; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+
+ /* Using SIMD while we can */
+ while( istop >= 16 )
+ {
+ for(i=0;i < bpp ; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+ rp -= bpp;
+
+ rp_vec = vec_ld(0,rp);
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3);
+ rp_vec = vec_add(rp_vec,part_vec);
+
+ vec_st(rp_vec,0,rp);
+ rp += 15;
+ istop -= 16;
+
+ /* Since 16 % bpp = 16 % 3 = 1, last element of array must
+ * be proceeded manually
+ */
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+ rp++;
+ }
+}
+
+void png_read_filter_row_avg4_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 4;
+
+ vector unsigned char rp_vec;
+ vector unsigned char pp_vec;
+ vector unsigned char pp_part_vec;
+ vector unsigned char rp_part_vec;
+ vector unsigned char avg_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+ rp -= bpp;
+ if(istop >= bpp)
+ istop -= bpp;
+
+ for (i = 0; i < bpp; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ ((int)(*pp++) / 2 )) & 0xff);
+
+ rp++;
+ }
+
+ /* Altivec operations require 16-byte aligned data
+ * but input can be unaligned. So we calculate
+ * unaligned part as usual.
+ */
+ for (i = 0; i < unaligned_top; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+
+ /* Using SIMD while we can */
+ while( istop >= 16 )
+ {
+ for(i=0;i < bpp ; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+ rp -= bpp;
+ pp -= bpp;
+
+ vec_ld_unaligned(pp_vec,pp);
+ rp_vec = vec_ld(0,rp);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_4);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_4);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_4);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ vec_st(rp_vec,0,rp);
+
+ rp += 16;
+ pp += 16;
+ istop -= 16;
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+}
+
+void png_read_filter_row_avg3_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 3;
+
+ vector unsigned char rp_vec;
+ vector unsigned char pp_vec;
+ vector unsigned char pp_part_vec;
+ vector unsigned char rp_part_vec;
+ vector unsigned char avg_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+ rp -= bpp;
+ if(istop >= bpp)
+ istop -= bpp;
+
+ for (i = 0; i < bpp; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ ((int)(*pp++) / 2 )) & 0xff);
+
+ rp++;
+ }
+
+ /* Altivec operations require 16-byte aligned data
+ * but input can be unaligned. So we calculate
+ * unaligned part as usual.
+ */
+ for (i = 0; i < unaligned_top; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+
+ /* Using SIMD while we can */
+ while( istop >= 16 )
+ {
+ for(i=0;i < bpp ; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+ rp -= bpp;
+ pp -= bpp;
+
+ vec_ld_unaligned(pp_vec,pp);
+ rp_vec = vec_ld(0,rp);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_3);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_3);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_3);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3);
+ pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED4_3);
+ avg_vec = vec_avg(rp_part_vec,pp_part_vec);
+ avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1)));
+ rp_vec = vec_add(rp_vec,avg_vec);
+
+ vec_st(rp_vec,0,rp);
+
+ rp += 15;
+ pp += 15;
+ istop -= 16;
+
+ /* Since 16 % bpp = 16 % 3 = 1, last element of array must
+ * be proceeded manually
+ */
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+ rp++;
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+ rp++;
+ }
+}
+
+/* Bytewise c ? t : e. */
+#define if_then_else(c,t,e) vec_sel(e,t,c)
+
+#define vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) {\
+ c = *(pp - bpp);\
+ a = *(rp - bpp);\
+ b = *pp++;\
+ p = b - c;\
+ pc = a - c;\
+ pa = vsx_abs(p);\
+ pb = vsx_abs(pc);\
+ pc = vsx_abs(p + pc);\
+ if (pb < pa) pa = pb, a = b;\
+ if (pc < pa) a = c;\
+ a += *rp;\
+ *rp++ = (png_byte)a;\
+ }
+
+void png_read_filter_row_paeth4_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 4;
+
+ int a, b, c, pa, pb, pc, p;
+ vector unsigned char rp_vec;
+ vector unsigned char pp_vec;
+ vector unsigned short a_vec,b_vec,c_vec,nearest_vec;
+ vector signed short pa_vec,pb_vec,pc_vec,smallest_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+ rp -= bpp;
+ if(istop >= bpp)
+ istop -= bpp;
+
+ /* Process the first pixel in the row completely (this is the same as 'up'
+ * because there is only one candidate predictor for the first row).
+ */
+ for(i = 0; i < bpp ; i++)
+ {
+ *rp = (png_byte)( *rp + *pp);
+ rp++;
+ pp++;
+ }
+
+ for(i = 0; i < unaligned_top ; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+
+ while( istop >= 16)
+ {
+ for(i = 0; i < bpp ; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+
+ rp -= bpp;
+ pp -= bpp;
+ rp_vec = vec_ld(0,rp);
+ vec_ld_unaligned(pp_vec,pp);
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_4),1,4);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,4)));
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_4),2,4);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,4)));
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_4),3,4);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,4)));
+
+ vec_st(rp_vec,0,rp);
+
+ rp += 16;
+ pp += 16;
+ istop -= 16;
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+}
+
+void png_read_filter_row_paeth3_vsx(png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row)
+{
+ png_byte bpp = 3;
+
+ int a, b, c, pa, pb, pc, p;
+ vector unsigned char rp_vec;
+ vector unsigned char pp_vec;
+ vector unsigned short a_vec,b_vec,c_vec,nearest_vec;
+ vector signed short pa_vec,pb_vec,pc_vec,smallest_vec;
+
+ vsx_declare_common_vars(row_info,row,prev_row,bpp)
+ rp -= bpp;
+ if(istop >= bpp)
+ istop -= bpp;
+
+ /* Process the first pixel in the row completely (this is the same as 'up'
+ * because there is only one candidate predictor for the first row).
+ */
+ for(i = 0; i < bpp ; i++)
+ {
+ *rp = (png_byte)( *rp + *pp);
+ rp++;
+ pp++;
+ }
+
+ for(i = 0; i < unaligned_top ; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+
+ while( istop >= 16)
+ {
+ for(i = 0; i < bpp ; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+
+ rp -= bpp;
+ pp -= bpp;
+ rp_vec = vec_ld(0,rp);
+ vec_ld_unaligned(pp_vec,pp);
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_3),1,3);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,3)));
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_3),2,3);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,3)));
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_3),3,3);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,3)));
+
+ a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3);
+ b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED4_3),4,3);
+ c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3);
+ pa_vec = (vector signed short) vec_sub(b_vec,c_vec);
+ pb_vec = (vector signed short) vec_sub(a_vec , c_vec);
+ pc_vec = vec_add(pa_vec,pb_vec);
+ pa_vec = vec_abs(pa_vec);
+ pb_vec = vec_abs(pb_vec);
+ pc_vec = vec_abs(pc_vec);
+ smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec));
+ nearest_vec = if_then_else(
+ vec_cmpeq(pa_vec,smallest_vec),
+ a_vec,
+ if_then_else(
+ vec_cmpeq(pb_vec,smallest_vec),
+ b_vec,
+ c_vec
+ )
+ );
+ rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,4,3)));
+
+ vec_st(rp_vec,0,rp);
+
+ rp += 15;
+ pp += 15;
+ istop -= 16;
+
+ /* Since 16 % bpp = 16 % 3 = 1, last element of array must
+ * be proceeded manually
+ */
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+
+ if(istop > 0)
+ for (i = 0; i < istop % 16; i++)
+ {
+ vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp)
+ }
+}
+
+#endif /* PNG_POWERPC_VSX_OPT > 0 */
+#endif /* PNG_POWERPC_VSX_IMPLEMENTATION == 1 (intrinsics) */
+#endif /* READ */
diff --git a/thirdparty/libpng/powerpc/powerpc_init.c b/thirdparty/libpng/powerpc/powerpc_init.c
new file mode 100644
index 0000000000..54426c558e
--- /dev/null
+++ b/thirdparty/libpng/powerpc/powerpc_init.c
@@ -0,0 +1,126 @@
+
+/* powerpc_init.c - POWERPC optimised filter functions
+ *
+ * Copyright (c) 2018 Cosmin Truta
+ * Copyright (c) 2017 Glenn Randers-Pehrson
+ * Written by Vadim Barkov, 2017.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
+ * called.
+ */
+#define _POSIX_SOURCE 1
+
+#include <stdio.h>
+#include "../pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+#if PNG_POWERPC_VSX_OPT > 0
+#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED /* Do run-time checks */
+/* WARNING: it is strongly recommended that you do not build libpng with
+ * run-time checks for CPU features if at all possible. In the case of the PowerPC
+ * VSX instructions there is no processor-specific way of detecting the
+ * presence of the required support, therefore run-time detection is extremely
+ * OS specific.
+ *
+ * You may set the macro PNG_POWERPC_VSX_FILE to the file name of file containing
+ * a fragment of C source code which defines the png_have_vsx function. There
+ * are a number of implementations in contrib/powerpc-vsx, but the only one that
+ * has partial support is contrib/powerpc-vsx/linux.c - a generic Linux
+ * implementation which reads /proc/cpufino.
+ */
+#ifndef PNG_POWERPC_VSX_FILE
+# ifdef __linux__
+# define PNG_POWERPC_VSX_FILE "contrib/powerpc-vsx/linux_aux.c"
+# endif
+#endif
+
+#ifdef PNG_POWERPC_VSX_FILE
+
+#include <signal.h> /* for sig_atomic_t */
+static int png_have_vsx(png_structp png_ptr);
+#include PNG_POWERPC_VSX_FILE
+
+#else /* PNG_POWERPC_VSX_FILE */
+# error "PNG_POWERPC_VSX_FILE undefined: no support for run-time POWERPC VSX checks"
+#endif /* PNG_POWERPC_VSX_FILE */
+#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */
+
+void
+png_init_filter_functions_vsx(png_structp pp, unsigned int bpp)
+{
+ /* The switch statement is compiled in for POWERPC_VSX_API, the call to
+ * png_have_vsx is compiled in for POWERPC_VSX_CHECK. If both are defined
+ * the check is only performed if the API has not set the PowerPC option on
+ * or off explicitly. In this case the check controls what happens.
+ */
+
+#ifdef PNG_POWERPC_VSX_API_SUPPORTED
+ switch ((pp->options >> PNG_POWERPC_VSX) & 3)
+ {
+ case PNG_OPTION_UNSET:
+ /* Allow the run-time check to execute if it has been enabled -
+ * thus both API and CHECK can be turned on. If it isn't supported
+ * this case will fall through to the 'default' below, which just
+ * returns.
+ */
+#endif /* PNG_POWERPC_VSX_API_SUPPORTED */
+#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED
+ {
+ static volatile sig_atomic_t no_vsx = -1; /* not checked */
+
+ if (no_vsx < 0)
+ no_vsx = !png_have_vsx(pp);
+
+ if (no_vsx)
+ return;
+ }
+#ifdef PNG_POWERPC_VSX_API_SUPPORTED
+ break;
+#endif
+#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */
+
+#ifdef PNG_POWERPC_VSX_API_SUPPORTED
+ default: /* OFF or INVALID */
+ return;
+
+ case PNG_OPTION_ON:
+ /* Option turned on */
+ break;
+ }
+#endif
+
+ /* IMPORTANT: any new internal functions used here must be declared using
+ * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the
+ * 'prefix' option to configure works:
+ *
+ * ./configure --with-libpng-prefix=foobar_
+ *
+ * Verify you have got this right by running the above command, doing a build
+ * and examining pngprefix.h; it must contain a #define for every external
+ * function you add. (Notice that this happens automatically for the
+ * initialization function.)
+ */
+ pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_vsx;
+
+ if (bpp == 3)
+ {
+ pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_vsx;
+ pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_vsx;
+ pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_vsx;
+ }
+
+ else if (bpp == 4)
+ {
+ pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_vsx;
+ pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_vsx;
+ pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_vsx;
+ }
+}
+#endif /* PNG_POWERPC_VSX_OPT > 0 */
+#endif /* READ */
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__ */