From b8fb6a944678f05d2db8c037ec1effc5c4398724 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Thu, 16 Jan 2025 14:14:15 -0800 Subject: [PATCH] git subrepo pull (merge) --force --branch=groups-4.4.2025-01-16T221116Z godot subrepo: subdir: "godot" merged: "b09245232d" upstream: origin: "https://github.com/V-Sekai/godot.git" branch: "groups-4.4.2025-01-16T221116Z" commit: "b09245232d" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "cce3d93" --- godot/.gitrepo | 6 +- godot/core/debugger/remote_debugger_peer.cpp | 4 +- godot/core/io/image.cpp | 93 ++++++++- godot/core/io/image.h | 1 + godot/core/os/memory.cpp | 4 - godot/core/typedefs.h | 6 + godot/doc/classes/DisplayServer.xml | 4 +- .../doc/classes/EditorSceneFormatImporter.xml | 5 - godot/doc/classes/NavigationRegion2D.xml | 6 + godot/doc/classes/NavigationRegion3D.xml | 6 + godot/doc/classes/NavigationServer2D.xml | 7 + godot/doc/classes/NavigationServer3D.xml | 7 + godot/doc/classes/ParticleProcessMaterial.xml | 8 +- godot/doc/classes/RenderingDevice.xml | 27 +++ godot/doc/classes/RenderingServer.xml | 24 +++ godot/doc/classes/SpringBoneSimulator3D.xml | 17 +- .../d3d12/rendering_device_driver_d3d12.cpp | 7 + .../d3d12/rendering_device_driver_d3d12.h | 1 + godot/drivers/gles3/storage/mesh_storage.cpp | 6 +- godot/drivers/gles3/storage/mesh_storage.h | 3 +- .../metal/rendering_device_driver_metal.h | 1 + .../metal/rendering_device_driver_metal.mm | 11 ++ .../vulkan/rendering_device_driver_vulkan.cpp | 46 ++++- .../vulkan/rendering_device_driver_vulkan.h | 2 + godot/drivers/wasapi/audio_driver_wasapi.cpp | 93 +++++++-- godot/editor/animation_track_editor.cpp | 48 +++-- godot/editor/editor_file_system.cpp | 2 +- godot/editor/editor_help.cpp | 85 +++++++- godot/editor/editor_help.h | 1 + godot/editor/editor_node.cpp | 15 +- godot/editor/editor_node.h | 1 + godot/editor/editor_settings.cpp | 7 +- .../editor/export/editor_export_platform.cpp | 2 +- godot/editor/icons/TexturePreviewChannels.svg | 1 + .../import/3d/editor_import_collada.cpp | 4 - .../editor/import/3d/editor_import_collada.h | 1 - .../import/3d/resource_importer_obj.cpp | 4 - .../editor/import/3d/resource_importer_obj.h | 1 - .../import/3d/resource_importer_scene.cpp | 52 ++--- .../import/3d/resource_importer_scene.h | 3 - .../editor/plugins/color_channel_selector.cpp | 146 ++++++++++++++ godot/editor/plugins/color_channel_selector.h | 65 ++++++ .../gizmos/spring_bone_3d_gizmo_plugin.cpp | 4 +- godot/editor/plugins/script_text_editor.cpp | 9 +- .../plugins/sprite_frames_editor_plugin.cpp | 30 +++ .../plugins/sprite_frames_editor_plugin.h | 10 + godot/editor/plugins/text_editor.cpp | 2 +- godot/editor/plugins/text_shader_editor.cpp | 2 +- .../plugins/texture_3d_editor_plugin.cpp | 55 +++++- .../editor/plugins/texture_3d_editor_plugin.h | 6 + .../editor/plugins/texture_editor_plugin.cpp | 126 ++++++++++-- godot/editor/plugins/texture_editor_plugin.h | 8 + .../plugins/texture_layered_editor_plugin.cpp | 101 +++++++++- .../plugins/texture_layered_editor_plugin.h | 6 + godot/editor/progress_dialog.cpp | 89 ++++++--- godot/editor/progress_dialog.h | 15 +- godot/editor/project_manager.cpp | 21 +- godot/editor/project_manager.h | 4 +- .../project_manager/quick_settings_dialog.cpp | 1 + godot/editor/window_wrapper.cpp | 7 + godot/editor/window_wrapper.h | 2 + godot/main/main.cpp | 6 +- .../godot_ios.xcodeproj/project.pbxproj | 4 + .../4.3-stable.expected | 24 +++ .../editor/editor_scene_importer_fbx2gltf.cpp | 4 - .../editor/editor_scene_importer_fbx2gltf.h | 1 - .../fbx/editor/editor_scene_importer_ufbx.cpp | 4 - .../fbx/editor/editor_scene_importer_ufbx.h | 1 - .../editor/editor_scene_importer_blend.cpp | 4 - .../gltf/editor/editor_scene_importer_blend.h | 1 - .../editor/editor_scene_importer_gltf.cpp | 4 - .../gltf/editor/editor_scene_importer_gltf.h | 1 - godot/modules/gltf/extensions/gltf_light.cpp | 24 ++- .../gridmap/editor/grid_map_editor_plugin.cpp | 3 + godot/modules/jsonrpc/jsonrpc.cpp | 5 + godot/modules/ktx/texture_loader_ktx.cpp | 34 ++-- .../2d/godot_navigation_server_2d.cpp | 12 ++ .../2d/godot_navigation_server_2d.h | 1 + .../3d/godot_navigation_server_3d.cpp | 7 + .../3d/godot_navigation_server_3d.h | 1 + godot/modules/regex/regex.cpp | 2 +- godot/modules/websocket/emws_peer.cpp | 2 +- godot/platform/android/SCsub | 1 + godot/platform/android/dialog_utils_jni.cpp | 52 +++++ godot/platform/android/dialog_utils_jni.h | 41 ++++ .../android/display_server_android.cpp | 15 +- .../platform/android/display_server_android.h | 5 + .../platform/android/java/editor/build.gradle | 2 +- .../lib/res/mipmap-hdpi/icon_monochrome.png | Bin 0 -> 2716 bytes .../lib/res/mipmap-mdpi/icon_monochrome.png | Bin 0 -> 1697 bytes .../lib/res/mipmap-xhdpi/icon_monochrome.png | Bin 0 -> 3615 bytes .../lib/res/mipmap-xxhdpi/icon_monochrome.png | Bin 0 -> 5452 bytes .../res/mipmap-xxxhdpi/icon_monochrome.png | Bin 0 -> 7605 bytes .../java/lib/res/mipmap/icon_monochrome.png | Bin 5617 -> 1697 bytes .../android/java/lib/res/values/dimens.xml | 6 +- .../lib/src/org/godotengine/godot/Godot.kt | 38 ++-- .../src/org/godotengine/godot/GodotLib.java | 5 - .../godotengine/godot/utils/DialogUtils.kt | 185 ++++++++++++++++++ godot/platform/android/java_godot_lib_jni.cpp | 8 - godot/platform/android/java_godot_lib_jni.h | 1 - godot/platform/android/java_godot_wrapper.cpp | 23 +++ godot/platform/android/java_godot_wrapper.h | 2 + godot/platform/ios/export/export_plugin.cpp | 2 +- godot/platform/ios/ios.mm | 2 +- .../wayland/display_server_wayland.cpp | 1 + .../linuxbsd/x11/display_server_x11.cpp | 1 + .../windows/display_server_windows.cpp | 1 + godot/scene/2d/navigation_region_2d.cpp | 28 +++ godot/scene/2d/navigation_region_2d.h | 5 + godot/scene/3d/gpu_particles_3d.cpp | 1 - godot/scene/3d/navigation_region_3d.cpp | 27 +++ godot/scene/3d/navigation_region_3d.h | 5 + godot/scene/3d/spring_bone_simulator_3d.cpp | 21 +- godot/scene/3d/spring_bone_simulator_3d.h | 2 - godot/scene/gui/color_picker.cpp | 59 +++--- godot/scene/gui/rich_text_label.cpp | 2 +- godot/scene/gui/tab_bar.cpp | 4 +- .../resources/particle_process_material.cpp | 34 +++- .../resources/particle_process_material.h | 8 +- godot/servers/navigation_server_2d.cpp | 1 + godot/servers/navigation_server_2d.h | 1 + godot/servers/navigation_server_2d_dummy.h | 1 + godot/servers/navigation_server_3d.cpp | 1 + godot/servers/navigation_server_3d.h | 2 + godot/servers/navigation_server_3d_dummy.h | 1 + .../rendering/dummy/storage/mesh_storage.h | 3 +- .../render_forward_clustered.cpp | 12 +- .../render_forward_clustered.h | 1 + .../forward_mobile/render_forward_mobile.cpp | 10 +- .../forward_mobile/render_forward_mobile.h | 1 + .../renderer_rd/storage_rd/mesh_storage.cpp | 44 ++++- .../renderer_rd/storage_rd/mesh_storage.h | 14 +- .../rendering/rendering_device.compat.inc | 16 ++ godot/servers/rendering/rendering_device.cpp | 44 ++++- godot/servers/rendering/rendering_device.h | 14 +- .../rendering/rendering_device_commons.h | 1 + .../rendering/rendering_device_driver.h | 3 + .../rendering/rendering_server_default.h | 3 +- .../rendering/storage/mesh_storage.cpp | 8 +- .../servers/rendering/storage/mesh_storage.h | 6 +- godot/servers/rendering_server.compat.inc | 5 + godot/servers/rendering_server.cpp | 3 +- godot/servers/rendering_server.h | 8 +- godot/version.py | 2 +- 144 files changed, 1898 insertions(+), 370 deletions(-) create mode 100644 godot/editor/icons/TexturePreviewChannels.svg create mode 100644 godot/editor/plugins/color_channel_selector.cpp create mode 100644 godot/editor/plugins/color_channel_selector.h create mode 100644 godot/platform/android/dialog_utils_jni.cpp create mode 100644 godot/platform/android/dialog_utils_jni.h create mode 100644 godot/platform/android/java/lib/res/mipmap-hdpi/icon_monochrome.png create mode 100644 godot/platform/android/java/lib/res/mipmap-mdpi/icon_monochrome.png create mode 100644 godot/platform/android/java/lib/res/mipmap-xhdpi/icon_monochrome.png create mode 100644 godot/platform/android/java/lib/res/mipmap-xxhdpi/icon_monochrome.png create mode 100644 godot/platform/android/java/lib/res/mipmap-xxxhdpi/icon_monochrome.png create mode 100644 godot/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt diff --git a/godot/.gitrepo b/godot/.gitrepo index dd9fac12..579aef10 100644 --- a/godot/.gitrepo +++ b/godot/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = https://github.com/V-Sekai/godot.git - branch = groups-4.4.2025-01-14T005028Z - commit = ef703d957f07eda98619a40314f05653edaf6781 - parent = 1be1b36904e73756e6429f39f7bc0dea107103f4 + branch = groups-4.4.2025-01-16T221116Z + commit = b09245232dd453f5a6b787adee6ed7442f50e082 + parent = 79b4aa74b943815c14c6e359f7062f0943331cb5 method = merge cmdver = 0.4.9 diff --git a/godot/core/debugger/remote_debugger_peer.cpp b/godot/core/debugger/remote_debugger_peer.cpp index 499b221c..1afb7fe0 100644 --- a/godot/core/debugger/remote_debugger_peer.cpp +++ b/godot/core/debugger/remote_debugger_peer.cpp @@ -173,12 +173,12 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } else { const int ms = waits[i]; OS::get_singleton()->delay_usec(ms * 1000); - print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); + print_verbose("Remote Debugger: Connection failed with status: '" + String::num_int64(tcp_client->get_status()) + "', retrying in " + String::num_int64(ms) + " msec."); } } if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num(tcp_client->get_status()))); + ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status()))); return FAILED; } connected = true; diff --git a/godot/core/io/image.cpp b/godot/core/io/image.cpp index 97335d0f..05586b3d 100644 --- a/godot/core/io/image.cpp +++ b/godot/core/io/image.cpp @@ -1135,7 +1135,7 @@ static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, } bool Image::is_size_po2() const { - return uint32_t(width) == next_power_of_2(width) && uint32_t(height) == next_power_of_2(height); + return is_power_of_2(width) && is_power_of_2(height); } void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { @@ -3953,6 +3953,97 @@ String Image::get_format_name(Format p_format) { return format_names[p_format]; } +uint32_t Image::get_format_component_mask(Format p_format) { + const uint32_t r = 1; + const uint32_t rg = 3; + const uint32_t rgb = 7; + const uint32_t rgba = 15; + + switch (p_format) { + case FORMAT_L8: + return rgb; + case FORMAT_LA8: + return rgba; + case FORMAT_R8: + return r; + case FORMAT_RG8: + return rg; + case FORMAT_RGB8: + return rgb; + case FORMAT_RGBA8: + return rgba; + case FORMAT_RGBA4444: + return rgba; + case FORMAT_RGB565: + return rgb; + case FORMAT_RF: + return r; + case FORMAT_RGF: + return rg; + case FORMAT_RGBF: + return rgb; + case FORMAT_RGBAF: + return rgba; + case FORMAT_RH: + return r; + case FORMAT_RGH: + return rg; + case FORMAT_RGBH: + return rgb; + case FORMAT_RGBAH: + return rgba; + case FORMAT_RGBE9995: + return rgba; + case FORMAT_DXT1: + return rgb; + case FORMAT_DXT3: + return rgb; + case FORMAT_DXT5: + return rgba; + case FORMAT_RGTC_R: + return r; + case FORMAT_RGTC_RG: + return rg; + case FORMAT_BPTC_RGBA: + return rgba; + case FORMAT_BPTC_RGBF: + return rgb; + case FORMAT_BPTC_RGBFU: + return rgb; + case FORMAT_ETC: + return rgb; + case FORMAT_ETC2_R11: + return r; + case FORMAT_ETC2_R11S: + return r; + case FORMAT_ETC2_RG11: + return rg; + case FORMAT_ETC2_RG11S: + return rg; + case FORMAT_ETC2_RGB8: + return rgb; + case FORMAT_ETC2_RGBA8: + return rgba; + case FORMAT_ETC2_RGB8A1: + return rgba; + case FORMAT_ETC2_RA_AS_RG: + return rgba; + case FORMAT_DXT5_RA_AS_RG: + return rgba; + case FORMAT_ASTC_4x4: + return rgba; + case FORMAT_ASTC_4x4_HDR: + return rgba; + case FORMAT_ASTC_8x8: + return rgba; + case FORMAT_ASTC_8x8_HDR: + return rgba; + default: + ERR_PRINT("Unhandled format."); + return rgba; + } +} + Error Image::load_png_from_buffer(const Vector &p_array) { return _load_from_buffer(p_array, _png_mem_loader_func); } diff --git a/godot/core/io/image.h b/godot/core/io/image.h index 30300bfa..992c4359 100644 --- a/godot/core/io/image.h +++ b/godot/core/io/image.h @@ -395,6 +395,7 @@ class Image : public Resource { Ref get_region(const Rect2i &p_area) const; static String get_format_name(Format p_format); + static uint32_t get_format_component_mask(Format p_format); Error load_png_from_buffer(const Vector &p_array); Error load_jpg_from_buffer(const Vector &p_array); diff --git a/godot/core/os/memory.cpp b/godot/core/os/memory.cpp index 3a496612..3d3b0392 100644 --- a/godot/core/os/memory.cpp +++ b/godot/core/os/memory.cpp @@ -64,10 +64,6 @@ SafeNumeric Memory::max_usage; SafeNumeric Memory::alloc_count; -inline bool is_power_of_2(size_t x) { - return x && ((x & (x - 1U)) == 0U); -} - void *Memory::alloc_aligned_static(size_t p_bytes, size_t p_alignment) { DEV_ASSERT(is_power_of_2(p_alignment)); diff --git a/godot/core/typedefs.h b/godot/core/typedefs.h index b5b44f21..7802b727 100644 --- a/godot/core/typedefs.h +++ b/godot/core/typedefs.h @@ -135,6 +135,12 @@ constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) { /* Functions to handle powers of 2 and shifting. */ +// Returns `true` if a positive integer is a power of 2, `false` otherwise. +template +inline bool is_power_of_2(const T x) { + return x && ((x & (x - 1)) == 0); +} + // Function to find the next power of 2 to an integer. static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) { if (x == 0) { diff --git a/godot/doc/classes/DisplayServer.xml b/godot/doc/classes/DisplayServer.xml index a607d6b8..a22d05f5 100644 --- a/godot/doc/classes/DisplayServer.xml +++ b/godot/doc/classes/DisplayServer.xml @@ -123,7 +123,7 @@ Shows a text dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [int] parameter which corresponds to the index of the pressed button. - [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include macOS and Windows. + [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include macOS, Windows, and Android. @@ -1937,7 +1937,7 @@ The display server supports all features of [constant FEATURE_NATIVE_DIALOG_FILE], with the added functionality of Options and native dialog file access to [code]res://[/code] and [code]user://[/code] paths. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b] - The display server supports initiating window drag operation on demand. See [method window_start_drag]. + The display server supports initiating window drag and resize operations on demand. See [method window_start_drag] and [method window_start_resize]. Display server supports [constant WINDOW_FLAG_EXCLUDE_FROM_CAPTURE] window flag. diff --git a/godot/doc/classes/EditorSceneFormatImporter.xml b/godot/doc/classes/EditorSceneFormatImporter.xml index 886185ff..3440fdc1 100644 --- a/godot/doc/classes/EditorSceneFormatImporter.xml +++ b/godot/doc/classes/EditorSceneFormatImporter.xml @@ -16,11 +16,6 @@ Return supported file extensions for this scene importer. - - - - - diff --git a/godot/doc/classes/NavigationRegion2D.xml b/godot/doc/classes/NavigationRegion2D.xml index 31ce1dba..731245fb 100644 --- a/godot/doc/classes/NavigationRegion2D.xml +++ b/godot/doc/classes/NavigationRegion2D.xml @@ -23,6 +23,12 @@ Bakes the [NavigationPolygon]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread. + + + + Returns the axis-aligned rectangle for the region's transformed navigation mesh. + + diff --git a/godot/doc/classes/NavigationRegion3D.xml b/godot/doc/classes/NavigationRegion3D.xml index ad31fd06..019afce3 100644 --- a/godot/doc/classes/NavigationRegion3D.xml +++ b/godot/doc/classes/NavigationRegion3D.xml @@ -23,6 +23,12 @@ Bakes the [NavigationMesh]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread. Baking on separate thread is useful because navigation baking is not a cheap operation. When it is completed, it automatically sets the new [NavigationMesh]. Please note that baking on separate thread may be very slow if geometry is parsed from meshes as async access to each mesh involves heavy synchronization. Also, please note that baking on a separate thread is automatically disabled on operating systems that cannot use threads (such as Web with threads disabled). + + + + Returns the axis-aligned bounding box for the region's transformed navigation mesh. + + diff --git a/godot/doc/classes/NavigationServer2D.xml b/godot/doc/classes/NavigationServer2D.xml index 41ef298b..95db5591 100644 --- a/godot/doc/classes/NavigationServer2D.xml +++ b/godot/doc/classes/NavigationServer2D.xml @@ -784,6 +784,13 @@ Creates a new region. + + + + + Returns the axis-aligned rectangle for the [param region]'s transformed navigation mesh. + + diff --git a/godot/doc/classes/NavigationServer3D.xml b/godot/doc/classes/NavigationServer3D.xml index 6f0c6874..64da1cda 100644 --- a/godot/doc/classes/NavigationServer3D.xml +++ b/godot/doc/classes/NavigationServer3D.xml @@ -925,6 +925,13 @@ Creates a new region. + + + + + Returns the axis-aligned bounding box for the [param region]'s transformed navigation mesh. + + diff --git a/godot/doc/classes/ParticleProcessMaterial.xml b/godot/doc/classes/ParticleProcessMaterial.xml index e1c97a74..8336899e 100644 --- a/godot/doc/classes/ParticleProcessMaterial.xml +++ b/godot/doc/classes/ParticleProcessMaterial.xml @@ -346,6 +346,10 @@ The amount of particles to spawn from the subemitter node when the particle expires. [b]Note:[/b] This value shouldn't exceed [member GPUParticles2D.amount] or [member GPUParticles3D.amount] defined on the [i]subemitter node[/i] (not the main node), relative to the subemitter's particle lifetime. If the number of particles is exceeded, no new particles will spawn from the subemitter until enough particles have expired. + + The amount of particles to spawn from the subemitter node when the particle spawns. + [b]Note:[/b] This value shouldn't exceed [member GPUParticles2D.amount] or [member GPUParticles3D.amount] defined on the [i]subemitter node[/i] (not the main node), relative to the subemitter's particle lifetime. If the number of particles is exceeded, no new particles will spawn from the subemitter until enough particles have expired. + The frequency at which particles should be emitted from the subemitter node. One particle will be spawned every [member sub_emitter_frequency] seconds. [b]Note:[/b] This value shouldn't exceed [member GPUParticles2D.amount] or [member GPUParticles3D.amount] defined on the [i]subemitter node[/i] (not the main node), relative to the subemitter's particle lifetime. If the number of particles is exceeded, no new particles will spawn from the subemitter until enough particles have expired. @@ -521,7 +525,9 @@ - + + + Represents the size of the [enum SubEmitterMode] enum. diff --git a/godot/doc/classes/RenderingDevice.xml b/godot/doc/classes/RenderingDevice.xml index 29a2f472..6ec7918d 100644 --- a/godot/doc/classes/RenderingDevice.xml +++ b/godot/doc/classes/RenderingDevice.xml @@ -81,6 +81,14 @@ [/codeblock] + + + + + Returns the address of the given [param buffer] which can be passed to shaders in any way to access underlying data. Buffer must have been created with this feature enabled. + [b]Note:[/b] You must check that the GPU supports this functionality by calling [method has_feature] with [constant SUPPORTS_BUFFER_DEVICE_ADDRESS] as a parameter. + + @@ -672,6 +680,13 @@ This is only used by Vulkan in debug builds. Godot must also be started with the [code]--extra-gpu-memory-tracking[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. + + + + + Returns [code]true[/code] if the [param feature] is supported by the GPU. + + @@ -688,9 +703,11 @@ + Creates a new index buffer. It can be accessed with the RID that is returned. Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. + Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it. @@ -1056,9 +1073,11 @@ + Creates a new uniform buffer. It can be accessed with the RID that is returned. Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. + Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it. @@ -1093,9 +1112,11 @@ + It can be accessed with the RID that is returned. Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. + Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it. @@ -2047,6 +2068,9 @@ + + Allows usage of [method buffer_get_device_address] on supported GPUs. + Sampler uniform. @@ -2418,6 +2442,9 @@ Floating-point specialization constant. + + Features support for buffer device address extension. + Maximum number of uniform sets that can be bound at a given time. diff --git a/godot/doc/classes/RenderingServer.xml b/godot/doc/classes/RenderingServer.xml index df2814fd..a3df48d0 100644 --- a/godot/doc/classes/RenderingServer.xml +++ b/godot/doc/classes/RenderingServer.xml @@ -2559,6 +2559,7 @@ + @@ -2593,6 +2594,29 @@ Returns the [RenderingDevice] [RID] handle of the [MultiMesh], which can be used as any other buffer on the Rendering Device. + + + + + Returns the [RenderingDevice] [RID] handle of the [MultiMesh] command buffer. This [RID] is only valid if [code]use_indirect[/code] is set to [code]true[/code] when allocating data through [method multimesh_allocate_data]. It can be used to directly modify the instance count via buffer. + The data structure is dependent on both how many surfaces the mesh contains and whether it is indexed or not, the buffer has 5 integers in it, with the last unused if the mesh is not indexed. + Each of the values in the buffer correspond to these options: + [codeblock lang=text] + Indexed: + 0 - indexCount; + 1 - instanceCount; + 2 - firstIndex; + 3 - vertexOffset; + 4 - firstInstance; + Non Indexed: + 0 - vertexCount; + 1 - instanceCount; + 2 - firstVertex; + 3 - firstInstance; + 4 - unused; + [/codeblock] + + diff --git a/godot/doc/classes/SpringBoneSimulator3D.xml b/godot/doc/classes/SpringBoneSimulator3D.xml index 6c639105..95c357f6 100644 --- a/godot/doc/classes/SpringBoneSimulator3D.xml +++ b/godot/doc/classes/SpringBoneSimulator3D.xml @@ -126,13 +126,6 @@ Returns the end bone name of the bone chain. - - - - - Returns the end bone tip radius of the bone chain when [method is_end_bone_extended] is [code]true[/code]. - - @@ -421,14 +414,6 @@ [b]Note:[/b] End bone must be the root bone or a child of the root bone. If they are the same, the tail must be extended by [method set_extend_end_bone] to jiggle the bone. - - - - - - Sets the end bone tip radius of the bone chain when [method is_end_bone_extended] is [code]true[/code]. - - @@ -452,6 +437,8 @@ If [param enabled] is [code]true[/code], the end bone is extended to have the tail. + The extended tail config is allocated to the last element in the joint list. + In other words, if you set [param enabled] is [code]false[/code], the config of last element in the joint list has no effect in the simulated result. diff --git a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp index 0e92d830..88d0ac28 100644 --- a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -905,6 +905,11 @@ void RenderingDeviceDriverD3D12::buffer_unmap(BufferID p_buffer) { buf_info->resource->Unmap(0, &VOID_RANGE); } +uint64_t RenderingDeviceDriverD3D12::buffer_get_device_address(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + return buf_info->resource->GetGPUVirtualAddress(); +} + /*****************/ /**** TEXTURE ****/ /*****************/ @@ -6271,6 +6276,8 @@ bool RenderingDeviceDriverD3D12::has_feature(Features p_feature) { return vrs_capabilities.ss_image_supported; case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; + case SUPPORTS_BUFFER_DEVICE_ADDRESS: + return true; default: return false; } diff --git a/godot/drivers/d3d12/rendering_device_driver_d3d12.h b/godot/drivers/d3d12/rendering_device_driver_d3d12.h index 5bcef066..50d0f2cd 100644 --- a/godot/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/godot/drivers/d3d12/rendering_device_driver_d3d12.h @@ -284,6 +284,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final; virtual uint8_t *buffer_map(BufferID p_buffer) override final; virtual void buffer_unmap(BufferID p_buffer) override final; + virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final; /*****************/ /**** TEXTURE ****/ diff --git a/godot/drivers/gles3/storage/mesh_storage.cpp b/godot/drivers/gles3/storage/mesh_storage.cpp index a3362942..428646aa 100644 --- a/godot/drivers/gles3/storage/mesh_storage.cpp +++ b/godot/drivers/gles3/storage/mesh_storage.cpp @@ -1518,7 +1518,7 @@ void MeshStorage::_multimesh_free(RID p_rid) { multimesh_owner.free(p_rid); } -void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -2041,6 +2041,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector &p_ } } +RID MeshStorage::_multimesh_get_command_buffer_rd_rid(RID p_multimesh) const { + ERR_FAIL_V_MSG(RID(), "GLES3 does not implement indirect multimeshes."); +} + RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const { ERR_FAIL_V_MSG(RID(), "GLES3 does not contain a Rid for the multimesh buffer."); } diff --git a/godot/drivers/gles3/storage/mesh_storage.h b/godot/drivers/gles3/storage/mesh_storage.h index 83ac0efc..3b9084a0 100644 --- a/godot/drivers/gles3/storage/mesh_storage.h +++ b/godot/drivers/gles3/storage/mesh_storage.h @@ -502,7 +502,7 @@ class MeshStorage : public RendererMeshStorage { virtual RID _multimesh_allocate() override; virtual void _multimesh_initialize(RID p_rid) override; virtual void _multimesh_free(RID p_rid) override; - virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override; virtual int _multimesh_get_instance_count(RID p_multimesh) const override; virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; @@ -521,6 +521,7 @@ class MeshStorage : public RendererMeshStorage { virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override; virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; virtual void _multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) override; + virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override; virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override; virtual Vector _multimesh_get_buffer(RID p_multimesh) const override; diff --git a/godot/drivers/metal/rendering_device_driver_metal.h b/godot/drivers/metal/rendering_device_driver_metal.h index ac8a1650..5ac1bab4 100644 --- a/godot/drivers/metal/rendering_device_driver_metal.h +++ b/godot/drivers/metal/rendering_device_driver_metal.h @@ -105,6 +105,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final; virtual uint8_t *buffer_map(BufferID p_buffer) override final; virtual void buffer_unmap(BufferID p_buffer) override final; + virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final; #pragma mark - Texture diff --git a/godot/drivers/metal/rendering_device_driver_metal.mm b/godot/drivers/metal/rendering_device_driver_metal.mm index 1d65d46b..2e32e99c 100644 --- a/godot/drivers/metal/rendering_device_driver_metal.mm +++ b/godot/drivers/metal/rendering_device_driver_metal.mm @@ -159,6 +159,15 @@ _FORCE_INLINE_ MTLSize mipmapLevelSizeFromSize(MTLSize p_size, NSUInteger p_leve // Nothing to do. } +uint64_t RenderingDeviceDriverMetal::buffer_get_device_address(BufferID p_buffer) { + if (@available(iOS 16.0, macOS 13.0, *)) { + id obj = rid::get(p_buffer); + return obj.gpuAddress; + } else { + return 0; + } +} + #pragma mark - Texture #pragma mark - Format Conversions @@ -4026,6 +4035,8 @@ bool isArrayTexture(MTLTextureType p_type) { return false; case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; + case SUPPORTS_BUFFER_DEVICE_ADDRESS: + return false; case SUPPORTS_METALFX_SPATIAL: return device_properties->features.metal_fx_spatial; case SUPPORTS_METALFX_TEMPORAL: diff --git a/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp b/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp index fb63e962..45c57491 100644 --- a/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -513,6 +513,7 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() { _register_requested_device_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, false); _register_requested_device_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); _register_requested_device_extension(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, false); if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { _register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); @@ -730,6 +731,7 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() { void *next_features = nullptr; VkPhysicalDeviceVulkan12Features device_features_vk_1_2 = {}; VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {}; + VkPhysicalDeviceBufferDeviceAddressFeaturesKHR buffer_device_address_features = {}; VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; VkPhysicalDeviceMultiviewFeatures multiview_features = {}; @@ -740,10 +742,17 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() { device_features_vk_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; device_features_vk_1_2.pNext = next_features; next_features = &device_features_vk_1_2; - } else if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { - shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; - shader_features.pNext = next_features; - next_features = &shader_features; + } else { + if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { + shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; + shader_features.pNext = next_features; + next_features = &shader_features; + } + if (enabled_device_extension_names.has(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) { + buffer_device_address_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR; + buffer_device_address_features.pNext = next_features; + next_features = &buffer_device_address_features; + } } if (enabled_device_extension_names.has(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { @@ -783,11 +792,17 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() { shader_capabilities.shader_float16_is_supported = device_features_vk_1_2.shaderFloat16; shader_capabilities.shader_int8_is_supported = device_features_vk_1_2.shaderInt8; } + if (enabled_device_extension_names.has(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) { + buffer_device_address_support = device_features_vk_1_2.bufferDeviceAddress; + } } else { if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; } + if (enabled_device_extension_names.has(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) { + buffer_device_address_support = buffer_device_address_features.bufferDeviceAddress; + } } if (enabled_device_extension_names.has(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { @@ -971,6 +986,14 @@ Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector p_usage, MemoryAllocationType p_allocation_type) { VkBufferCreateInfo create_info = {}; @@ -1588,6 +1615,15 @@ void RenderingDeviceDriverVulkan::buffer_unmap(BufferID p_buffer) { vmaUnmapMemory(allocator, buf_info->allocation.handle); } +uint64_t RenderingDeviceDriverVulkan::buffer_get_device_address(BufferID p_buffer) { + const BufferInfo *buf_info = (const BufferInfo *)p_buffer.id; + VkBufferDeviceAddressInfo address_info = {}; + address_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + address_info.pNext = nullptr; + address_info.buffer = buf_info->vk_buffer; + return vkGetBufferDeviceAddress(vk_device, &address_info); +} + /*****************/ /**** TEXTURE ****/ /*****************/ @@ -5874,6 +5910,8 @@ bool RenderingDeviceDriverVulkan::has_feature(Features p_feature) { return vrs_capabilities.attachment_vrs_supported && physical_device_features.shaderStorageImageExtendedFormats; case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; + case SUPPORTS_BUFFER_DEVICE_ADDRESS: + return buffer_device_address_support; default: return false; } diff --git a/godot/drivers/vulkan/rendering_device_driver_vulkan.h b/godot/drivers/vulkan/rendering_device_driver_vulkan.h index 4eec7547..ea12450a 100644 --- a/godot/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/godot/drivers/vulkan/rendering_device_driver_vulkan.h @@ -138,6 +138,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { VRSCapabilities vrs_capabilities; ShaderCapabilities shader_capabilities; StorageBufferCapabilities storage_buffer_capabilities; + bool buffer_device_address_support = false; bool pipeline_cache_control_support = false; bool device_fault_support = false; #if defined(VK_TRACK_DEVICE_MEMORY) @@ -204,6 +205,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final; virtual uint8_t *buffer_map(BufferID p_buffer) override final; virtual void buffer_unmap(BufferID p_buffer) override final; + virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final; /*****************/ /**** TEXTURE ****/ diff --git a/godot/drivers/wasapi/audio_driver_wasapi.cpp b/godot/drivers/wasapi/audio_driver_wasapi.cpp index b5cb8da2..7d568069 100644 --- a/godot/drivers/wasapi/audio_driver_wasapi.cpp +++ b/godot/drivers/wasapi/audio_driver_wasapi.cpp @@ -123,6 +123,8 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); static bool default_output_device_changed = false; static bool default_input_device_changed = false; +static int output_reinit_countdown = 0; +static int input_reinit_countdown = 0; // Silence warning due to a COM API weirdness (GH-35194). #if defined(__GNUC__) && !defined(__clang__) @@ -134,6 +136,8 @@ class CMMNotificationClient : public IMMNotificationClient { LONG _cRef = 1; public: + ComPtr enumerator = nullptr; + CMMNotificationClient() {} virtual ~CMMNotificationClient() {} @@ -199,6 +203,9 @@ class CMMNotificationClient : public IMMNotificationClient { static CMMNotificationClient notif_client; Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) { + // This function can be called recursively, so clean up before starting: + audio_device_finish(p_device); + WAVEFORMATEX *pwfex; ComPtr enumerator = nullptr; ComPtr output_device = nullptr; @@ -225,21 +232,25 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i ComPtr tmp_device = nullptr; hr = devices->Item(i, &tmp_device); - ERR_BREAK(hr != S_OK); + ERR_BREAK_MSG(hr != S_OK, "Cannot get devices item."); ComPtr props = nullptr; hr = tmp_device->OpenPropertyStore(STGM_READ, &props); - ERR_BREAK(hr != S_OK); + ERR_BREAK_MSG(hr != S_OK, "Cannot open property store."); PROPVARIANT propvar; PropVariantInit(&propvar); hr = props->GetValue(PKEY_Device_FriendlyNameGodot, &propvar); - ERR_BREAK(hr != S_OK); + ERR_BREAK_MSG(hr != S_OK, "Cannot get value."); if (p_device->device_name == String(propvar.pwszVal)) { hr = tmp_device->GetId(&strId); - ERR_BREAK(hr != S_OK); + if (unlikely(hr != S_OK)) { + PropVariantClear(&propvar); + ERR_PRINT("Cannot get device ID string."); + break; + } found = true; } @@ -270,9 +281,14 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } + if (notif_client.enumerator != nullptr) { + notif_client.enumerator->UnregisterEndpointNotificationCallback(¬if_client); + notif_client.enumerator = nullptr; + } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); - - if (hr != S_OK) { + if (hr == S_OK) { + notif_client.enumerator = enumerator; + } else { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } @@ -317,6 +333,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + // From this point onward, CoTaskMemFree(pwfex) must be called before returning or pwfex will leak! print_verbose("WASAPI: wFormatTag = " + itos(pwfex->wFormatTag)); print_verbose("WASAPI: nChannels = " + itos(pwfex->nChannels)); @@ -340,6 +357,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i print_verbose("WASAPI: closest->cbSize = " + itos(closest->cbSize)); WARN_PRINT("WASAPI: Using closest match instead"); + CoTaskMemFree(pwfex); pwfex = closest; } } @@ -359,11 +377,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i p_device->format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); + CoTaskMemFree(pwfex); ERR_FAIL_V(ERR_CANT_OPEN); } } else { if (p_device->format_tag != WAVE_FORMAT_PCM && p_device->format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); + CoTaskMemFree(pwfex); ERR_FAIL_V(ERR_CANT_OPEN); } } @@ -376,10 +396,28 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_input ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); - ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + + if (p_reinit) { + // In case we're trying to re-initialize the device, prevent throwing this error on the console, + // otherwise if there is currently no device available this will spam the console. + if (hr != S_OK) { + print_verbose("WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + CoTaskMemFree(pwfex); + return ERR_CANT_OPEN; + } + } else { + if (unlikely(hr != S_OK)) { + CoTaskMemFree(pwfex); + ERR_FAIL_V_MSG(ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + } + } + UINT32 max_frames; hr = p_device->audio_client->GetBufferSize(&max_frames); - ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + if (unlikely(hr != S_OK)) { + CoTaskMemFree(pwfex); + ERR_FAIL_V(ERR_CANT_OPEN); + } // Due to WASAPI Shared Mode we have no control of the buffer size if (!p_input) { @@ -453,7 +491,10 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i } else { hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); } - ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + if (unlikely(hr != S_OK)) { + CoTaskMemFree(pwfex); + ERR_FAIL_V(ERR_CANT_OPEN); + } // Free memory CoTaskMemFree(pwfex); @@ -464,6 +505,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i Error AudioDriverWASAPI::init_output_device(bool p_reinit) { Error err = audio_device_init(&audio_output, false, p_reinit); if (err != OK) { + // We've tried to init the device, but have failed. Time to clean up. + Error finish_err = finish_output_device(); + if (finish_err != OK) { + ERR_PRINT("WASAPI: finish_output_device error after failed output audio_device_init"); + } return err; } @@ -504,6 +550,11 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) { Error AudioDriverWASAPI::init_input_device(bool p_reinit) { Error err = audio_device_init(&audio_input, true, p_reinit); if (err != OK) { + // We've tried to init the device, but have failed. Time to clean up. + Error finish_err = finish_input_device(); + if (finish_err != OK) { + ERR_PRINT("WASAPI: finish_input_device error after failed input audio_device_init"); + } return err; } @@ -826,9 +877,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } if (!ad->audio_output.audio_client) { - Error err = ad->init_output_device(true); - if (err == OK) { - ad->start(); + if (output_reinit_countdown < 1) { + Error err = ad->init_output_device(true); + if (err == OK) { + ad->start(); + } else { + output_reinit_countdown = 1000; + } + } else { + output_reinit_countdown--; } avail_frames = 0; @@ -899,9 +956,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } if (!ad->audio_input.audio_client) { - Error err = ad->init_input_device(true); - if (err == OK) { - ad->input_start(); + if (input_reinit_countdown < 1) { + Error err = ad->init_input_device(true); + if (err == OK) { + ad->input_start(); + } else { + input_reinit_countdown = 1000; + } + } else { + input_reinit_countdown--; } } } diff --git a/godot/editor/animation_track_editor.cpp b/godot/editor/animation_track_editor.cpp index f93029a3..dbb23d6f 100644 --- a/godot/editor/animation_track_editor.cpp +++ b/godot/editor/animation_track_editor.cpp @@ -50,6 +50,7 @@ #include "scene/animation/tween.h" #include "scene/gui/check_box.h" #include "scene/gui/color_picker.h" +#include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" @@ -7606,31 +7607,34 @@ AnimationTrackEditor::AnimationTrackEditor() { track_vbox->set_h_size_flags(SIZE_EXPAND_FILL); scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); - HBoxContainer *bottom_hb = memnew(HBoxContainer); - add_child(bottom_hb); + HFlowContainer *bottom_hf = memnew(HFlowContainer); + add_child(bottom_hf); imported_anim_warning = memnew(Button); imported_anim_warning->hide(); imported_anim_warning->set_text(TTR("Imported Scene")); imported_anim_warning->set_tooltip_text(TTR("Warning: Editing imported animation")); imported_anim_warning->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_show_imported_anim_warning)); - bottom_hb->add_child(imported_anim_warning); + bottom_hf->add_child(imported_anim_warning); dummy_player_warning = memnew(Button); dummy_player_warning->hide(); dummy_player_warning->set_text(TTR("Dummy Player")); dummy_player_warning->set_tooltip_text(TTR("Warning: Editing dummy AnimationPlayer")); dummy_player_warning->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_show_dummy_player_warning)); - bottom_hb->add_child(dummy_player_warning); + bottom_hf->add_child(dummy_player_warning); inactive_player_warning = memnew(Button); inactive_player_warning->hide(); inactive_player_warning->set_text(TTR("Inactive Player")); inactive_player_warning->set_tooltip_text(TTR("Warning: AnimationPlayer is inactive")); inactive_player_warning->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_show_inactive_player_warning)); - bottom_hb->add_child(inactive_player_warning); + bottom_hf->add_child(inactive_player_warning); - bottom_hb->add_spacer(); + Control *spacer = memnew(Control); + spacer->set_mouse_filter(MOUSE_FILTER_PASS); + spacer->set_h_size_flags(SIZE_EXPAND_FILL); + bottom_hf->add_child(spacer); bezier_edit_icon = memnew(Button); bezier_edit_icon->set_flat(true); @@ -7639,7 +7643,7 @@ AnimationTrackEditor::AnimationTrackEditor() { bezier_edit_icon->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_toggle_bezier_edit)); bezier_edit_icon->set_tooltip_text(TTR("Toggle between the bezier curve editor and track editor.")); - bottom_hb->add_child(bezier_edit_icon); + bottom_hf->add_child(bezier_edit_icon); selected_filter = memnew(Button); selected_filter->set_flat(true); @@ -7647,7 +7651,7 @@ AnimationTrackEditor::AnimationTrackEditor() { selected_filter->set_toggle_mode(true); selected_filter->set_tooltip_text(TTR("Only show tracks from nodes selected in tree.")); - bottom_hb->add_child(selected_filter); + bottom_hf->add_child(selected_filter); view_group = memnew(Button); view_group->set_flat(true); @@ -7655,12 +7659,12 @@ AnimationTrackEditor::AnimationTrackEditor() { view_group->set_toggle_mode(true); view_group->set_tooltip_text(TTR("Group tracks by node or display them as plain list.")); - bottom_hb->add_child(view_group); - bottom_hb->add_child(memnew(VSeparator)); + bottom_hf->add_child(view_group); + bottom_hf->add_child(memnew(VSeparator)); snap_timeline = memnew(Button); snap_timeline->set_flat(true); - bottom_hb->add_child(snap_timeline); + bottom_hf->add_child(snap_timeline); snap_timeline->set_disabled(true); snap_timeline->set_toggle_mode(true); snap_timeline->set_pressed(false); @@ -7668,7 +7672,7 @@ AnimationTrackEditor::AnimationTrackEditor() { snap_keys = memnew(Button); snap_keys->set_flat(true); - bottom_hb->add_child(snap_keys); + bottom_hf->add_child(snap_keys); snap_keys->set_disabled(true); snap_keys->set_toggle_mode(true); snap_keys->set_pressed(true); @@ -7676,7 +7680,7 @@ AnimationTrackEditor::AnimationTrackEditor() { fps_compat = memnew(Button); fps_compat->set_flat(true); - bottom_hb->add_child(fps_compat); + bottom_hf->add_child(fps_compat); fps_compat->set_disabled(true); fps_compat->set_toggle_mode(true); fps_compat->set_pressed(true); @@ -7684,7 +7688,7 @@ AnimationTrackEditor::AnimationTrackEditor() { fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode)); nearest_fps_label = memnew(Label); - bottom_hb->add_child(nearest_fps_label); + bottom_hf->add_child(nearest_fps_label); step = memnew(EditorSpinSlider); step->set_min(0); @@ -7693,22 +7697,23 @@ AnimationTrackEditor::AnimationTrackEditor() { step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); step->set_tooltip_text(TTR("Animation step value.")); - bottom_hb->add_child(step); + bottom_hf->add_child(step); step->connect(SceneStringName(value_changed), callable_mp(this, &AnimationTrackEditor::_update_step)); step->set_read_only(true); snap_mode = memnew(OptionButton); snap_mode->add_item(TTR("Seconds")); snap_mode->add_item(TTR("FPS")); - bottom_hb->add_child(snap_mode); + bottom_hf->add_child(snap_mode); snap_mode->connect(SceneStringName(item_selected), callable_mp(this, &AnimationTrackEditor::_snap_mode_changed)); snap_mode->set_disabled(true); - bottom_hb->add_child(memnew(VSeparator)); + bottom_hf->add_child(memnew(VSeparator)); + HBoxContainer *zoom_hb = memnew(HBoxContainer); zoom_icon = memnew(TextureRect); zoom_icon->set_v_size_flags(SIZE_SHRINK_CENTER); - bottom_hb->add_child(zoom_icon); + zoom_hb->add_child(zoom_icon); zoom = memnew(HSlider); zoom->set_step(0.01); zoom->set_min(0.0); @@ -7716,7 +7721,8 @@ AnimationTrackEditor::AnimationTrackEditor() { zoom->set_value(1.0); zoom->set_custom_minimum_size(Size2(200, 0) * EDSCALE); zoom->set_v_size_flags(SIZE_SHRINK_CENTER); - bottom_hb->add_child(zoom); + zoom_hb->add_child(zoom); + bottom_hf->add_child(zoom_hb); timeline->set_zoom(zoom); ED_SHORTCUT("animation_editor/auto_fit", TTRC("Fit to panel"), KeyModifierMask::ALT | Key::F); @@ -7725,14 +7731,14 @@ AnimationTrackEditor::AnimationTrackEditor() { auto_fit->set_flat(true); auto_fit->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit)); auto_fit->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); - bottom_hb->add_child(auto_fit); + bottom_hf->add_child(auto_fit); auto_fit_bezier = memnew(Button); auto_fit_bezier->set_flat(true); auto_fit_bezier->set_visible(false); auto_fit_bezier->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier)); auto_fit_bezier->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); - bottom_hb->add_child(auto_fit_bezier); + bottom_hf->add_child(auto_fit_bezier); edit = memnew(MenuButton); edit->set_shortcut_context(this); diff --git a/godot/editor/editor_file_system.cpp b/godot/editor/editor_file_system.cpp index 8670cb49..e5e27a11 100644 --- a/godot/editor/editor_file_system.cpp +++ b/godot/editor/editor_file_system.cpp @@ -1747,7 +1747,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, if (!p_dir) { return; //none } - p_file->store_line("::" + p_dir->get_path() + "::" + String::num(p_dir->modified_time)); + p_file->store_line("::" + p_dir->get_path() + "::" + String::num_int64(p_dir->modified_time)); for (int i = 0; i < p_dir->files.size(); i++) { const EditorFileSystemDirectory::FileInfo *file_info = p_dir->files[i]; diff --git a/godot/editor/editor_help.cpp b/godot/editor/editor_help.cpp index 468f58d5..0caabffe 100644 --- a/godot/editor/editor_help.cpp +++ b/godot/editor/editor_help.cpp @@ -46,6 +46,7 @@ #include "editor/editor_property_name_processor.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" +#include "editor/filesystem_dock.h" #include "editor/gui/editor_toaster.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/themes/editor_scale.h" @@ -3870,6 +3871,37 @@ void EditorHelpBit::_update_labels() { _add_text_to_rt(help_data.description.replace("", comment_color.to_html()), content, this, symbol_class_name); } + if (!help_data.resource_path.is_empty()) { + if (has_prev_text) { + content->add_newline(); + content->add_newline(); + } + has_prev_text = true; + + const String ext = help_data.resource_path.get_extension(); + const bool is_dir = ext.is_empty(); + const bool is_valid = is_dir || EditorFileSystem::get_singleton()->get_valid_extensions().has(ext); + if (!is_dir && is_valid) { + content->push_meta("open-res:" + help_data.resource_path, RichTextLabel::META_UNDERLINE_ON_HOVER); + content->add_image(get_editor_theme_icon(SNAME("Load"))); + content->add_text(nbsp + TTR("Open")); + content->pop(); // meta + content->add_newline(); + } + + if (is_valid) { + content->push_meta("show:" + help_data.resource_path, RichTextLabel::META_UNDERLINE_ON_HOVER); + content->add_image(get_editor_theme_icon(SNAME("Filesystem"))); + content->add_text(nbsp + TTR("Show in FileSystem")); + content->pop(); // meta + } else { + content->push_meta("open-file:" + help_data.resource_path, RichTextLabel::META_UNDERLINE_ON_HOVER); + content->add_image(get_editor_theme_icon(SNAME("Filesystem"))); + content->add_text(nbsp + TTR("Open in File Manager")); + content->pop(); // meta + } + } + if (is_inside_tree()) { update_content_height(); } @@ -3951,6 +3983,17 @@ void EditorHelpBit::_meta_clicked(const String &p_select) { } else { _go_to_help(topic + ":" + symbol_class_name + ":" + link); } + } else if (p_select.begins_with("open-file:")) { + String path = ProjectSettings::get_singleton()->globalize_path(p_select.trim_prefix("open-file:")); + OS::get_singleton()->shell_show_in_file_manager(path, true); + } else if (p_select.begins_with("open-res:")) { + if (help_data.doc_type.type == "PackedScene") { + EditorNode::get_singleton()->load_scene(p_select.trim_prefix("open-res:")); + } else { + EditorNode::get_singleton()->load_resource(p_select.trim_prefix("open-res:")); + } + } else if (p_select.begins_with("show:")) { + FileSystemDock::get_singleton()->navigate_to_path(p_select.trim_prefix("show:")); } else if (p_select.begins_with("http:") || p_select.begins_with("https:")) { OS::get_singleton()->shell_open(p_select); } else if (p_select.begins_with("^")) { // Copy button. @@ -4074,6 +4117,46 @@ void EditorHelpBit::parse_symbol(const String &p_symbol, const String &p_prologu help_data.doc_type.enumeration = item_data.get("enumeration", ""); help_data.doc_type.is_bitfield = item_data.get("is_bitfield", false); help_data.value = item_data.get("value", ""); + } else if (item_type == "resource") { + String path = item_name.simplify_path(); + const bool is_uid = path.begins_with("uid://"); + if (is_uid) { + if (ResourceUID::get_singleton()->has_id(ResourceUID::get_singleton()->text_to_id(path))) { + path = ResourceUID::uid_to_path(path); + } else { + path = ""; + } + } + help_data.resource_path = path; + + Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da->file_exists(path)) { + help_data.doc_type.type = ResourceLoader::get_resource_type(path); + if (help_data.doc_type.type.is_empty()) { + const Vector textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false); + symbol_type = textfile_ext.has(path.get_extension()) ? TTR("TextFile") : TTR("File"); + } else { + symbol_type = TTR("Resource"); + symbol_hint = SYMBOL_HINT_ASSIGNABLE; + if (is_uid) { + help_data.description = vformat("%s: [color=]%s[/color]", TTR("Path"), path); + } + } + symbol_name = path.get_file(); + } else if (!is_uid && da->dir_exists(path)) { + symbol_type = TTR("Directory"); + symbol_name = path; + } else { + help_data.resource_path = ""; + symbol_name = ""; + if (is_uid) { + symbol_type = TTR("Invalid UID"); + help_data.description = "[color=][i]" + TTR("This UID does not point to any valid Resource.") + "[/i][/color]"; + } else { + symbol_type = TTR("Invalid path"); + help_data.description = "[color=][i]" + TTR("This path does not exist.") + "[/i][/color]"; + } + } } else { ERR_FAIL_MSG("Invalid doc id: Unknown item type " + item_type.quote() + "."); } @@ -4091,7 +4174,7 @@ void EditorHelpBit::parse_symbol(const String &p_symbol, const String &p_prologu } } - if (help_data.description.is_empty()) { + if (help_data.description.is_empty() && item_type != "resource") { help_data.description = "[color=][i]" + TTR("No description available.") + "[/i][/color]"; } diff --git a/godot/editor/editor_help.h b/godot/editor/editor_help.h index eb687e9c..9686d6e1 100644 --- a/godot/editor/editor_help.h +++ b/godot/editor/editor_help.h @@ -277,6 +277,7 @@ class EditorHelpBit : public VBoxContainer { String value; Vector arguments; String qualifiers; + String resource_path; }; inline static HashMap doc_class_cache; diff --git a/godot/editor/editor_node.cpp b/godot/editor/editor_node.cpp index c3345e50..4d4f8c23 100644 --- a/godot/editor/editor_node.cpp +++ b/godot/editor/editor_node.cpp @@ -341,6 +341,19 @@ void EditorNode::_update_title() { } } +void EditorNode::input(const Ref &p_event) { + // EditorNode::get_singleton()->set_process_input is set to true in ProgressDialog + // only when the progress dialog is visible. + // We need to discard all key events to disable all shortcuts while the progress + // dialog is displayed, simulating an exclusive popup. Mouse events are + // captured by a full-screen container in front of the EditorNode in ProgressDialog, + // allowing interaction with the actual dialog where a Cancel button may be visible. + Ref k = p_event; + if (k.is_valid()) { + get_tree()->get_root()->set_input_as_handled(); + } +} + void EditorNode::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -7079,7 +7092,7 @@ EditorNode::EditorNode() { resource_preview = memnew(EditorResourcePreview); add_child(resource_preview); progress_dialog = memnew(ProgressDialog); - progress_dialog->set_unparent_when_invisible(true); + add_child(progress_dialog); progress_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_progress_dialog_visibility_changed)); gui_base = memnew(Panel); diff --git a/godot/editor/editor_node.h b/godot/editor/editor_node.h index 4866809d..e9ad3492 100644 --- a/godot/editor/editor_node.h +++ b/godot/editor/editor_node.h @@ -592,6 +592,7 @@ class EditorNode : public Node { void _exit_editor(int p_exit_code); + virtual void input(const Ref &p_event) override; virtual void shortcut_input(const Ref &p_event) override; bool has_main_screen() const { return true; } diff --git a/godot/editor/editor_settings.cpp b/godot/editor/editor_settings.cpp index dabd437f..11695051 100644 --- a/godot/editor/editor_settings.cpp +++ b/godot/editor/editor_settings.cpp @@ -761,10 +761,9 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { // GridMap // GridMapEditor - _initial_set("editors/grid_map/pick_distance", 5000.0); - _initial_set("editors/grid_map/palette_min_width", 230); - set_restart_if_changed("editors/grid_map/palette_min_width", true); - _initial_set("editors/grid_map/preview_size", 64); + EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/grid_map/pick_distance", 5000.0, "1,8192,0.1,or_greater"); + EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "editors/grid_map/palette_min_width", 230, "100,500,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_RANGE, "editors/grid_map/preview_size", 64, "16,128,1") // 3D EDITOR_SETTING_BASIC(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56, 0.5), "") diff --git a/godot/editor/export/editor_export_platform.cpp b/godot/editor/export/editor_export_platform.cpp index 8abfd8d6..8b142198 100644 --- a/godot/editor/export/editor_export_platform.cpp +++ b/godot/editor/export/editor_export_platform.cpp @@ -2209,7 +2209,7 @@ Vector EditorExportPlatform::gen_export_flags(BitField breakpoints; ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); diff --git a/godot/editor/icons/TexturePreviewChannels.svg b/godot/editor/icons/TexturePreviewChannels.svg new file mode 100644 index 00000000..e70fd998 --- /dev/null +++ b/godot/editor/icons/TexturePreviewChannels.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/editor/import/3d/editor_import_collada.cpp b/godot/editor/import/3d/editor_import_collada.cpp index b60c5eec..238aa940 100644 --- a/godot/editor/import/3d/editor_import_collada.cpp +++ b/godot/editor/import/3d/editor_import_collada.cpp @@ -1796,10 +1796,6 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) { /*************************************** SCENE ***********************************/ /*********************************************************************************/ -uint32_t EditorSceneFormatImporterCollada::get_import_flags() const { - return IMPORT_SCENE | IMPORT_ANIMATION; -} - void EditorSceneFormatImporterCollada::get_extensions(List *r_extensions) const { r_extensions->push_back("dae"); } diff --git a/godot/editor/import/3d/editor_import_collada.h b/godot/editor/import/3d/editor_import_collada.h index e0214c2f..4467c1ec 100644 --- a/godot/editor/import/3d/editor_import_collada.h +++ b/godot/editor/import/3d/editor_import_collada.h @@ -37,7 +37,6 @@ class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterCollada, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps = nullptr, Error *r_err = nullptr) override; diff --git a/godot/editor/import/3d/resource_importer_obj.cpp b/godot/editor/import/3d/resource_importer_obj.cpp index 750584b3..efada9f2 100644 --- a/godot/editor/import/3d/resource_importer_obj.cpp +++ b/godot/editor/import/3d/resource_importer_obj.cpp @@ -38,10 +38,6 @@ #include "scene/resources/mesh.h" #include "scene/resources/surface_tool.h" -uint32_t EditorOBJImporter::get_import_flags() const { - return IMPORT_SCENE; -} - static Error _parse_material_library(const String &p_path, HashMap> &material_map, List *r_missing_deps) { Ref f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path)); diff --git a/godot/editor/import/3d/resource_importer_obj.h b/godot/editor/import/3d/resource_importer_obj.h index c4a99428..46f6a551 100644 --- a/godot/editor/import/3d/resource_importer_obj.h +++ b/godot/editor/import/3d/resource_importer_obj.h @@ -37,7 +37,6 @@ class EditorOBJImporter : public EditorSceneFormatImporter { GDCLASS(EditorOBJImporter, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err = nullptr) override; diff --git a/godot/editor/import/3d/resource_importer_scene.cpp b/godot/editor/import/3d/resource_importer_scene.cpp index 5e955753..25ef0d3f 100644 --- a/godot/editor/import/3d/resource_importer_scene.cpp +++ b/godot/editor/import/3d/resource_importer_scene.cpp @@ -57,15 +57,6 @@ #include "scene/resources/packed_scene.h" #include "scene/resources/resource_format_text.h" -uint32_t EditorSceneFormatImporter::get_import_flags() const { - uint32_t ret; - if (GDVIRTUAL_CALL(_get_import_flags, ret)) { - return ret; - } - - ERR_FAIL_V(0); -} - void EditorSceneFormatImporter::get_extensions(List *r_extensions) const { Vector arr; if (GDVIRTUAL_CALL(_get_extensions, arr)) { @@ -118,7 +109,6 @@ void EditorSceneFormatImporter::_bind_methods() { ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorSceneFormatImporter::add_import_option); ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorSceneFormatImporter::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT)); - GDVIRTUAL_BIND(_get_import_flags); GDVIRTUAL_BIND(_get_extensions); GDVIRTUAL_BIND(_import_scene, "path", "flags", "options"); GDVIRTUAL_BIND(_get_import_options, "path"); @@ -2027,29 +2017,29 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p decomposition_default.instantiate(); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_concavity())); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_symmetry_planes_clipping_bias())); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_revolution_axes_clipping_bias())); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_min_volume_per_convex_hull())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_resolution())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_num_vertices_per_convex_hull())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_plane_downsampling())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_convex_hull_downsampling())); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_normalize_mesh())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), static_cast(decomposition_default->get_mode()))); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_convex_hull_approximation())); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_convex_hulls())); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_project_hull_vertices())); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_concavity())); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_symmetry_planes_clipping_bias())); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_revolution_axes_clipping_bias())); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_min_volume_per_convex_hull())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_resolution())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_num_vertices_per_convex_hull())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_plane_downsampling())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_downsampling())); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_normalize_mesh())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT), static_cast(decomposition_default->get_mode()))); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_approximation())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_convex_hulls())); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_project_hull_vertices())); // Primitives: Box, Sphere, Cylinder, Capsule. - r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3(2.0, 2.0, 2.0))); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3())); - r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3())); + r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3(2.0, 2.0, 2.0))); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3())); + r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3())); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/occluder", PROPERTY_HINT_ENUM, "Disabled,Mesh + Occluder,Occluder Only", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0.1f)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT), 0.1f)); } break; case INTERNAL_IMPORT_CATEGORY_MESH: { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); @@ -3281,10 +3271,6 @@ void ResourceImporterScene::get_scene_importer_extensions(List *p_extens /////////////////////////////////////// -uint32_t EditorSceneFormatImporterESCN::get_import_flags() const { - return IMPORT_SCENE; -} - void EditorSceneFormatImporterESCN::get_extensions(List *r_extensions) const { r_extensions->push_back("escn"); } diff --git a/godot/editor/import/3d/resource_importer_scene.h b/godot/editor/import/3d/resource_importer_scene.h index df2fe3d4..f25a2231 100644 --- a/godot/editor/import/3d/resource_importer_scene.h +++ b/godot/editor/import/3d/resource_importer_scene.h @@ -58,7 +58,6 @@ class EditorSceneFormatImporter : public RefCounted { Node *import_scene_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options); Ref import_animation_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options); - GDVIRTUAL0RC(uint32_t, _get_import_flags) GDVIRTUAL0RC(Vector, _get_extensions) GDVIRTUAL3R(Object *, _import_scene, String, uint32_t, Dictionary) GDVIRTUAL1(_get_import_options, String) @@ -77,7 +76,6 @@ class EditorSceneFormatImporter : public RefCounted { void add_import_option(const String &p_name, const Variant &p_default_value); void add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT); - virtual uint32_t get_import_flags() const; virtual void get_extensions(List *r_extensions) const; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err = nullptr); virtual void get_import_options(const String &p_path, List *r_options); @@ -322,7 +320,6 @@ class EditorSceneFormatImporterESCN : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterESCN, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err = nullptr) override; }; diff --git a/godot/editor/plugins/color_channel_selector.cpp b/godot/editor/plugins/color_channel_selector.cpp new file mode 100644 index 00000000..087a60e7 --- /dev/null +++ b/godot/editor/plugins/color_channel_selector.cpp @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* color_channel_selector.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 "color_channel_selector.h" + +#include "editor/themes/editor_scale.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/panel_container.h" +#include "scene/resources/style_box_flat.h" + +ColorChannelSelector::ColorChannelSelector() { + toggle_button = memnew(Button); + toggle_button->set_flat(true); + toggle_button->set_toggle_mode(true); + toggle_button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_toggled)); + toggle_button->add_theme_style_override("focus", memnew(StyleBoxEmpty)); + add_child(toggle_button); + + panel = memnew(PanelContainer); + panel->hide(); + + HBoxContainer *container = memnew(HBoxContainer); + container->add_theme_constant_override("separation", 0); + + create_button(0, "R", container); + create_button(1, "G", container); + create_button(2, "B", container); + create_button(3, "A", container); + + // Use a bit of transparency to be less distracting. + set_modulate(Color(1, 1, 1, 0.7)); + + panel->add_child(container); + + add_child(panel); +} + +void ColorChannelSelector::_notification(int p_what) { + if (p_what == NOTIFICATION_THEME_CHANGED) { + // PanelContainer's background is invisible in the editor. We need a background. + // And we need this in turn because buttons don't look good without background (for example, hover is transparent). + Ref bg_style = get_theme_stylebox(SceneStringName(panel), "TabContainer"); + ERR_FAIL_COND(bg_style.is_null()); + bg_style = bg_style->duplicate(); + // The default content margin makes the widget become a bit too large. It should be like mini-toolbar. + const float editor_scale = EditorScale::get_scale(); + bg_style->set_content_margin(SIDE_LEFT, 1.0f * editor_scale); + bg_style->set_content_margin(SIDE_RIGHT, 1.0f * editor_scale); + bg_style->set_content_margin(SIDE_TOP, 1.0f * editor_scale); + bg_style->set_content_margin(SIDE_BOTTOM, 1.0f * editor_scale); + panel->add_theme_style_override(SceneStringName(panel), bg_style); + + Ref icon = get_editor_theme_icon(SNAME("TexturePreviewChannels")); + toggle_button->set_button_icon(icon); + } +} + +void ColorChannelSelector::set_available_channels_mask(uint32_t p_mask) { + for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) { + const bool available = (p_mask & (1u << i)) != 0; + Button *button = channel_buttons[i]; + button->set_visible(available); + } +} + +void ColorChannelSelector::on_channel_button_toggled(bool p_unused_pressed) { + emit_signal("selected_channels_changed"); +} + +uint32_t ColorChannelSelector::get_selected_channels_mask() const { + uint32_t mask = 0; + for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) { + Button *button = channel_buttons[i]; + if (button->is_visible() && channel_buttons[i]->is_pressed()) { + mask |= (1 << i); + } + } + return mask; +} + +// Helper +Vector4 ColorChannelSelector::get_selected_channel_factors() const { + Vector4 channel_factors; + const uint32_t mask = get_selected_channels_mask(); + for (unsigned int i = 0; i < 4; ++i) { + if ((mask & (1 << i)) != 0) { + channel_factors[i] = 1; + } + } + return channel_factors; +} + +void ColorChannelSelector::create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent) { + ERR_FAIL_COND(p_channel_index >= CHANNEL_COUNT); + ERR_FAIL_COND(channel_buttons[p_channel_index] != nullptr); + Button *button = memnew(Button); + button->set_text(p_text); + button->set_toggle_mode(true); + button->set_pressed(true); + + // Don't show focus, it stands out too much and remains visible which can be confusing. + button->add_theme_style_override("focus", memnew(StyleBoxEmpty)); + + // Make it look similar to toolbar buttons. + button->set_theme_type_variation(SceneStringName(FlatButton)); + + button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_channel_button_toggled)); + p_parent->add_child(button); + channel_buttons[p_channel_index] = button; +} + +void ColorChannelSelector::on_toggled(bool p_pressed) { + panel->set_visible(p_pressed); +} + +void ColorChannelSelector::_bind_methods() { + ADD_SIGNAL(MethodInfo("selected_channels_changed")); +} diff --git a/godot/editor/plugins/color_channel_selector.h b/godot/editor/plugins/color_channel_selector.h new file mode 100644 index 00000000..f1498d30 --- /dev/null +++ b/godot/editor/plugins/color_channel_selector.h @@ -0,0 +1,65 @@ +/**************************************************************************/ +/* color_channel_selector.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 COLOR_CHANNEL_SELECTOR_H +#define COLOR_CHANNEL_SELECTOR_H + +#include "scene/gui/box_container.h" + +class PanelContainer; +class Button; + +class ColorChannelSelector : public HBoxContainer { + GDCLASS(ColorChannelSelector, HBoxContainer); + + static const unsigned int CHANNEL_COUNT = 4; + +public: + ColorChannelSelector(); + + void set_available_channels_mask(uint32_t p_mask); + uint32_t get_selected_channels_mask() const; + Vector4 get_selected_channel_factors() const; + +private: + void _notification(int p_what); + + void on_channel_button_toggled(bool p_unused_pressed); + void create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent); + void on_toggled(bool p_pressed); + + static void _bind_methods(); + + Button *channel_buttons[CHANNEL_COUNT] = {}; + PanelContainer *panel = nullptr; + Button *toggle_button = nullptr; +}; + +#endif // COLOR_CHANNEL_SELECTOR_H diff --git a/godot/editor/plugins/gizmos/spring_bone_3d_gizmo_plugin.cpp b/godot/editor/plugins/gizmos/spring_bone_3d_gizmo_plugin.cpp index 1cb943b0..e98a5a52 100644 --- a/godot/editor/plugins/gizmos/spring_bone_3d_gizmo_plugin.cpp +++ b/godot/editor/plugins/gizmos/spring_bone_3d_gizmo_plugin.cpp @@ -140,7 +140,7 @@ Ref SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_s Transform3D parent_global_pose = p_skeleton->get_bone_global_rest(prev_bone); Transform3D global_pose = p_skeleton->get_bone_global_rest(current_bone); draw_line(surface_tool, parent_global_pose.origin, global_pose.origin, bone_color); - draw_sphere(surface_tool, global_pose.basis, global_pose.origin, p_simulator->get_joint_radius(i, j), bone_color); + draw_sphere(surface_tool, global_pose.basis, global_pose.origin, p_simulator->get_joint_radius(i, j - 1), bone_color); } if (j == joint_end && p_simulator->is_end_bone_extended(i) && p_simulator->get_end_bone_length(i) > 0) { Vector3 axis = p_simulator->get_end_bone_axis(current_bone, p_simulator->get_end_bone_direction(i)); @@ -153,7 +153,7 @@ Ref SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_s Transform3D global_pose = p_skeleton->get_bone_global_rest(current_bone); axis = global_pose.xform(axis * p_simulator->get_end_bone_length(i)); draw_line(surface_tool, global_pose.origin, axis, bone_color); - draw_sphere(surface_tool, global_pose.basis, axis, p_simulator->get_end_bone_tip_radius(i), bone_color); + draw_sphere(surface_tool, global_pose.basis, axis, p_simulator->get_joint_radius(i, j), bone_color); } else { bones[0] = current_bone; surface_tool->set_bones(bones); diff --git a/godot/editor/plugins/script_text_editor.cpp b/godot/editor/plugins/script_text_editor.cpp index fa16aba6..fd5d1cc3 100644 --- a/godot/editor/plugins/script_text_editor.cpp +++ b/godot/editor/plugins/script_text_editor.cpp @@ -757,7 +757,7 @@ void ScriptTextEditor::_update_bookmark_list() { line = line.substr(0, 50); } - bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - `" + line + "`"); + bookmarks_menu->add_item(String::num_int64(bookmark_list[i] + 1) + " - `" + line + "`"); bookmarks_menu->set_item_metadata(-1, bookmark_list[i]); } } @@ -912,7 +912,7 @@ void ScriptTextEditor::_update_breakpoint_list() { line = line.substr(0, 50); } - breakpoints_menu->add_item(String::num((int)breakpoint_list[i] + 1) + " - `" + line + "`"); + breakpoints_menu->add_item(String::num_int64(breakpoint_list[i] + 1) + " - `" + line + "`"); breakpoints_menu->set_item_metadata(-1, breakpoint_list[i]); } } @@ -1102,6 +1102,11 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) { } void ScriptTextEditor::_show_symbol_tooltip(const String &p_symbol, int p_row, int p_column) { + if (p_symbol.begins_with("res://") || p_symbol.begins_with("uid://")) { + EditorHelpBitTooltip::show_tooltip(code_editor->get_text_editor(), "resource||" + p_symbol); + return; + } + Node *base = get_tree()->get_edited_scene_root(); if (base) { base = _find_node_for_script(base, base, script); diff --git a/godot/editor/plugins/sprite_frames_editor_plugin.cpp b/godot/editor/plugins/sprite_frames_editor_plugin.cpp index d3b051fc..5cea634e 100644 --- a/godot/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/godot/editor/plugins/sprite_frames_editor_plugin.cpp @@ -38,6 +38,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/filesystem_dock.h" #include "editor/gui/editor_bottom_panel.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" @@ -1309,10 +1310,39 @@ void SpriteFramesEditor::_frame_list_gui_input(const Ref &p_event) { _zoom_out(); // Don't scroll down after zooming out. accept_event(); + } else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { + Point2 pos = mb->get_position(); + right_clicked_frame = frame_list->get_item_at_position(pos, true); + if (right_clicked_frame != -1) { + if (!menu) { + menu = memnew(PopupMenu); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(this, &SpriteFramesEditor::_menu_selected)); + menu->add_icon_item(get_editor_theme_icon(SNAME("ShowInFileSystem")), TTRC("Show in FileSystem")); + } + + menu->set_position(get_screen_position() + get_local_mouse_position()); + menu->popup(); + } } } } +void SpriteFramesEditor::_menu_selected(int p_index) { + switch (p_index) { + case MENU_SHOW_IN_FILESYSTEM: { + String path = frames->get_frame_texture(edited_anim, right_clicked_frame)->get_path(); + // Check if the file is an atlas resource, if it is find the source texture. + Ref at = frames->get_frame_texture(edited_anim, right_clicked_frame); + while (at.is_valid() && at->get_atlas().is_valid()) { + path = at->get_atlas()->get_path(); + at = at->get_atlas(); + } + FileSystemDock::get_singleton()->navigate_to_path(path); + } break; + } +} + void SpriteFramesEditor::_frame_list_item_selected(int p_index, bool p_selected) { if (updating) { return; diff --git a/godot/editor/plugins/sprite_frames_editor_plugin.h b/godot/editor/plugins/sprite_frames_editor_plugin.h index 9f5f9579..89629448 100644 --- a/godot/editor/plugins/sprite_frames_editor_plugin.h +++ b/godot/editor/plugins/sprite_frames_editor_plugin.h @@ -87,6 +87,12 @@ class SpriteFramesEditor : public HSplitContainer { FRAME_ORDER_BOTTOM_TOP_RIGHT_LEFT, }; + enum { + MENU_SHOW_IN_FILESYSTEM, + }; + + int right_clicked_frame = -1; + bool read_only = false; Ref autoplay_icon; @@ -137,6 +143,8 @@ class SpriteFramesEditor : public HSplitContainer { AcceptDialog *dialog = nullptr; + PopupMenu *menu = nullptr; + StringName edited_anim; ConfirmationDialog *delete_dialog = nullptr; @@ -219,6 +227,8 @@ class SpriteFramesEditor : public HSplitContainer { void _frame_list_gui_input(const Ref &p_event); void _frame_list_item_selected(int p_index, bool p_selected); + void _menu_selected(int p_index); + void _zoom_in(); void _zoom_out(); void _zoom_reset(); diff --git a/godot/editor/plugins/text_editor.cpp b/godot/editor/plugins/text_editor.cpp index ee2d592e..14c1b698 100644 --- a/godot/editor/plugins/text_editor.cpp +++ b/godot/editor/plugins/text_editor.cpp @@ -229,7 +229,7 @@ void TextEditor::_update_bookmark_list() { line = line.substr(0, 50); } - bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\""); + bookmarks_menu->add_item(String::num_int64(bookmark_list[i] + 1) + " - \"" + line + "\""); bookmarks_menu->set_item_metadata(-1, bookmark_list[i]); } } diff --git a/godot/editor/plugins/text_shader_editor.cpp b/godot/editor/plugins/text_shader_editor.cpp index 2bf14203..693cf243 100644 --- a/godot/editor/plugins/text_shader_editor.cpp +++ b/godot/editor/plugins/text_shader_editor.cpp @@ -1075,7 +1075,7 @@ void TextShaderEditor::_update_bookmark_list() { line = line.substr(0, 50); } - bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\""); + bookmarks_menu->add_item(String::num_int64(bookmark_list[i] + 1) + " - \"" + line + "\""); bookmarks_menu->set_item_metadata(-1, bookmark_list[i]); } } diff --git a/godot/editor/plugins/texture_3d_editor_plugin.cpp b/godot/editor/plugins/texture_3d_editor_plugin.cpp index 8eace986..dd4498a3 100644 --- a/godot/editor/plugins/texture_3d_editor_plugin.cpp +++ b/godot/editor/plugins/texture_3d_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "texture_3d_editor_plugin.h" #include "editor/editor_string_names.h" +#include "editor/plugins/color_channel_selector.h" #include "editor/themes/editor_scale.h" #include "scene/gui/label.h" @@ -44,8 +45,29 @@ constexpr const char *texture_3d_shader = R"( uniform sampler3D tex; uniform float layer; + uniform vec4 u_channel_factors = vec4(1.0); + + vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; + } + void fragment() { COLOR = textureLod(tex, vec3(UV, layer), 0.0); + COLOR = filter_preview_colors(COLOR, u_channel_factors); } )"; @@ -94,6 +116,8 @@ void Texture3DEditor::_update_material(bool p_texture_changed) { if (p_texture_changed) { material->set_shader_parameter("tex", texture->get_rid()); } + + material->set_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors()); } void Texture3DEditor::_make_shaders() { @@ -138,30 +162,44 @@ void Texture3DEditor::_update_gui() { layer->set_max(texture->get_depth() - 1); - const String format = Image::get_format_name(texture->get_format()); + const Image::Format format = texture->get_format(); + const String format_name = Image::get_format_name(format); if (texture->has_mipmaps()) { - const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), texture->get_format()); - const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), true) * texture->get_depth(); + const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format); + const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_depth(); info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"), texture->get_width(), texture->get_height(), texture->get_depth(), - format, + format_name, mip_count, String::humanize_size(memory))); } else { - const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), false) * texture->get_depth(); + const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_depth(); info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"), texture->get_width(), texture->get_height(), texture->get_depth(), - format, + format_name, String::humanize_size(memory))); } + + const uint32_t components_mask = Image::get_format_component_mask(format); + if (is_power_of_2(components_mask)) { + // Only one channel available, no point in showing a channel selector. + channel_selector->hide(); + } else { + channel_selector->show(); + channel_selector->set_available_channels_mask(components_mask); + } +} + +void Texture3DEditor::on_selected_channels_changed() { + _update_material(false); } void Texture3DEditor::edit(Ref p_texture) { @@ -215,6 +253,11 @@ Texture3DEditor::Texture3DEditor() { add_child(layer); + channel_selector = memnew(ColorChannelSelector); + channel_selector->connect("selected_channels_changed", callable_mp(this, &Texture3DEditor::on_selected_channels_changed)); + channel_selector->set_anchors_preset(Control::PRESET_TOP_LEFT); + add_child(channel_selector); + info = memnew(Label); info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1)); info->add_theme_color_override("font_shadow_color", Color(0, 0, 0)); diff --git a/godot/editor/plugins/texture_3d_editor_plugin.h b/godot/editor/plugins/texture_3d_editor_plugin.h index 4124fc58..58424955 100644 --- a/godot/editor/plugins/texture_3d_editor_plugin.h +++ b/godot/editor/plugins/texture_3d_editor_plugin.h @@ -37,6 +37,8 @@ #include "scene/resources/shader.h" #include "scene/resources/texture.h" +class ColorChannelSelector; + class Texture3DEditor : public Control { GDCLASS(Texture3DEditor, Control); @@ -49,6 +51,8 @@ class Texture3DEditor : public Control { Control *texture_rect = nullptr; + ColorChannelSelector *channel_selector = nullptr; + bool setting = false; void _make_shaders(); @@ -67,6 +71,8 @@ class Texture3DEditor : public Control { void _update_material(bool p_texture_changed); void _update_gui(); + void on_selected_channels_changed(); + protected: void _notification(int p_what); diff --git a/godot/editor/plugins/texture_editor_plugin.cpp b/godot/editor/plugins/texture_editor_plugin.cpp index 8b0c69e9..561146be 100644 --- a/godot/editor/plugins/texture_editor_plugin.cpp +++ b/godot/editor/plugins/texture_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "texture_editor_plugin.h" #include "editor/editor_string_names.h" +#include "editor/plugins/color_channel_selector.h" #include "editor/themes/editor_scale.h" #include "scene/gui/aspect_ratio_container.h" #include "scene/gui/color_rect.h" @@ -41,6 +42,36 @@ #include "scene/resources/compressed_texture.h" #include "scene/resources/image_texture.h" #include "scene/resources/portable_compressed_texture.h" +#include "scene/resources/style_box_flat.h" + +constexpr const char *texture_2d_shader = R"( +shader_type canvas_item; +render_mode blend_mix; + +uniform vec4 u_channel_factors = vec4(1.0); + +vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; +} + +void fragment() { + COLOR = filter_preview_colors(texture(TEXTURE, UV), u_channel_factors); +} +)"; TextureRect *TexturePreview::get_texture_display() { return texture_display; @@ -72,8 +103,8 @@ void TexturePreview::_notification(int p_what) { void TexturePreview::_draw_outline() { const float outline_width = Math::round(EDSCALE); - const Rect2 outline_rect = Rect2(Vector2(), texture_display->get_size()).grow(outline_width * 0.5); - texture_display->draw_rect(outline_rect, cached_outline_color, false, outline_width); + const Rect2 outline_rect = Rect2(Vector2(), outline_overlay->get_size()).grow(outline_width * 0.5); + outline_overlay->draw_rect(outline_rect, cached_outline_color, false, outline_width); } void TexturePreview::_update_texture_display_ratio() { @@ -82,25 +113,49 @@ void TexturePreview::_update_texture_display_ratio() { } } -void TexturePreview::_update_metadata_label_text() { - const Ref texture = texture_display->get_texture(); +static Image::Format get_texture_2d_format(const Ref &p_texture) { + const Ref image_texture = p_texture; + if (image_texture.is_valid()) { + return image_texture->get_format(); + } - String format; - if (Object::cast_to(*texture)) { - format = Image::get_format_name(Object::cast_to(*texture)->get_format()); - } else if (Object::cast_to(*texture)) { - format = Image::get_format_name(Object::cast_to(*texture)->get_format()); - } else { - format = texture->get_class(); + const Ref compressed_texture = p_texture; + if (compressed_texture.is_valid()) { + return compressed_texture->get_format(); } - const Ref image = texture->get_image(); + // AtlasTexture? + + // Unknown + return Image::FORMAT_MAX; +} + +static int get_texture_mipmaps_count(const Ref &p_texture) { + ERR_FAIL_COND_V(p_texture.is_null(), -1); + // We are having to download the image only to get its mipmaps count. It would be nice if we didn't have to. + Ref image = p_texture->get_image(); if (image.is_valid()) { - const int mipmaps = image->get_mipmap_count(); + return image->get_mipmap_count(); + } + return -1; +} + +void TexturePreview::_update_metadata_label_text() { + const Ref texture = texture_display->get_texture(); + ERR_FAIL_COND(texture.is_null()); + + const Image::Format format = get_texture_2d_format(texture.ptr()); + + const String format_name = format != Image::FORMAT_MAX ? Image::get_format_name(format) : texture->get_class(); + + const Vector2i resolution = texture->get_size(); + const int mipmaps = get_texture_mipmaps_count(texture); + + if (format != Image::FORMAT_MAX) { // Avoid signed integer overflow that could occur with huge texture sizes by casting everything to uint64_t. - uint64_t memory = uint64_t(image->get_width()) * uint64_t(image->get_height()) * uint64_t(Image::get_format_pixel_size(image->get_format())); + uint64_t memory = uint64_t(resolution.x) * uint64_t(resolution.y) * uint64_t(Image::get_format_pixel_size(format)); // Handle VRAM-compressed formats that are stored with 4 bpp. - memory >>= Image::get_format_pixel_rshift(image->get_format()); + memory >>= Image::get_format_pixel_rshift(format); float mipmaps_multiplier = 1.0; float mipmap_increase = 0.25; @@ -117,7 +172,7 @@ void TexturePreview::_update_metadata_label_text() { vformat(String::utf8("%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"), texture->get_width(), texture->get_height(), - format, + format_name, mipmaps, String::humanize_size(memory))); } else { @@ -127,7 +182,7 @@ void TexturePreview::_update_metadata_label_text() { vformat(String::utf8("%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"), texture->get_width(), texture->get_height(), - format, + format_name, String::humanize_size(memory))); } } else { @@ -135,10 +190,14 @@ void TexturePreview::_update_metadata_label_text() { vformat(String::utf8("%d×%d %s"), texture->get_width(), texture->get_height(), - format)); + format_name)); } } +void TexturePreview::on_selected_channels_changed() { + material->set_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors()); +} + TexturePreview::TexturePreview(Ref p_texture, bool p_show_metadata) { set_custom_minimum_size(Size2(0.0, 256.0) * EDSCALE); @@ -163,19 +222,48 @@ TexturePreview::TexturePreview(Ref p_texture, bool p_show_metadata) { checkerboard->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED); centering_container->add_child(checkerboard); + { + Ref shader; + shader.instantiate(); + shader->set_code(texture_2d_shader); + + material.instantiate(); + material->set_shader(shader); + material->set_shader_parameter("u_channel_factors", Vector4(1, 1, 1, 1)); + } + texture_display = memnew(TextureRect); texture_display->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); texture_display->set_texture(p_texture); texture_display->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); + texture_display->set_material(material); centering_container->add_child(texture_display); - texture_display->connect(SceneStringName(draw), callable_mp(this, &TexturePreview::_draw_outline)); + // Creating a separate control so it is not affected by the filtering shader. + outline_overlay = memnew(Control); + centering_container->add_child(outline_overlay); + + outline_overlay->connect(SceneStringName(draw), callable_mp(this, &TexturePreview::_draw_outline)); if (p_texture.is_valid()) { _update_texture_display_ratio(); p_texture->connect_changed(callable_mp(this, &TexturePreview::_update_texture_display_ratio)); } + // Null can be passed by `Camera3DPreview` (which immediately after sets a texture anyways). + const Image::Format format = p_texture.is_valid() ? get_texture_2d_format(p_texture.ptr()) : Image::FORMAT_MAX; + const uint32_t components_mask = format != Image::FORMAT_MAX ? Image::get_format_component_mask(format) : 0xf; + + // Add color channel selector at the bottom left if more than 1 channel is available. + if (p_show_metadata && !is_power_of_2(components_mask)) { + channel_selector = memnew(ColorChannelSelector); + channel_selector->connect("selected_channels_changed", callable_mp(this, &TexturePreview::on_selected_channels_changed)); + channel_selector->set_h_size_flags(Control::SIZE_SHRINK_BEGIN); + channel_selector->set_v_size_flags(Control::SIZE_SHRINK_BEGIN); + channel_selector->set_available_channels_mask(components_mask); + add_child(channel_selector); + } + if (p_show_metadata) { metadata_label = memnew(Label); diff --git a/godot/editor/plugins/texture_editor_plugin.h b/godot/editor/plugins/texture_editor_plugin.h index 01008108..d27cb4ea 100644 --- a/godot/editor/plugins/texture_editor_plugin.h +++ b/godot/editor/plugins/texture_editor_plugin.h @@ -39,6 +39,8 @@ class AspectRatioContainer; class ColorRect; class TextureRect; +class ShaderMaterial; +class ColorChannelSelector; class TexturePreview : public MarginContainer { GDCLASS(TexturePreview, MarginContainer); @@ -47,10 +49,14 @@ class TexturePreview : public MarginContainer { TextureRect *texture_display = nullptr; MarginContainer *margin_container = nullptr; + Control *outline_overlay = nullptr; AspectRatioContainer *centering_container = nullptr; ColorRect *bg_rect = nullptr; TextureRect *checkerboard = nullptr; Label *metadata_label = nullptr; + Ref material; + + ColorChannelSelector *channel_selector = nullptr; Color cached_outline_color; @@ -61,6 +67,8 @@ class TexturePreview : public MarginContainer { void _notification(int p_what); void _update_texture_display_ratio(); + void on_selected_channels_changed(); + public: TextureRect *get_texture_display(); TexturePreview(Ref p_texture, bool p_show_metadata); diff --git a/godot/editor/plugins/texture_layered_editor_plugin.cpp b/godot/editor/plugins/texture_layered_editor_plugin.cpp index 0eadc939..2ecd5216 100644 --- a/godot/editor/plugins/texture_layered_editor_plugin.cpp +++ b/godot/editor/plugins/texture_layered_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "texture_layered_editor_plugin.h" #include "editor/editor_string_names.h" +#include "editor/plugins/color_channel_selector.h" #include "editor/themes/editor_scale.h" #include "scene/gui/label.h" @@ -43,9 +44,29 @@ constexpr const char *array_2d_shader = R"( uniform sampler2DArray tex; uniform float layer; + uniform vec4 u_channel_factors = vec4(1.0); + + vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; + } void fragment() { COLOR = textureLod(tex, vec3(UV, layer), 0.0); + COLOR = filter_preview_colors(COLOR, u_channel_factors); } )"; @@ -58,9 +79,30 @@ constexpr const char *cubemap_shader = R"( uniform vec3 normal; uniform mat3 rot; + uniform vec4 u_channel_factors = vec4(1.0); + + vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; + } + void fragment() { vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z)); COLOR = textureLod(tex, n, 0.0); + COLOR = filter_preview_colors(COLOR, u_channel_factors); } )"; @@ -73,9 +115,30 @@ constexpr const char *cubemap_array_shader = R"( uniform mat3 rot; uniform float layer; + uniform vec4 u_channel_factors = vec4(1.0); + + vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; + } + void fragment() { vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z)); COLOR = textureLod(tex, vec4(n, layer), 0.0); + COLOR = filter_preview_colors(COLOR, u_channel_factors); } )"; @@ -102,7 +165,8 @@ void TextureLayeredEditor::_update_gui() { _texture_rect_update_area(); - const String format = Image::get_format_name(texture->get_format()); + const Image::Format format = texture->get_format(); + const String format_name = Image::get_format_name(format); String texture_info; switch (texture->get_layered_type()) { @@ -113,7 +177,7 @@ void TextureLayeredEditor::_update_gui() { texture->get_width(), texture->get_height(), texture->get_layers(), - format); + format_name); } break; case TextureLayered::LAYERED_TYPE_CUBEMAP: { @@ -122,7 +186,7 @@ void TextureLayeredEditor::_update_gui() { texture_info = vformat(String::utf8("%d×%d %s\n"), texture->get_width(), texture->get_height(), - format); + format_name); } break; case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: { @@ -132,7 +196,7 @@ void TextureLayeredEditor::_update_gui() { texture->get_width(), texture->get_height(), texture->get_layers() / 6, - format); + format_name); } break; @@ -141,21 +205,30 @@ void TextureLayeredEditor::_update_gui() { } if (texture->has_mipmaps()) { - const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), texture->get_format()); - const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), true) * texture->get_layers(); + const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format); + const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_layers(); texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"), mip_count, String::humanize_size(memory)); } else { - const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), false) * texture->get_layers(); + const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_layers(); texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"), String::humanize_size(memory)); } info->set_text(texture_info); + + const uint32_t components_mask = Image::get_format_component_mask(format); + if (is_power_of_2(components_mask)) { + // Only one channel available, no point in showing a channel selector. + channel_selector->hide(); + } else { + channel_selector->show(); + channel_selector->set_available_channels_mask(components_mask); + } } void TextureLayeredEditor::_notification(int p_what) { @@ -212,6 +285,15 @@ void TextureLayeredEditor::_update_material(bool p_texture_changed) { if (p_texture_changed) { materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid()); } + + const Vector4 channel_factors = channel_selector->get_selected_channel_factors(); + for (unsigned int i = 0; i < 3; ++i) { + materials[i]->set_shader_parameter("u_channel_factors", channel_factors); + } +} + +void TextureLayeredEditor::on_selected_channels_changed() { + _update_material(false); } void TextureLayeredEditor::_make_shaders() { @@ -309,6 +391,11 @@ TextureLayeredEditor::TextureLayeredEditor() { add_child(layer); + channel_selector = memnew(ColorChannelSelector); + channel_selector->connect("selected_channels_changed", callable_mp(this, &TextureLayeredEditor::on_selected_channels_changed)); + channel_selector->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT); + add_child(channel_selector); + info = memnew(Label); info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1)); info->add_theme_color_override("font_shadow_color", Color(0, 0, 0)); diff --git a/godot/editor/plugins/texture_layered_editor_plugin.h b/godot/editor/plugins/texture_layered_editor_plugin.h index 5648a04c..d2264175 100644 --- a/godot/editor/plugins/texture_layered_editor_plugin.h +++ b/godot/editor/plugins/texture_layered_editor_plugin.h @@ -37,6 +37,8 @@ #include "scene/resources/shader.h" #include "scene/resources/texture.h" +class ColorChannelSelector; + class TextureLayeredEditor : public Control { GDCLASS(TextureLayeredEditor, Control); @@ -53,6 +55,8 @@ class TextureLayeredEditor : public Control { bool setting = false; + ColorChannelSelector *channel_selector = nullptr; + void _make_shaders(); void _update_material(bool p_texture_changed); @@ -69,6 +73,8 @@ class TextureLayeredEditor : public Control { void _update_gui(); + void on_selected_channels_changed(); + protected: void _notification(int p_what); virtual void gui_input(const Ref &p_event) override; diff --git a/godot/editor/progress_dialog.cpp b/godot/editor/progress_dialog.cpp index 2f345e51..ae061167 100644 --- a/godot/editor/progress_dialog.cpp +++ b/godot/editor/progress_dialog.cpp @@ -35,6 +35,8 @@ #include "editor/editor_node.h" #include "editor/themes/editor_scale.h" #include "main/main.h" +#include "scene/gui/panel_container.h" +#include "scene/main/window.h" #include "servers/display_server.h" void BackgroundProgress::_add_task(const String &p_task, const String &p_label, int p_steps) { @@ -126,6 +128,21 @@ void BackgroundProgress::end_task(const String &p_task) { ProgressDialog *ProgressDialog::singleton = nullptr; +void ProgressDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + Ref style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu")); + main_border_size = style->get_minimum_size(); + main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT)); + main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT)); + main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); + main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); + + center_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), "PopupPanel")); + } break; + } +} + void ProgressDialog::_update_ui() { // Run main loop for two frames. if (is_inside_tree()) { @@ -135,33 +152,33 @@ void ProgressDialog::_update_ui() { } void ProgressDialog::_popup() { + // Activate processing of all inputs in EditorNode, and the EditorNode::input method + // will discard every key input. + EditorNode::get_singleton()->set_process_input(true); + // Disable all other windows to prevent interaction with them. + for (Window *w : host_windows) { + w->set_process_mode(PROCESS_MODE_DISABLED); + } + Size2 ms = main->get_combined_minimum_size(); ms.width = MAX(500 * EDSCALE, ms.width); + ms += main_border_size; - Ref style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu")); - ms += style->get_minimum_size(); - - main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT)); - main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT)); - main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); - main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); + center_panel->set_custom_minimum_size(ms); - if (is_inside_tree()) { - Rect2i adjust = _popup_adjust_rect(); - if (adjust != Rect2i()) { - set_position(adjust.position); - set_size(adjust.size); - } - } else { - for (Window *window : host_windows) { - if (window->has_focus()) { - popup_exclusive_centered(window, ms); - return; - } - } - // No host window found, use main window. - EditorInterface::get_singleton()->popup_dialog_centered(this, ms); + Window *current_window = Window::get_from_id(DisplayServer::get_singleton()->get_focused_window()); + if (!current_window) { + current_window = get_tree()->get_root(); } + + reparent(current_window); + + // Ensures that events are properly released before the dialog blocks input. + bool window_is_input_disabled = current_window->is_input_disabled(); + current_window->set_disable_input(!window_is_input_disabled); + current_window->set_disable_input(window_is_input_disabled); + + show(); } void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { @@ -231,6 +248,10 @@ void ProgressDialog::end_task(const String &p_task) { if (tasks.is_empty()) { hide(); + EditorNode::get_singleton()->set_process_input(false); + for (Window *w : host_windows) { + w->set_process_mode(PROCESS_MODE_INHERIT); + } } else { _popup(); } @@ -241,17 +262,31 @@ void ProgressDialog::add_host_window(Window *p_window) { host_windows.push_back(p_window); } +void ProgressDialog::remove_host_window(Window *p_window) { + ERR_FAIL_NULL(p_window); + host_windows.erase(p_window); +} + void ProgressDialog::_cancel_pressed() { canceled = true; } ProgressDialog::ProgressDialog() { - main = memnew(VBoxContainer); - add_child(main); - main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - set_exclusive(true); - set_flag(Window::FLAG_POPUP, false); + // We want to cover the entire screen to prevent the user from interacting with the Editor. + set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + // Be sure it's the top most component. + set_z_index(RS::CANVAS_ITEM_Z_MAX); singleton = this; + hide(); + + center_panel = memnew(PanelContainer); + add_child(center_panel); + center_panel->set_h_size_flags(SIZE_SHRINK_BEGIN); + center_panel->set_v_size_flags(SIZE_SHRINK_BEGIN); + + main = memnew(VBoxContainer); + center_panel->add_child(main); + cancel_hb = memnew(HBoxContainer); main->add_child(cancel_hb); cancel_hb->hide(); diff --git a/godot/editor/progress_dialog.h b/godot/editor/progress_dialog.h index 355812b0..87fb02b3 100644 --- a/godot/editor/progress_dialog.h +++ b/godot/editor/progress_dialog.h @@ -33,8 +33,8 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/center_container.h" #include "scene/gui/label.h" -#include "scene/gui/popup.h" #include "scene/gui/progress_bar.h" class BackgroundProgress : public HBoxContainer { @@ -64,8 +64,10 @@ class BackgroundProgress : public HBoxContainer { BackgroundProgress() {} }; -class ProgressDialog : public PopupPanel { - GDCLASS(ProgressDialog, PopupPanel); +class PanelContainer; + +class ProgressDialog : public CenterContainer { + GDCLASS(ProgressDialog, CenterContainer); struct Task { String task; VBoxContainer *vb = nullptr; @@ -77,10 +79,13 @@ class ProgressDialog : public PopupPanel { Button *cancel = nullptr; HashMap tasks; + PanelContainer *center_panel = nullptr; VBoxContainer *main = nullptr; LocalVector host_windows; + Size2 main_border_size; + static ProgressDialog *singleton; void _popup(); @@ -89,6 +94,9 @@ class ProgressDialog : public PopupPanel { void _update_ui(); bool canceled = false; +protected: + void _notification(int p_what); + public: static ProgressDialog *get_singleton() { return singleton; } void add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false); @@ -96,6 +104,7 @@ class ProgressDialog : public PopupPanel { void end_task(const String &p_task); void add_host_window(Window *p_window); + void remove_host_window(Window *p_window); ProgressDialog(); }; diff --git a/godot/editor/project_manager.cpp b/godot/editor/project_manager.cpp index cde0c96b..20f85679 100644 --- a/godot/editor/project_manager.cpp +++ b/godot/editor/project_manager.cpp @@ -149,7 +149,7 @@ void ProjectManager::_build_icon_type_cache(Ref p_theme) { // Main layout. -void ProjectManager::_update_size_limits() { +void ProjectManager::_update_size_limits(bool p_custom_res) { const Size2 minimum_size = Size2(720, 450) * EDSCALE; const Size2 default_size = Size2(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT) * EDSCALE; @@ -159,20 +159,21 @@ void ProjectManager::_update_size_limits() { // Calling Window methods this early doesn't sync properties with DS. w->set_min_size(minimum_size); DisplayServer::get_singleton()->window_set_min_size(minimum_size); - if (DisplayServer::get_singleton()->window_get_size() == default_size) { + if (!p_custom_res) { // Only set window size if it currently matches the default, which is defined in `main/main.cpp`. // This allows CLI arguments to override the window size. w->set_size(default_size); DisplayServer::get_singleton()->window_set_size(default_size); } } + Size2 real_size = DisplayServer::get_singleton()->window_get_size(); Rect2i screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(DisplayServer::get_singleton()->window_get_current_screen()); if (screen_rect.size != Vector2i()) { // Center the window on the screen. Vector2i window_position; - window_position.x = screen_rect.position.x + (screen_rect.size.x - default_size.x) / 2; - window_position.y = screen_rect.position.y + (screen_rect.size.y - default_size.y) / 2; + window_position.x = screen_rect.position.x + (screen_rect.size.x - real_size.x) / 2; + window_position.y = screen_rect.position.y + (screen_rect.size.y - real_size.y) / 2; DisplayServer::get_singleton()->window_set_position(window_position); // Limit popup menus to prevent unusably long lists. @@ -612,7 +613,13 @@ void ProjectManager::_open_selected_projects_check_warnings() { } void ProjectManager::_open_selected_projects_check_recovery_mode() { - ProjectList::Item project = project_list->get_selected_projects()[0]; + Vector selected_projects = project_list->get_selected_projects(); + + if (selected_projects.is_empty()) { + return; + } + + const ProjectList::Item &project = selected_projects[0]; if (project.missing) { return; } @@ -1152,7 +1159,7 @@ void ProjectManager::_titlebar_resized() { // Object methods. -ProjectManager::ProjectManager() { +ProjectManager::ProjectManager(bool p_custom_res) { singleton = this; set_translation_domain("godot.editor"); @@ -1740,7 +1747,7 @@ ProjectManager::ProjectManager() { title_bar->connect(SceneStringName(item_rect_changed), callable_mp(this, &ProjectManager::_titlebar_resized)); } - _update_size_limits(); + _update_size_limits(p_custom_res); } ProjectManager::~ProjectManager() { diff --git a/godot/editor/project_manager.h b/godot/editor/project_manager.h index 8ae76475..73a77e3d 100644 --- a/godot/editor/project_manager.h +++ b/godot/editor/project_manager.h @@ -70,7 +70,7 @@ class ProjectManager : public Control { Ref theme; - void _update_size_limits(); + void _update_size_limits(bool p_custom_res); void _update_theme(bool p_skip_creation = false); void _titlebar_resized(); @@ -262,7 +262,7 @@ class ProjectManager : public Control { void add_new_tag(const String &p_tag); - ProjectManager(); + ProjectManager(bool p_custom_res); ~ProjectManager(); }; diff --git a/godot/editor/project_manager/quick_settings_dialog.cpp b/godot/editor/project_manager/quick_settings_dialog.cpp index 041713b6..a805f6c7 100644 --- a/godot/editor/project_manager/quick_settings_dialog.cpp +++ b/godot/editor/project_manager/quick_settings_dialog.cpp @@ -97,6 +97,7 @@ void QuickSettingsDialog::_update_current_values() { if (current_theme == theme_value) { theme_option_button->set_text(current_theme); theme_option_button->select(i); + theme_option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); custom_theme_label->set_visible(current_theme == "Custom"); } diff --git a/godot/editor/window_wrapper.cpp b/godot/editor/window_wrapper.cpp index 97ecda9b..37a3efc9 100644 --- a/godot/editor/window_wrapper.cpp +++ b/godot/editor/window_wrapper.cpp @@ -336,6 +336,7 @@ WindowWrapper::WindowWrapper() { } window = memnew(Window); + window_id = window->get_instance_id(); window->set_wrap_controls(true); add_child(window); @@ -354,6 +355,12 @@ WindowWrapper::WindowWrapper() { ProgressDialog::get_singleton()->add_host_window(window); } +WindowWrapper::~WindowWrapper() { + if (ObjectDB::get_instance(window_id)) { + ProgressDialog::get_singleton()->remove_host_window(window); + } +} + // ScreenSelect void ScreenSelect::_build_advanced_menu() { diff --git a/godot/editor/window_wrapper.h b/godot/editor/window_wrapper.h index 0a9f0f21..3f4e9385 100644 --- a/godot/editor/window_wrapper.h +++ b/godot/editor/window_wrapper.h @@ -44,6 +44,7 @@ class WindowWrapper : public MarginContainer { Control *wrapped_control = nullptr; MarginContainer *margins = nullptr; Window *window = nullptr; + ObjectID window_id; Panel *window_background = nullptr; @@ -84,6 +85,7 @@ class WindowWrapper : public MarginContainer { void grab_window_focus(); WindowWrapper(); + ~WindowWrapper(); }; class ScreenSelect : public Button { diff --git a/godot/main/main.cpp b/godot/main/main.cpp index 40b437fa..0ff135a0 100644 --- a/godot/main/main.cpp +++ b/godot/main/main.cpp @@ -230,6 +230,8 @@ static bool init_use_custom_pos = false; static bool init_use_custom_screen = false; static Vector2 init_custom_pos; static int64_t init_embed_parent_window_id = 0; +static bool use_custom_res = true; +static bool force_res = false; // Debug @@ -1019,8 +1021,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph String remotefs_pass; Vector breakpoints; - bool use_custom_res = true; - bool force_res = false; bool delta_smoothing_override = false; String default_renderer = ""; @@ -4318,7 +4318,7 @@ int Main::start() { translation_server->get_editor_domain()->set_pseudolocalization_enabled(true); } - ProjectManager *pmanager = memnew(ProjectManager); + ProjectManager *pmanager = memnew(ProjectManager(force_res || use_custom_res)); ProgressDialog *progress_dialog = memnew(ProgressDialog); pmanager->add_child(progress_dialog); diff --git a/godot/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/godot/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index 1975bde1..040f7c6c 100644 --- a/godot/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/godot/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 054F8BE52D38852F00B81423 /* MetalFX.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 1F1575721F582BE20003B888 /* dylibs in Resources */ = {isa = PBXBuildFile; fileRef = 1F1575711F582BE20003B888 /* dylibs */; }; DEADBEEF2F582BE20003B888 /* $binary.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEADBEEF1F582BE20003B888 /* $binary.xcframework */; }; $modules_buildfile @@ -42,6 +43,7 @@ 1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "$binary.entitlements"; sourceTree = ""; }; 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = ""; }; 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MoltenVK; path = MoltenVK.xcframework; sourceTree = ""; }; + 054F8BE52D38852F00B81423 /* MetalFX.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalFX.framework; path = System/Library/Frameworks/MetalFX.framework; sourceTree = SDKROOT; }; D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "$binary.app"; sourceTree = BUILT_PRODUCTS_DIR; }; D0BCFE4318AEBDA2004A7AAE /* $binary-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "$binary-Info.plist"; sourceTree = ""; }; @@ -60,6 +62,7 @@ buildActionMask = 2147483647; files = ( 9039D3BE24C093AC0020482C /* MoltenVK.xcframework in Frameworks */, + 054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */, DEADBEEF2F582BE20003B888 /* $binary.xcframework */, $modules_buildphase $additional_pbx_frameworks_build @@ -94,6 +97,7 @@ isa = PBXGroup; children = ( 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */, + 054F8BE52D38852F00B81423 /* MetalFX.framework */, DEADBEEF1F582BE20003B888 /* $binary.xcframework */, $modules_buildgrp $additional_pbx_frameworks_refs diff --git a/godot/misc/extension_api_validation/4.3-stable.expected b/godot/misc/extension_api_validation/4.3-stable.expected index d210417f..7b5889f4 100644 --- a/godot/misc/extension_api_validation/4.3-stable.expected +++ b/godot/misc/extension_api_validation/4.3-stable.expected @@ -217,6 +217,13 @@ Validate extension JSON: Error: Field 'classes/Control/properties/offset_top': t Property type changed to float to match the actual internal API and documentation. +GH-99455 +-------- +Validate extension JSON: Error: Field 'classes/RenderingServer/methods/multimesh_allocate_data/arguments': size changed value in new API, from 5 to 6. + +Optional argument added to allow setting indirect draw mode on Multimesh. Compatibility method registered. + + GH-100129 --------- Validate extension JSON: Error: Field 'classes/NavigationServer2D/methods/query_path': is_const changed value in new API, from true to false. @@ -285,3 +292,20 @@ GH-101482 Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/set_table_column_expand/arguments': size changed value in new API, from 3 to 4. Added optional "shrink" argument. Compatibility method registered. + + +GH-100062 +-------- +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/index_buffer_create/arguments': size changed value in new API, from 4 to 5. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/uniform_buffer_create/arguments': size changed value in new API, from 2 to 3. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/vertex_buffer_create/arguments': size changed value in new API, from 3 to 4. + +Optional argument added. Compatibility methods registered. + + +GH-101531 +--------- +Validate extension JSON: API was removed: classes/EditorSceneFormatImporter/methods/_get_import_flags + +This virtual method, and the internal public `get_import_flags`, were never used by the engine, since it was open sourced. +So we're removing it despite the compat breakage as there's no way for users to rely on this affecting engine behavior. diff --git a/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp b/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp index 288ed145..3881fcdf 100644 --- a/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp +++ b/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp @@ -38,10 +38,6 @@ #include "modules/gltf/gltf_document.h" -uint32_t EditorSceneFormatImporterFBX2GLTF::get_import_flags() const { - return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; -} - void EditorSceneFormatImporterFBX2GLTF::get_extensions(List *r_extensions) const { r_extensions->push_back("fbx"); } diff --git a/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.h b/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.h index ce2bac6f..1b68ccfe 100644 --- a/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.h +++ b/godot/modules/fbx/editor/editor_scene_importer_fbx2gltf.h @@ -42,7 +42,6 @@ class EditorSceneFormatImporterFBX2GLTF : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterFBX2GLTF, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, diff --git a/godot/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/godot/modules/fbx/editor/editor_scene_importer_ufbx.cpp index 64075c06..9da03a50 100644 --- a/godot/modules/fbx/editor/editor_scene_importer_ufbx.cpp +++ b/godot/modules/fbx/editor/editor_scene_importer_ufbx.cpp @@ -37,10 +37,6 @@ #include "core/config/project_settings.h" -uint32_t EditorSceneFormatImporterUFBX::get_import_flags() const { - return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; -} - void EditorSceneFormatImporterUFBX::get_extensions(List *r_extensions) const { r_extensions->push_back("fbx"); } diff --git a/godot/modules/fbx/editor/editor_scene_importer_ufbx.h b/godot/modules/fbx/editor/editor_scene_importer_ufbx.h index 6e3eafc1..f81b3a82 100644 --- a/godot/modules/fbx/editor/editor_scene_importer_ufbx.h +++ b/godot/modules/fbx/editor/editor_scene_importer_ufbx.h @@ -46,7 +46,6 @@ class EditorSceneFormatImporterUFBX : public EditorSceneFormatImporter { FBX_IMPORTER_UFBX, FBX_IMPORTER_FBX2GLTF, }; - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, diff --git a/godot/modules/gltf/editor/editor_scene_importer_blend.cpp b/godot/modules/gltf/editor/editor_scene_importer_blend.cpp index e64a571a..c9540220 100644 --- a/godot/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/godot/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -102,10 +102,6 @@ static bool _get_blender_version(const String &p_path, int &r_major, int &r_mino return true; } -uint32_t EditorSceneFormatImporterBlend::get_import_flags() const { - return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; -} - void EditorSceneFormatImporterBlend::get_extensions(List *r_extensions) const { r_extensions->push_back("blend"); } diff --git a/godot/modules/gltf/editor/editor_scene_importer_blend.h b/godot/modules/gltf/editor/editor_scene_importer_blend.h index 17eb9e57..a4f40143 100644 --- a/godot/modules/gltf/editor/editor_scene_importer_blend.h +++ b/godot/modules/gltf/editor/editor_scene_importer_blend.h @@ -67,7 +67,6 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { BLEND_MODIFIERS_ALL }; - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, diff --git a/godot/modules/gltf/editor/editor_scene_importer_gltf.cpp b/godot/modules/gltf/editor/editor_scene_importer_gltf.cpp index 3e75017f..e63b3ec9 100644 --- a/godot/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/godot/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -35,10 +35,6 @@ #include "../gltf_defines.h" #include "../gltf_document.h" -uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { - return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; -} - void EditorSceneFormatImporterGLTF::get_extensions(List *r_extensions) const { r_extensions->push_back("gltf"); r_extensions->push_back("glb"); diff --git a/godot/modules/gltf/editor/editor_scene_importer_gltf.h b/godot/modules/gltf/editor/editor_scene_importer_gltf.h index e17b6f3f..1dee456c 100644 --- a/godot/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/godot/modules/gltf/editor/editor_scene_importer_gltf.h @@ -42,7 +42,6 @@ class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const override; virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, diff --git a/godot/modules/gltf/extensions/gltf_light.cpp b/godot/modules/gltf/extensions/gltf_light.cpp index 2bdcab2f..41680baf 100644 --- a/godot/modules/gltf/extensions/gltf_light.cpp +++ b/godot/modules/gltf/extensions/gltf_light.cpp @@ -221,21 +221,27 @@ Ref GLTFLight::from_dictionary(const Dictionary p_dictionary) { Dictionary GLTFLight::to_dictionary() const { Dictionary d; - Array color_array; - color_array.resize(3); - color_array[0] = color.r; - color_array[1] = color.g; - color_array[2] = color.b; - d["color"] = color_array; - d["type"] = light_type; + if (color != Color(1.0f, 1.0f, 1.0f)) { + Array color_array; + color_array.resize(3); + color_array[0] = color.r; + color_array[1] = color.g; + color_array[2] = color.b; + d["color"] = color_array; + } + if (intensity != 1.0f) { + d["intensity"] = intensity; + } + if (light_type != "directional" && range != INFINITY) { + d["range"] = range; + } if (light_type == "spot") { Dictionary spot_dict; spot_dict["innerConeAngle"] = inner_cone_angle; spot_dict["outerConeAngle"] = outer_cone_angle; d["spot"] = spot_dict; } - d["intensity"] = intensity; - d["range"] = range; + d["type"] = light_type; return d; } diff --git a/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp b/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp index 77c4e608..f4157ca7 100644 --- a/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1254,6 +1254,9 @@ void GridMapEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { indicator_mat->set_albedo(EDITOR_GET("editors/3d_gizmos/gizmo_colors/gridmap_grid")); + + // Take Preview Size changes into account. + update_palette(); } break; } } diff --git a/godot/modules/jsonrpc/jsonrpc.cpp b/godot/modules/jsonrpc/jsonrpc.cpp index a3c0ce88..0f03e49f 100644 --- a/godot/modules/jsonrpc/jsonrpc.cpp +++ b/godot/modules/jsonrpc/jsonrpc.cpp @@ -122,6 +122,11 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem Variant id; if (dict.has("id")) { id = dict["id"]; + + // Account for implementations that discern between int and float on the json serialization level, by using an int if there is a .0 fraction. See #100914 + if (id.get_type() == Variant::FLOAT && id.operator float() == (float)(id.operator int())) { + id = id.operator int(); + } } if (object == nullptr || !object->has_method(method)) { diff --git a/godot/modules/ktx/texture_loader_ktx.cpp b/godot/modules/ktx/texture_loader_ktx.cpp index 7c73f9cb..c088abcd 100644 --- a/godot/modules/ktx/texture_loader_ktx.cpp +++ b/godot/modules/ktx/texture_loader_ktx.cpp @@ -208,32 +208,33 @@ static Ref load_from_file_access(Ref f, Error *r_error) { case GL_COMPRESSED_RGBA_BPTC_UNORM: format = Image::FORMAT_BPTC_RGBA; break; -#if 0 // TODO: ETC compression is bogus. case GL_ETC1_RGB8_OES: format = Image::FORMAT_ETC; break; case GL_COMPRESSED_R11_EAC: format = Image::FORMAT_ETC2_R11; break; - case GL_COMPRESSED_SIGNED_R11_EAC: + // Decompression is not supported for this format. + /*case GL_COMPRESSED_SIGNED_R11_EAC: format = Image::FORMAT_ETC2_R11S; - break; + break;*/ case GL_COMPRESSED_RG11_EAC: format = Image::FORMAT_ETC2_RG11; break; - case GL_COMPRESSED_SIGNED_RG11_EAC: + // Decompression is not supported for this format. + /*case GL_COMPRESSED_SIGNED_RG11_EAC: format = Image::FORMAT_ETC2_RG11S; - break; + break;*/ case GL_COMPRESSED_RGB8_ETC2: format = Image::FORMAT_ETC2_RGB8; break; case GL_COMPRESSED_RGBA8_ETC2_EAC: format = Image::FORMAT_ETC2_RGBA8; break; - case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + // Decompression is not supported for this format. + /*case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: format = Image::FORMAT_ETC2_RGB8A1; - break; -#endif + break;*/ case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: format = Image::FORMAT_ASTC_4x4; break; @@ -406,29 +407,30 @@ static Ref load_from_file_access(Ref f, Error *r_error) { case VK_FORMAT_BC7_UNORM_BLOCK: format = Image::FORMAT_BPTC_RGBA; break; -#if 0 // TODO: ETC compression is bogus. case VK_FORMAT_EAC_R11_UNORM_BLOCK: format = Image::FORMAT_ETC2_R11; break; - case VK_FORMAT_EAC_R11_SNORM_BLOCK: + // Decompression is not supported for this format. + /*case VK_FORMAT_EAC_R11_SNORM_BLOCK: format = Image::FORMAT_ETC2_R11S; - break; + break;*/ case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: format = Image::FORMAT_ETC2_RG11; break; - case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + // Decompression is not supported for this format. + /*case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: format = Image::FORMAT_ETC2_RG11S; - break; + break;*/ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: format = Image::FORMAT_ETC2_RGB8; break; case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: format = Image::FORMAT_ETC2_RGBA8; break; - case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + // Decompression is not supported for this format. + /*case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: format = Image::FORMAT_ETC2_RGB8A1; - break; -#endif + break;*/ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: format = Image::FORMAT_ASTC_4x4; break; diff --git a/godot/modules/navigation/2d/godot_navigation_server_2d.cpp b/godot/modules/navigation/2d/godot_navigation_server_2d.cpp index f6876b31..89e4de3b 100644 --- a/godot/modules/navigation/2d/godot_navigation_server_2d.cpp +++ b/godot/modules/navigation/2d/godot_navigation_server_2d.cpp @@ -158,6 +158,13 @@ static Ref poly_to_mesh(Ref d) { } } +static Rect2 aabb_to_rect2(AABB aabb) { + Rect2 rect2; + rect2.position = Vector2(aabb.position.x, aabb.position.z); + rect2.size = Vector2(aabb.size.x, aabb.size.z); + return rect2; +} + void GodotNavigationServer2D::init() { #ifdef CLIPPER2_ENABLED navmesh_generator_2d = memnew(NavMeshGenerator2D); @@ -332,6 +339,11 @@ Vector2 GodotNavigationServer2D::region_get_random_point(RID p_region, uint32_t return v3_to_v2(result); } +Rect2 GodotNavigationServer2D::region_get_bounds(RID p_region) const { + AABB bounds = NavigationServer3D::get_singleton()->region_get_bounds(p_region); + return aabb_to_rect2(bounds); +} + RID FORWARD_0(link_create); void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid); diff --git a/godot/modules/navigation/2d/godot_navigation_server_2d.h b/godot/modules/navigation/2d/godot_navigation_server_2d.h index f4a5023a..361b812b 100644 --- a/godot/modules/navigation/2d/godot_navigation_server_2d.h +++ b/godot/modules/navigation/2d/godot_navigation_server_2d.h @@ -105,6 +105,7 @@ class GodotNavigationServer2D : public NavigationServer2D { virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; + virtual Rect2 region_get_bounds(RID p_region) const override; virtual RID link_create() override; diff --git a/godot/modules/navigation/3d/godot_navigation_server_3d.cpp b/godot/modules/navigation/3d/godot_navigation_server_3d.cpp index e2b8d050..ef15bcd6 100644 --- a/godot/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/godot/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -595,6 +595,13 @@ Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t return region->get_random_point(p_navigation_layers, p_uniformly); } +AABB GodotNavigationServer3D::region_get_bounds(RID p_region) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, AABB()); + + return region->get_bounds(); +} + RID GodotNavigationServer3D::link_create() { MutexLock lock(operations_mutex); diff --git a/godot/modules/navigation/3d/godot_navigation_server_3d.h b/godot/modules/navigation/3d/godot_navigation_server_3d.h index 268dbee8..c595fbc0 100644 --- a/godot/modules/navigation/3d/godot_navigation_server_3d.h +++ b/godot/modules/navigation/3d/godot_navigation_server_3d.h @@ -187,6 +187,7 @@ class GodotNavigationServer3D : public NavigationServer3D { virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override; virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; + virtual AABB region_get_bounds(RID p_region) const override; virtual RID link_create() override; COMMAND_2(link_set_map, RID, p_link, RID, p_map); diff --git a/godot/modules/regex/regex.cpp b/godot/modules/regex/regex.cpp index 02f97b3d..9424d57e 100644 --- a/godot/modules/regex/regex.cpp +++ b/godot/modules/regex/regex.cpp @@ -196,7 +196,7 @@ Error RegEx::compile(const String &p_pattern, bool p_show_error) { if (p_show_error) { PCRE2_UCHAR32 buf[256]; pcre2_get_error_message_32(err, buf, 256); - String message = String::num(offset) + ": " + String((const char32_t *)buf); + String message = String::num_int64(offset) + ": " + String((const char32_t *)buf); ERR_PRINT(message.utf8()); } return FAILED; diff --git a/godot/modules/websocket/emws_peer.cpp b/godot/modules/websocket/emws_peer.cpp index c5768c9f..d8cb7642 100644 --- a/godot/modules/websocket/emws_peer.cpp +++ b/godot/modules/websocket/emws_peer.cpp @@ -93,7 +93,7 @@ Error EMWSPeer::connect_to_url(const String &p_url, Ref p_tls_option requested_url = scheme + host; if (port && ((scheme == "ws://" && port != 80) || (scheme == "wss://" && port != 443))) { - requested_url += ":" + String::num(port); + requested_url += ":" + String::num_int64(port); } if (!path.is_empty()) { diff --git a/godot/platform/android/SCsub b/godot/platform/android/SCsub index 66c95525..d0928a93 100644 --- a/godot/platform/android/SCsub +++ b/godot/platform/android/SCsub @@ -29,6 +29,7 @@ android_files = [ "plugin/godot_plugin_jni.cpp", "rendering_context_driver_vulkan_android.cpp", "variant/callable_jni.cpp", + "dialog_utils_jni.cpp", ] env_android = env.Clone() diff --git a/godot/platform/android/dialog_utils_jni.cpp b/godot/platform/android/dialog_utils_jni.cpp new file mode 100644 index 00000000..c1f8cea0 --- /dev/null +++ b/godot/platform/android/dialog_utils_jni.cpp @@ -0,0 +1,52 @@ +/**************************************************************************/ +/* dialog_utils_jni.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 "dialog_utils_jni.h" + +#include "display_server_android.h" +#include "jni_utils.h" + +extern "C" { + +JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index) { + DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); + if (ds) { + ds->emit_dialog_callback(p_button_index); + } +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) { + DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); + if (ds) { + String text = jstring_to_string(p_text, env); + ds->emit_input_dialog_callback(text); + } +} +} diff --git a/godot/platform/android/dialog_utils_jni.h b/godot/platform/android/dialog_utils_jni.h new file mode 100644 index 00000000..7b2da46d --- /dev/null +++ b/godot/platform/android/dialog_utils_jni.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* dialog_utils_jni.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 DIALOG_UTILS_JNI_H +#define DIALOG_UTILS_JNI_H + +#include + +extern "C" { +JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index); +JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text); +} + +#endif // DIALOG_UTILS_JNI_H diff --git a/godot/platform/android/display_server_android.cpp b/godot/platform/android/display_server_android.cpp index 77b87913..06973f28 100644 --- a/godot/platform/android/display_server_android.cpp +++ b/godot/platform/android/display_server_android.cpp @@ -70,7 +70,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const { //case FEATURE_IME: case FEATURE_MOUSE: //case FEATURE_MOUSE_WARP: - //case FEATURE_NATIVE_DIALOG: + case FEATURE_NATIVE_DIALOG: case FEATURE_NATIVE_DIALOG_INPUT: case FEATURE_NATIVE_DIALOG_FILE: //case FEATURE_NATIVE_DIALOG_FILE_EXTRA: @@ -178,6 +178,19 @@ bool DisplayServerAndroid::clipboard_has() const { } } +Error DisplayServerAndroid::dialog_show(String p_title, String p_description, Vector p_buttons, const Callable &p_callback) { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_NULL_V(godot_java, FAILED); + dialog_callback = p_callback; + return godot_java->show_dialog(p_title, p_description, p_buttons); +} + +void DisplayServerAndroid::emit_dialog_callback(int p_button_index) { + if (dialog_callback.is_valid()) { + dialog_callback.call_deferred(p_button_index); + } +} + Error DisplayServerAndroid::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) { GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); ERR_FAIL_NULL_V(godot_java, FAILED); diff --git a/godot/platform/android/display_server_android.h b/godot/platform/android/display_server_android.h index 582f722f..0d2caa07 100644 --- a/godot/platform/android/display_server_android.h +++ b/godot/platform/android/display_server_android.h @@ -87,7 +87,9 @@ class DisplayServerAndroid : public DisplayServer { Callable system_theme_changed; + Callable dialog_callback; Callable input_dialog_callback; + Callable file_picker_callback; void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const; @@ -119,6 +121,9 @@ class DisplayServerAndroid : public DisplayServer { virtual String clipboard_get() const override; virtual bool clipboard_has() const override; + virtual Error dialog_show(String p_title, String p_description, Vector p_buttons, const Callable &p_callback) override; + void emit_dialog_callback(int p_button_index); + virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override; void emit_input_dialog_callback(String p_text); diff --git a/godot/platform/android/java/editor/build.gradle b/godot/platform/android/java/editor/build.gradle index 32ff4d76..e5b5c4f2 100644 --- a/godot/platform/android/java/editor/build.gradle +++ b/godot/platform/android/java/editor/build.gradle @@ -86,7 +86,7 @@ android { missingDimensionStrategy 'products', 'editor' manifestPlaceholders += [ - editorAppName: "Godot Editor 4", + editorAppName: "Godot Engine 4", editorBuildSuffix: "" ] } diff --git a/godot/platform/android/java/lib/res/mipmap-hdpi/icon_monochrome.png b/godot/platform/android/java/lib/res/mipmap-hdpi/icon_monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..294caf2dd02c8f439baf5442151729e842e4cdc8 GIT binary patch literal 2716 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 literal 0 HcmV?d00001 diff --git a/godot/platform/android/java/lib/res/mipmap-mdpi/icon_monochrome.png b/godot/platform/android/java/lib/res/mipmap-mdpi/icon_monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..d6dd804f10fff626a983b8fb3bff9d14c9daedfa GIT binary patch literal 1697 zcmcJQjX%=~0LJ&iW?o`>DKCjjN3rGR(8}EAnBfviUQbp^BBvpBlC8#7j2~IVw9dRF zvs)!)Gkzz1tT`)Er*2+KZLMVUlI2k6{)j%G@AG`0|KPb078+<`Xk!S0Kum%t{t>HA z`#%PV)h<08HLxmZW<;PLL?E|QLm-H_Ab--{lbjEnnDhH0?ZA8wucKHV?^0ZgKB>B{ zZXql}GGB4c(&Nfu>thys!>FX1eWs)c-+-l~%7{efVqz8A0u$VRfN5J1E3cB@Q*!J- z>kg)mE8f(+Iip%ue>vALIh|aq@m48r4-9WtxzC5a7sJRpcdG1b@EFTSS0OoZB*hH) z5tRC+?sc~*V8QGWUHS`f4J^c!qWfNm3!XF4nq`nl*>1%)0r`);*F+=Y;q%*!cIw9) zFPE2}_(sSQbYF9(-vQy^#=Lz`V1vUwa+0Wx76VzKJL871n-`Nmm@4SYxa-7ALOA~A zF0lS>hp!0I2+Pd9lH-2l-P%Cl%Y$~u*2ZDOQn)k25wCv7xG%Z!GyAa<-)eDoNffm` zh~{nUR4wXVGm!{gY_|ZCsZ6J|Bjk}Ii6%ps1y3F$Qkvz+;)^2LVa$Pm!lxnhC;dGF z{WCCut8lsTdNKYG*A#ZO_??#+oOSKPtCp%?a#C|@r)Y9dyy_??vvj9wNBqaP8FIo} zHsKNeuCvuMC8tK|lfSgIvbvnR#J!c2%le;_4zUSq zPTs7x+|t)1tUZ*|lI3jPnq-4BnJLsz31A6FyOB`? z?2(h!IVY){c47gnIrc+%=h7E$W6{aXf2xlDDPldt7k_h zIJU8uo#h=2dB;<0bz$HpHWEb9 zoiI+p4-C$=&hIk9GJyg~K^i3i)p?HZJM1v}sxO?AjZ(Kev^XCGIuh_2cy_q`9zZkU z^VP0j)Z zo@AYb6(!$BCiHr8Hb1iYjzaSO@xOd<>sBN83O>VTR2Ne^I(L!gtPFbxlv3uxi zzTT*_ygZ#E%j9?ig5zlws*FVZtriKwTA>$Y;JSeYa`DH{5KE#=iI)qp((~eV&qZov zXXru;&({KR&tRrOpA4UjUA*+d_QhURE87*7p6zvUi`sSK+Agp z?1{+bo9J!H^hH?%o)H|iInGI$Q?-ffQ-lVSV)XRv>{@GRQx@p>=T zir4t-TI=un0zAbSinJa)JXA1%7}Fc;e>hoZ*mF6l*IF;W7Ttw!TE1Z>bGeEB#1h2W zA1^6W;9`M>hJR?XlTh`qAsCObU|TxFIwh{`DVv{@5@`^yUW$L9%8UA@Ky3DM43*Mw z5U;J$1Um_ z$=>tZltn309$3=s>7bGPnI))hb99~hAl2qO(-6l4=j}SL(b^1`#@7xB$F4)n8;!pL b#raV0Ei;LpUJ1XgehwrkAk<&rmw5I+hU_7u literal 0 HcmV?d00001 diff --git a/godot/platform/android/java/lib/res/mipmap-xhdpi/icon_monochrome.png b/godot/platform/android/java/lib/res/mipmap-xhdpi/icon_monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..6e1665aa5737f145dec9a3e85ee5d9ac55732ad2 GIT binary patch literal 3615 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 McFwkB>%i;(1H!ih-2eap literal 0 HcmV?d00001 diff --git a/godot/platform/android/java/lib/res/mipmap-xxxhdpi/icon_monochrome.png b/godot/platform/android/java/lib/res/mipmap-xxxhdpi/icon_monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..86627deb45400930206bf8fb11a9031b0923200d GIT binary patch literal 7605 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 b#raV0Ei;LpUJ1XgehwrkAk<&rmw5I+hU_7u literal 5617 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 48dp - 10dp - 5dp + 48dp + 10dp + 16dp + 8dp diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/godot/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index fcbb8830..3fc3caed 100644 --- a/godot/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -44,7 +44,6 @@ import android.os.* import android.util.Log import android.util.TypedValue import android.view.* -import android.widget.EditText import android.widget.FrameLayout import androidx.annotation.Keep import androidx.annotation.StringRes @@ -65,6 +64,7 @@ import org.godotengine.godot.plugin.GodotPlugin import org.godotengine.godot.plugin.GodotPluginRegistry import org.godotengine.godot.tts.GodotTTS import org.godotengine.godot.utils.CommandLineFileParser +import org.godotengine.godot.utils.DialogUtils import org.godotengine.godot.utils.GodotNetUtils import org.godotengine.godot.utils.PermissionsUtil import org.godotengine.godot.utils.PermissionsUtil.requestPermission @@ -903,27 +903,27 @@ class Godot(private val context: Context) { } /** - * Popup a dialog to input text. + * This method shows a dialog with multiple buttons. + * + * @param title The title of the dialog. + * @param message The message displayed in the dialog. + * @param buttons An array of button labels to display. + */ + @Keep + private fun showDialog(title: String, message: String, buttons: Array) { + getActivity()?.let { DialogUtils.showDialog(it, title, message, buttons) } + } + + /** + * This method shows a dialog with a text input field, allowing the user to input text. + * + * @param title The title of the input dialog. + * @param message The message displayed in the input dialog. + * @param existingText The existing text that will be pre-filled in the input field. */ @Keep private fun showInputDialog(title: String, message: String, existingText: String) { - val activity: Activity = getActivity() ?: return - val inputField = EditText(activity) - val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_horizontal) - val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_vertical) - inputField.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical) - inputField.setText(existingText) - runOnUiThread { - val builder = AlertDialog.Builder(activity) - builder.setMessage(message).setTitle(title).setView(inputField) - builder.setPositiveButton(R.string.dialog_ok) { - dialog: DialogInterface, id: Int -> - GodotLib.inputDialogCallback(inputField.text.toString()) - dialog.dismiss() - } - val dialog = builder.create() - dialog.show() - } + getActivity()?.let { DialogUtils.showInputDialog(it, title, message, existingText) } } @Keep diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 9af91924..67aa3283 100644 --- a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -235,11 +235,6 @@ public static void calldeferred(long p_id, String p_method, Object[] p_params) { */ public static native void onNightModeChanged(); - /** - * Invoked on the input dialog submitted. - */ - public static native void inputDialogCallback(String p_text); - /** * Invoked on the file picker closed. */ diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt b/godot/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt new file mode 100644 index 00000000..45ec72b7 --- /dev/null +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* DialogUtils.kt */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +package org.godotengine.godot.utils + +import android.app.Activity +import android.app.AlertDialog +import android.content.DialogInterface +import android.widget.Button +import android.widget.EditText +import android.widget.LinearLayout + +import org.godotengine.godot.R + +/** + * Utility class for managing dialogs. + */ +internal class DialogUtils { + companion object { + private val TAG = DialogUtils::class.java.simpleName + + /** + * Invoked on dialog button press. + */ + @JvmStatic + private external fun dialogCallback(buttonIndex: Int) + + /** + * Invoked on the input dialog submitted. + */ + @JvmStatic + private external fun inputDialogCallback(text: String) + + /** + * Displays a dialog with dynamically arranged buttons based on their text length. + * + * The buttons are laid out in rows, with a maximum of 2 buttons per row. If a button's text + * is too long to fit within half the screen width, it occupies the entire row. + * + * @param activity The activity where the dialog will be displayed. + * @param title The title of the dialog. + * @param message The message displayed in the dialog. + * @param buttons An array of button labels to display. + */ + fun showDialog(activity: Activity, title: String, message: String, buttons: Array) { + var dismissDialog: () -> Unit = {} // Helper to dismiss the Dialog when a button is clicked. + activity.runOnUiThread { + val builder = AlertDialog.Builder(activity) + builder.setTitle(title) + builder.setMessage(message) + + val buttonHeight = activity.resources.getDimensionPixelSize(R.dimen.button_height) + val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_horizontal) + val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_vertical) + val buttonPadding = activity.resources.getDimensionPixelSize(R.dimen.button_padding) + + // Create a vertical parent layout to hold all rows of buttons. + val parentLayout = LinearLayout(activity) + parentLayout.orientation = LinearLayout.VERTICAL + parentLayout.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical) + + // Horizontal row layout for arranging buttons. + var rowLayout = LinearLayout(activity) + rowLayout.orientation = LinearLayout.HORIZONTAL + rowLayout.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + + // Calculate the maximum width for a button to allow two buttons per row. + val screenWidth = activity.resources.displayMetrics.widthPixels + val availableWidth = screenWidth - (2 * paddingHorizontal) + val maxButtonWidth = availableWidth / 2 + + buttons.forEachIndexed { index, buttonLabel -> + val button = Button(activity) + button.text = buttonLabel + button.isSingleLine = true + button.setPadding(buttonPadding, buttonPadding, buttonPadding, buttonPadding) + + // Measure the button to determine its width. + button.measure(0, 0) + val buttonWidth = button.measuredWidth + + val params = LinearLayout.LayoutParams( + if (buttonWidth > maxButtonWidth) LinearLayout.LayoutParams.MATCH_PARENT else 0, + buttonHeight + ) + params.weight = if (buttonWidth > maxButtonWidth) 0f else 1f + button.layoutParams = params + + // Handle full-width buttons by finalizing the current row, if needed. + if (buttonWidth > maxButtonWidth) { + if (rowLayout.childCount > 0) { + parentLayout.addView(rowLayout) + rowLayout = LinearLayout(activity) + rowLayout.orientation = LinearLayout.HORIZONTAL + } + // Add the full-width button directly to the parent layout. + parentLayout.addView(button) + } else { + rowLayout.addView(button) + + // Finalize the row if it reaches 2 buttons. + if (rowLayout.childCount == 2) { + parentLayout.addView(rowLayout) + rowLayout = LinearLayout(activity) + rowLayout.orientation = LinearLayout.HORIZONTAL + } + + // Handle the last button with incomplete row. + if (index == buttons.size - 1 && rowLayout.childCount > 0) { + parentLayout.addView(rowLayout) + } + } + + button.setOnClickListener { + dialogCallback(index) + dismissDialog() + } + } + + // Attach the parent layout to the dialog. + builder.setView(parentLayout) + val dialog = builder.create() + dismissDialog = {dialog.dismiss()} + dialog.show() + } + } + + /** + * This method shows a dialog with a text input field, allowing the user to input text. + * + * @param activity The activity where the input dialog will be displayed. + * @param title The title of the input dialog. + * @param message The message displayed in the input dialog. + * @param existingText The existing text that will be pre-filled in the input field. + */ + fun showInputDialog(activity: Activity, title: String, message: String, existingText: String) { + val inputField = EditText(activity) + val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_horizontal) + val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_vertical) + inputField.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical) + inputField.setText(existingText) + activity.runOnUiThread { + val builder = AlertDialog.Builder(activity) + builder.setMessage(message).setTitle(title).setView(inputField) + builder.setPositiveButton(R.string.dialog_ok) { + dialog: DialogInterface, id: Int -> + inputDialogCallback(inputField.text.toString()) + dialog.dismiss() + } + val dialog = builder.create() + dialog.show() + } + } + } +} diff --git a/godot/platform/android/java_godot_lib_jni.cpp b/godot/platform/android/java_godot_lib_jni.cpp index 023832a2..b291473e 100644 --- a/godot/platform/android/java_godot_lib_jni.cpp +++ b/godot/platform/android/java_godot_lib_jni.cpp @@ -494,14 +494,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) { - DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); - if (ds) { - String text = jstring_to_string(p_text, env); - ds->emit_input_dialog_callback(text); - } -} - JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) { DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); if (ds) { diff --git a/godot/platform/android/java_godot_lib_jni.h b/godot/platform/android/java_godot_lib_jni.h index 48d91795..6feaec47 100644 --- a/godot/platform/android/java_godot_lib_jni.h +++ b/godot/platform/android/java_godot_lib_jni.h @@ -65,7 +65,6 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getEditorSetting(J JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz); diff --git a/godot/platform/android/java_godot_wrapper.cpp b/godot/platform/android/java_godot_wrapper.cpp index 42249b9f..8c75e666 100644 --- a/godot/platform/android/java_godot_wrapper.cpp +++ b/godot/platform/android/java_godot_wrapper.cpp @@ -69,6 +69,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V"); _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z"); + _show_dialog = p_env->GetMethodID(godot_class, "showDialog", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"); _show_input_dialog = p_env->GetMethodID(godot_class, "showInputDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); _show_file_picker = p_env->GetMethodID(godot_class, "showFilePicker", "(Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)V"); _request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z"); @@ -303,6 +304,28 @@ bool GodotJavaWrapper::has_clipboard() { } } +Error GodotJavaWrapper::show_dialog(const String &p_title, const String &p_description, const Vector &p_buttons) { + if (_show_input_dialog) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); + jstring j_title = env->NewStringUTF(p_title.utf8().get_data()); + jstring j_description = env->NewStringUTF(p_description.utf8().get_data()); + jobjectArray j_buttons = env->NewObjectArray(p_buttons.size(), env->FindClass("java/lang/String"), nullptr); + for (int i = 0; i < p_buttons.size(); ++i) { + jstring j_button = env->NewStringUTF(p_buttons[i].utf8().get_data()); + env->SetObjectArrayElement(j_buttons, i, j_button); + env->DeleteLocalRef(j_button); + } + env->CallVoidMethod(godot_instance, _show_dialog, j_title, j_description, j_buttons); + env->DeleteLocalRef(j_title); + env->DeleteLocalRef(j_description); + env->DeleteLocalRef(j_buttons); + return OK; + } else { + return ERR_UNCONFIGURED; + } +} + Error GodotJavaWrapper::show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text) { if (_show_input_dialog) { JNIEnv *env = get_jni_env(); diff --git a/godot/platform/android/java_godot_wrapper.h b/godot/platform/android/java_godot_wrapper.h index 50612abf..1c74072e 100644 --- a/godot/platform/android/java_godot_wrapper.h +++ b/godot/platform/android/java_godot_wrapper.h @@ -60,6 +60,7 @@ class GodotJavaWrapper { jmethodID _get_clipboard = nullptr; jmethodID _set_clipboard = nullptr; jmethodID _has_clipboard = nullptr; + jmethodID _show_dialog = nullptr; jmethodID _show_input_dialog = nullptr; jmethodID _show_file_picker = nullptr; jmethodID _request_permission = nullptr; @@ -109,6 +110,7 @@ class GodotJavaWrapper { void set_clipboard(const String &p_text); bool has_has_clipboard(); bool has_clipboard(); + Error show_dialog(const String &p_title, const String &p_description, const Vector &p_buttons); Error show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text); Error show_file_picker(const String &p_current_directory, const String &p_filename, int p_mode, const Vector &p_filters); bool request_permission(const String &p_name); diff --git a/godot/platform/ios/export/export_plugin.cpp b/godot/platform/ios/export/export_plugin.cpp index 95ef4def..3684089d 100644 --- a/godot/platform/ios/export/export_plugin.cpp +++ b/godot/platform/ios/export/export_plugin.cpp @@ -3163,7 +3163,7 @@ Error EditorExportPlatformIOS::run(const Ref &p_preset, int if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) { cmd_args_list.push_back("--remote-debug"); - cmd_args_list.push_back(get_debug_protocol() + host + ":" + String::num(remote_port)); + cmd_args_list.push_back(get_debug_protocol() + host + ":" + String::num_int64(remote_port)); List breakpoints; ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); diff --git a/godot/platform/ios/ios.mm b/godot/platform/ios/ios.mm index 26067b94..e257b506 100644 --- a/godot/platform/ios/ios.mm +++ b/godot/platform/ios/ios.mm @@ -191,7 +191,7 @@ String iOS::get_rate_url(int p_app_id) const { String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID"; - String ret = app_url_path.replace("APP_ID", String::num(p_app_id)); + String ret = app_url_path.replace("APP_ID", String::num_int64(p_app_id)); print_verbose(vformat("Returning rate url %s", ret)); return ret; diff --git a/godot/platform/linuxbsd/wayland/display_server_wayland.cpp b/godot/platform/linuxbsd/wayland/display_server_wayland.cpp index d5dc8c2a..0ad29405 100644 --- a/godot/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/godot/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -209,6 +209,7 @@ bool DisplayServerWayland::has_feature(Feature p_feature) const { case FEATURE_SWAP_BUFFERS: case FEATURE_KEEP_SCREEN_ON: case FEATURE_IME: + case FEATURE_WINDOW_DRAG: case FEATURE_CLIPBOARD_PRIMARY: { return true; } break; diff --git a/godot/platform/linuxbsd/x11/display_server_x11.cpp b/godot/platform/linuxbsd/x11/display_server_x11.cpp index 77f08095..b17f72dc 100644 --- a/godot/platform/linuxbsd/x11/display_server_x11.cpp +++ b/godot/platform/linuxbsd/x11/display_server_x11.cpp @@ -152,6 +152,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const { case FEATURE_CLIPBOARD_PRIMARY: case FEATURE_TEXT_TO_SPEECH: case FEATURE_WINDOW_EMBEDDING: + case FEATURE_WINDOW_DRAG: return true; case FEATURE_SCREEN_CAPTURE: return !xwayland; diff --git a/godot/platform/windows/display_server_windows.cpp b/godot/platform/windows/display_server_windows.cpp index 4d9e7b39..f35624d8 100644 --- a/godot/platform/windows/display_server_windows.cpp +++ b/godot/platform/windows/display_server_windows.cpp @@ -138,6 +138,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_SCREEN_CAPTURE: case FEATURE_STATUS_INDICATOR: case FEATURE_WINDOW_EMBEDDING: + case FEATURE_WINDOW_DRAG: case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE: return true; case FEATURE_EMOJI_AND_SYMBOL_PICKER: diff --git a/godot/scene/2d/navigation_region_2d.cpp b/godot/scene/2d/navigation_region_2d.cpp index e33c7c08..365d5ba4 100644 --- a/godot/scene/2d/navigation_region_2d.cpp +++ b/godot/scene/2d/navigation_region_2d.cpp @@ -200,6 +200,9 @@ void NavigationRegion2D::set_navigation_polygon(const Ref &p_ #ifdef DEBUG_ENABLED debug_mesh_dirty = true; #endif // DEBUG_ENABLED + + _update_bounds(); + NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon); if (navigation_polygon.is_valid()) { @@ -341,6 +344,8 @@ void NavigationRegion2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed); + ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationRegion2D::get_bounds); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections"); @@ -648,3 +653,26 @@ void NavigationRegion2D::_set_debug_visible(bool p_visible) { } } #endif // DEBUG_ENABLED + +void NavigationRegion2D::_update_bounds() { + if (navigation_polygon.is_null()) { + bounds = Rect2(); + return; + } + + const Vector &vertices = navigation_polygon->get_vertices(); + if (vertices.is_empty()) { + bounds = Rect2(); + return; + } + + const Transform2D gt = is_inside_tree() ? get_global_transform() : get_transform(); + + Rect2 new_bounds; + new_bounds.position = gt.xform(vertices[0]); + + for (const Vector2 &vertex : vertices) { + new_bounds.expand_to(gt.xform(vertex)); + } + bounds = new_bounds; +} diff --git a/godot/scene/2d/navigation_region_2d.h b/godot/scene/2d/navigation_region_2d.h index 761eaef5..9ea87b1d 100644 --- a/godot/scene/2d/navigation_region_2d.h +++ b/godot/scene/2d/navigation_region_2d.h @@ -50,6 +50,8 @@ class NavigationRegion2D : public Node2D { void _navigation_polygon_changed(); + Rect2 bounds; + #ifdef DEBUG_ENABLED private: RID debug_mesh_rid; @@ -113,10 +115,13 @@ class NavigationRegion2D : public Node2D { void _bake_finished(Ref p_navigation_polygon); bool is_baking() const; + Rect2 get_bounds() const { return bounds; } + NavigationRegion2D(); ~NavigationRegion2D(); private: + void _update_bounds(); void _region_enter_navigation_map(); void _region_exit_navigation_map(); void _region_update_transform(); diff --git a/godot/scene/3d/gpu_particles_3d.cpp b/godot/scene/3d/gpu_particles_3d.cpp index 17e1b7f9..2b748e88 100644 --- a/godot/scene/3d/gpu_particles_3d.cpp +++ b/godot/scene/3d/gpu_particles_3d.cpp @@ -557,7 +557,6 @@ void GPUParticles3D::_notification(int p_what) { Ref material = get_process_material(); ERR_FAIL_COND(material.is_null()); - material->disconnect("emission_shape_changed", callable_mp((Node3D *)this, &GPUParticles3D::update_gizmos)); } break; case NOTIFICATION_SUSPENDED: diff --git a/godot/scene/3d/navigation_region_3d.cpp b/godot/scene/3d/navigation_region_3d.cpp index ce8cef56..34be901e 100644 --- a/godot/scene/3d/navigation_region_3d.cpp +++ b/godot/scene/3d/navigation_region_3d.cpp @@ -193,6 +193,8 @@ void NavigationRegion3D::set_navigation_mesh(const Ref &p_naviga navigation_mesh->connect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } + _update_bounds(); + NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh); #ifdef DEBUG_ENABLED @@ -314,6 +316,8 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_baking"), &NavigationRegion3D::is_baking); + ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationRegion3D::get_bounds); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_mesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections"); @@ -740,3 +744,26 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { } } #endif // DEBUG_ENABLED + +void NavigationRegion3D::_update_bounds() { + if (navigation_mesh.is_null()) { + bounds = AABB(); + return; + } + + const Vector &vertices = navigation_mesh->get_vertices(); + if (vertices.is_empty()) { + bounds = AABB(); + return; + } + + const Transform3D gt = is_inside_tree() ? get_global_transform() : get_transform(); + + AABB new_bounds; + new_bounds.position = gt.xform(vertices[0]); + + for (const Vector3 &vertex : vertices) { + new_bounds.expand_to(gt.xform(vertex)); + } + bounds = new_bounds; +} diff --git a/godot/scene/3d/navigation_region_3d.h b/godot/scene/3d/navigation_region_3d.h index 82468627..97abe397 100644 --- a/godot/scene/3d/navigation_region_3d.h +++ b/godot/scene/3d/navigation_region_3d.h @@ -51,6 +51,8 @@ class NavigationRegion3D : public Node3D { void _navigation_mesh_changed(); + AABB bounds; + #ifdef DEBUG_ENABLED RID debug_instance; RID debug_edge_connections_instance; @@ -110,10 +112,13 @@ class NavigationRegion3D : public Node3D { PackedStringArray get_configuration_warnings() const override; + AABB get_bounds() const { return bounds; } + NavigationRegion3D(); ~NavigationRegion3D(); private: + void _update_bounds(); void _region_enter_navigation_map(); void _region_exit_navigation_map(); void _region_update_transform(); diff --git a/godot/scene/3d/spring_bone_simulator_3d.cpp b/godot/scene/3d/spring_bone_simulator_3d.cpp index db275528..41061c72 100644 --- a/godot/scene/3d/spring_bone_simulator_3d.cpp +++ b/godot/scene/3d/spring_bone_simulator_3d.cpp @@ -56,8 +56,6 @@ bool SpringBoneSimulator3D::_set(const StringName &p_path, const Variant &p_valu set_end_bone_direction(which, static_cast((int)p_value)); } else if (opt == "length") { set_end_bone_length(which, p_value); - } else if (opt == "tip_radius") { - set_end_bone_tip_radius(which, p_value); } else { return false; } @@ -178,8 +176,6 @@ bool SpringBoneSimulator3D::_get(const StringName &p_path, Variant &r_ret) const r_ret = (int)get_end_bone_direction(which); } else if (opt == "length") { r_ret = get_end_bone_length(which); - } else if (opt == "tip_radius") { - r_ret = get_end_bone_tip_radius(which); } else { return false; } @@ -294,7 +290,6 @@ void SpringBoneSimulator3D::_get_property_list(List *p_list) const p_list->push_back(PropertyInfo(Variant::BOOL, path + "extend_end_bone")); p_list->push_back(PropertyInfo(Variant::INT, path + "end_bone/direction", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z,FromParent")); p_list->push_back(PropertyInfo(Variant::FLOAT, path + "end_bone/length", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m")); - p_list->push_back(PropertyInfo(Variant::FLOAT, path + "end_bone/tip_radius", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m")); p_list->push_back(PropertyInfo(Variant::INT, path + "center_from", PROPERTY_HINT_ENUM, "WorldOrigin,Node,Bone")); p_list->push_back(PropertyInfo(Variant::NODE_PATH, path + "center_node")); p_list->push_back(PropertyInfo(Variant::STRING, path + "center_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint)); @@ -516,17 +511,6 @@ float SpringBoneSimulator3D::get_end_bone_length(int p_index) const { return settings[p_index]->end_bone_length; } -void SpringBoneSimulator3D::set_end_bone_tip_radius(int p_index, float p_radius) { - ERR_FAIL_INDEX(p_index, settings.size()); - settings[p_index]->end_bone_tip_radius = p_radius; - _make_joints_dirty(p_index); -} - -float SpringBoneSimulator3D::get_end_bone_tip_radius(int p_index) const { - ERR_FAIL_INDEX_V(p_index, settings.size(), 0); - return settings[p_index]->end_bone_tip_radius; -} - Vector3 SpringBoneSimulator3D::get_end_bone_axis(int p_end_bone, BoneDirection p_direction) const { Vector3 axis; if (p_direction == BONE_DIRECTION_FROM_PARENT) { @@ -853,6 +837,9 @@ void SpringBoneSimulator3D::set_joint_radius(int p_index, int p_joint, float p_r Vector &joints = settings[p_index]->joints; ERR_FAIL_INDEX(p_joint, joints.size()); joints[p_joint]->radius = p_radius; +#ifdef TOOLS_ENABLED + update_gizmos(); +#endif // TOOLS_ENABLED } float SpringBoneSimulator3D::get_joint_radius(int p_index, int p_joint) const { @@ -1119,8 +1106,6 @@ void SpringBoneSimulator3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_end_bone_direction", "index"), &SpringBoneSimulator3D::get_end_bone_direction); ClassDB::bind_method(D_METHOD("set_end_bone_length", "index", "length"), &SpringBoneSimulator3D::set_end_bone_length); ClassDB::bind_method(D_METHOD("get_end_bone_length", "index"), &SpringBoneSimulator3D::get_end_bone_length); - ClassDB::bind_method(D_METHOD("set_end_bone_tip_radius", "index", "radius"), &SpringBoneSimulator3D::set_end_bone_tip_radius); - ClassDB::bind_method(D_METHOD("get_end_bone_tip_radius", "index"), &SpringBoneSimulator3D::get_end_bone_tip_radius); ClassDB::bind_method(D_METHOD("set_center_from", "index", "center_from"), &SpringBoneSimulator3D::set_center_from); ClassDB::bind_method(D_METHOD("get_center_from", "index"), &SpringBoneSimulator3D::get_center_from); diff --git a/godot/scene/3d/spring_bone_simulator_3d.h b/godot/scene/3d/spring_bone_simulator_3d.h index 9c9aa913..0a934e86 100644 --- a/godot/scene/3d/spring_bone_simulator_3d.h +++ b/godot/scene/3d/spring_bone_simulator_3d.h @@ -181,8 +181,6 @@ class SpringBoneSimulator3D : public SkeletonModifier3D { BoneDirection get_end_bone_direction(int p_index) const; void set_end_bone_length(int p_index, float p_length); float get_end_bone_length(int p_index) const; - void set_end_bone_tip_radius(int p_index, float p_radius); - float get_end_bone_tip_radius(int p_index) const; Vector3 get_end_bone_axis(int p_end_bone, BoneDirection p_direction) const; // Helper. void set_center_from(int p_index, CenterFrom p_center_from); diff --git a/godot/scene/gui/color_picker.cpp b/godot/scene/gui/color_picker.cpp index f5be4c59..349c4b22 100644 --- a/godot/scene/gui/color_picker.cpp +++ b/godot/scene/gui/color_picker.cpp @@ -159,7 +159,6 @@ void ColorPicker::_notification(int p_what) { } DisplayServer *ds = DisplayServer::get_singleton(); Vector2 ofs = ds->mouse_get_position(); - picker_window->set_position(ofs - Vector2(28, 28)); Color c = DisplayServer::get_singleton()->screen_get_pixel(ofs); @@ -171,8 +170,13 @@ void ColorPicker::_notification(int p_what) { picker_window->set_position(ofs - Vector2(28, 28)); picker_texture_zoom->set_texture(ImageTexture::create_from_image(zoom_preview_img)); } else { - Size2i screen_size = ds->screen_get_size(); - picker_window->set_position(ofs + Vector2(ofs.x < screen_size.width / 2 ? 8 : -36, ofs.y < screen_size.height / 2 ? 8 : -36)); + Size2i screen_size = ds->screen_get_size(DisplayServer::SCREEN_WITH_MOUSE_FOCUS); + Vector2i screen_position = ds->screen_get_position(DisplayServer::SCREEN_WITH_MOUSE_FOCUS); + + float ofs_decal_x = (ofs.x < screen_position.x + screen_size.width - 51) ? 8 : -36; + float ofs_decal_y = (ofs.y < screen_position.y + screen_size.height - 51) ? 8 : -36; + + picker_window->set_position(ofs + Vector2(ofs_decal_x, ofs_decal_y)); } set_pick_color(c); @@ -1879,35 +1883,23 @@ void ColorPicker::_pick_button_pressed_legacy() { picker_window->add_child(picker_texture_rect); picker_texture_rect->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_picker_texture_input)); - bool has_feature_exclude_from_capture = DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE); picker_preview = memnew(Panel); picker_preview->set_mouse_filter(MOUSE_FILTER_IGNORE); - if (!has_feature_exclude_from_capture) { - picker_preview->set_size(Vector2i(28, 28)); - } else { - picker_preview->set_size(Vector2i(55, 72)); - } + picker_preview->set_size(Vector2i(55, 72)); picker_window->add_child(picker_preview); picker_preview_color = memnew(Panel); picker_preview_color->set_mouse_filter(MOUSE_FILTER_IGNORE); - if (!has_feature_exclude_from_capture) { - picker_preview_color->set_size(Vector2i(24, 24)); - picker_preview_color->set_position(Vector2i(2, 2)); - } else { - picker_preview_color->set_size(Vector2i(51, 15)); - picker_preview_color->set_position(Vector2i(2, 55)); - } + picker_preview_color->set_size(Vector2i(51, 15)); + picker_preview_color->set_position(Vector2i(2, 55)); picker_preview->add_child(picker_preview_color); - if (has_feature_exclude_from_capture) { - picker_texture_zoom = memnew(TextureRect); - picker_texture_zoom->set_mouse_filter(MOUSE_FILTER_IGNORE); - picker_texture_zoom->set_custom_minimum_size(Vector2i(51, 51)); - picker_texture_zoom->set_position(Vector2i(2, 2)); - picker_texture_zoom->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); - picker_preview->add_child(picker_texture_zoom); - } + picker_texture_zoom = memnew(TextureRect); + picker_texture_zoom->set_mouse_filter(MOUSE_FILTER_IGNORE); + picker_texture_zoom->set_custom_minimum_size(Vector2i(51, 51)); + picker_texture_zoom->set_position(Vector2i(2, 2)); + picker_texture_zoom->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + picker_preview->add_child(picker_texture_zoom); picker_preview_style_box.instantiate(); picker_preview->add_theme_style_override(SceneStringName(panel), picker_preview_style_box); @@ -1923,9 +1915,16 @@ void ColorPicker::_pick_button_pressed_legacy() { picker_window->set_position(Point2i()); picker_texture_rect->set_texture(tx); + Vector2 ofs = picker_window->get_mouse_position(); + picker_preview->set_position(ofs - Vector2(28, 28)); + + Vector2 scale = screen_rect.size / tx->get_image()->get_size(); + ofs /= scale; + Ref atlas; atlas.instantiate(); atlas->set_atlas(tx); + atlas->set_region(Rect2i(ofs.x - 8, ofs.y - 8, 17, 17)); picker_texture_zoom->set_texture(atlas); } else { screen_rect = picker_window->get_parent_rect(); @@ -1951,7 +1950,17 @@ void ColorPicker::_pick_button_pressed_legacy() { target_image->blit_rect(img, Rect2i(Point2i(0, 0), img->get_size()), w->get_position()); } - picker_texture_rect->set_texture(ImageTexture::create_from_image(target_image)); + Ref tx = ImageTexture::create_from_image(target_image); + picker_texture_rect->set_texture(tx); + + Vector2 ofs = screen_rect.position - DisplayServer::get_singleton()->mouse_get_position(); + picker_preview->set_position(ofs - Vector2(28, 28)); + + Ref atlas; + atlas.instantiate(); + atlas->set_atlas(tx); + atlas->set_region(Rect2i(ofs.x - 8, ofs.y - 8, 17, 17)); + picker_texture_zoom->set_texture(atlas); } picker_window->set_size(screen_rect.size); diff --git a/godot/scene/gui/rich_text_label.cpp b/godot/scene/gui/rich_text_label.cpp index 12954c62..87c7062f 100644 --- a/godot/scene/gui/rich_text_label.cpp +++ b/godot/scene/gui/rich_text_label.cpp @@ -1222,7 +1222,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o custom_fx_ok = effect_status; char_xform = charfx->transform; - fx_offset += charfx->offset; + fx_offset = charfx->offset; font_color = charfx->color; frid = charfx->font; gl = charfx->glyph_index; diff --git a/godot/scene/gui/tab_bar.cpp b/godot/scene/gui/tab_bar.cpp index e8ff6420..65e3d23c 100644 --- a/godot/scene/gui/tab_bar.cpp +++ b/godot/scene/gui/tab_bar.cpp @@ -1488,8 +1488,8 @@ int TabBar::get_tab_width(int p_idx) const { style = theme_cache.tab_disabled_style; } else if (current == p_idx) { style = theme_cache.tab_selected_style; - // Use the unselected style's width if the hovered one is shorter, to avoid an infinite loop when switching tabs with the mouse. - } else if (hover == p_idx && theme_cache.tab_hovered_style->get_minimum_size().width >= theme_cache.tab_unselected_style->get_minimum_size().width) { + // Always pick the widest style between hovered and unselected, to avoid an infinite loop when switching tabs with the mouse. + } else if (theme_cache.tab_hovered_style->get_minimum_size().width > theme_cache.tab_unselected_style->get_minimum_size().width) { style = theme_cache.tab_hovered_style; } else { style = theme_cache.tab_unselected_style; diff --git a/godot/scene/resources/particle_process_material.cpp b/godot/scene/resources/particle_process_material.cpp index 13bc0201..2c54564f 100644 --- a/godot/scene/resources/particle_process_material.cpp +++ b/godot/scene/resources/particle_process_material.cpp @@ -132,6 +132,7 @@ void ParticleProcessMaterial::init_shaders() { shader_names->sub_emitter_frequency = "sub_emitter_frequency"; shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end"; shader_names->sub_emitter_amount_at_collision = "sub_emitter_amount_at_collision"; + shader_names->sub_emitter_amount_at_start = "sub_emitter_amount_at_start"; shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity"; shader_names->collision_friction = "collision_friction"; @@ -287,6 +288,9 @@ void ParticleProcessMaterial::_update_shader() { if (sub_emitter_mode == SUB_EMITTER_AT_COLLISION) { code += "uniform int sub_emitter_amount_at_collision;\n"; } + if (sub_emitter_mode == SUB_EMITTER_AT_START) { + code += "uniform int sub_emitter_amount_at_start;\n"; + } code += "uniform bool sub_emitter_keep_velocity;\n"; } @@ -923,6 +927,10 @@ void ParticleProcessMaterial::_update_shader() { code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n\n"; + if (sub_emitter_mode == SUB_EMITTER_AT_START && !RenderingServer::get_singleton()->is_low_end()) { + code += " bool just_spawned = CUSTOM.y == 0.0;\n"; + } + code += " CUSTOM.y += DELTA / LIFETIME;\n"; code += " CUSTOM.y = mix(CUSTOM.y, 1.0, INTERPOLATE_TO_END);\n"; code += " float lifetime_percent = CUSTOM.y / params.lifetime;\n"; @@ -1139,6 +1147,11 @@ void ParticleProcessMaterial::_update_shader() { code += " emit_count = sub_emitter_amount_at_end;\n"; code += " }\n"; } break; + case SUB_EMITTER_AT_START: { + code += " if (just_spawned) {\n"; + code += " emit_count = sub_emitter_amount_at_start;\n"; + code += " }\n"; + } break; default: { } } @@ -1858,6 +1871,10 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const p_property.usage = PROPERTY_USAGE_NONE; } + if (p_property.name == "sub_emitter_amount_at_start" && sub_emitter_mode != SUB_EMITTER_AT_START) { + p_property.usage = PROPERTY_USAGE_NONE; + } + if (!turbulence_enabled) { if (p_property.name == "turbulence_noise_strength" || p_property.name == "turbulence_noise_scale" || @@ -1932,6 +1949,15 @@ int ParticleProcessMaterial::get_sub_emitter_amount_at_collision() const { return sub_emitter_amount_at_collision; } +void ParticleProcessMaterial::set_sub_emitter_amount_at_start(int p_amount) { + sub_emitter_amount_at_start = p_amount; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_start, p_amount); +} + +int ParticleProcessMaterial::get_sub_emitter_amount_at_start() const { + return sub_emitter_amount_at_start; +} + void ParticleProcessMaterial::set_sub_emitter_keep_velocity(bool p_enable) { sub_emitter_keep_velocity = p_enable; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable); @@ -2113,6 +2139,9 @@ void ParticleProcessMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_collision"), &ParticleProcessMaterial::get_sub_emitter_amount_at_collision); ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_collision", "amount"), &ParticleProcessMaterial::set_sub_emitter_amount_at_collision); + ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_start"), &ParticleProcessMaterial::get_sub_emitter_amount_at_start); + ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_start", "amount"), &ParticleProcessMaterial::set_sub_emitter_amount_at_start); + ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticleProcessMaterial::get_sub_emitter_keep_velocity); ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticleProcessMaterial::set_sub_emitter_keep_velocity); @@ -2242,10 +2271,11 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_scale"), "set_collision_use_scale", "is_collision_using_scale"); ADD_GROUP("Sub Emitter", "sub_emitter_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,At End,At Collision"), "set_sub_emitter_mode", "get_sub_emitter_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled:0,Constant:1,At Start:4,At End:2,At Collision:3"), "set_sub_emitter_mode", "get_sub_emitter_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:Hz"), "set_sub_emitter_frequency", "get_sub_emitter_frequency"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_collision", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_collision", "get_sub_emitter_amount_at_collision"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_start", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_start", "get_sub_emitter_amount_at_start"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity"); ADD_SIGNAL(MethodInfo("emission_shape_changed")); @@ -2290,6 +2320,7 @@ void ParticleProcessMaterial::_bind_methods() { BIND_ENUM_CONSTANT(SUB_EMITTER_CONSTANT); BIND_ENUM_CONSTANT(SUB_EMITTER_AT_END); BIND_ENUM_CONSTANT(SUB_EMITTER_AT_COLLISION); + BIND_ENUM_CONSTANT(SUB_EMITTER_AT_START); BIND_ENUM_CONSTANT(SUB_EMITTER_MAX); BIND_ENUM_CONSTANT(COLLISION_DISABLED); @@ -2361,6 +2392,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() : set_sub_emitter_frequency(4); set_sub_emitter_amount_at_end(1); set_sub_emitter_amount_at_collision(1); + set_sub_emitter_amount_at_start(1); set_sub_emitter_keep_velocity(false); set_attractor_interaction_enabled(true); diff --git a/godot/scene/resources/particle_process_material.h b/godot/scene/resources/particle_process_material.h index 12e3fbb6..30bf9fc3 100644 --- a/godot/scene/resources/particle_process_material.h +++ b/godot/scene/resources/particle_process_material.h @@ -96,6 +96,7 @@ class ParticleProcessMaterial : public Material { SUB_EMITTER_CONSTANT, SUB_EMITTER_AT_END, SUB_EMITTER_AT_COLLISION, + SUB_EMITTER_AT_START, SUB_EMITTER_MAX }; @@ -117,7 +118,7 @@ class ParticleProcessMaterial : public Material { uint64_t emission_shape : 3; uint64_t invalid_key : 1; uint64_t has_emission_color : 1; - uint64_t sub_emitter : 2; + uint64_t sub_emitter : 3; uint64_t attractor_enabled : 1; uint64_t collision_mode : 2; uint64_t collision_scale : 1; @@ -282,6 +283,7 @@ class ParticleProcessMaterial : public Material { StringName sub_emitter_frequency; StringName sub_emitter_amount_at_end; StringName sub_emitter_amount_at_collision; + StringName sub_emitter_amount_at_start; StringName sub_emitter_keep_velocity; StringName collision_friction; @@ -349,6 +351,7 @@ class ParticleProcessMaterial : public Material { double sub_emitter_frequency = 0.0; int sub_emitter_amount_at_end = 0; int sub_emitter_amount_at_collision = 0; + int sub_emitter_amount_at_start = 0; bool sub_emitter_keep_velocity = false; //do not save emission points here @@ -487,6 +490,9 @@ class ParticleProcessMaterial : public Material { void set_sub_emitter_amount_at_collision(int p_amount); int get_sub_emitter_amount_at_collision() const; + void set_sub_emitter_amount_at_start(int p_amount); + int get_sub_emitter_amount_at_start() const; + void set_sub_emitter_keep_velocity(bool p_enable); bool get_sub_emitter_keep_velocity() const; diff --git a/godot/servers/navigation_server_2d.cpp b/godot/servers/navigation_server_2d.cpp index a24a3312..7994cd17 100644 --- a/godot/servers/navigation_server_2d.cpp +++ b/godot/servers/navigation_server_2d.cpp @@ -91,6 +91,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer2D::region_get_closest_point); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point); + ClassDB::bind_method(D_METHOD("region_get_bounds", "region"), &NavigationServer2D::region_get_bounds); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map); diff --git a/godot/servers/navigation_server_2d.h b/godot/servers/navigation_server_2d.h index 510cf396..e53d84ae 100644 --- a/godot/servers/navigation_server_2d.h +++ b/godot/servers/navigation_server_2d.h @@ -154,6 +154,7 @@ class NavigationServer2D : public Object { virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const = 0; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + virtual Rect2 region_get_bounds(RID p_region) const = 0; /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; diff --git a/godot/servers/navigation_server_2d_dummy.h b/godot/servers/navigation_server_2d_dummy.h index dd6e618e..4e16c00e 100644 --- a/godot/servers/navigation_server_2d_dummy.h +++ b/godot/servers/navigation_server_2d_dummy.h @@ -87,6 +87,7 @@ class NavigationServer2DDummy : public NavigationServer2D { Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); } Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override { return Vector2(); } Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); } + Rect2 region_get_bounds(RID p_region) const override { return Rect2(); } RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} diff --git a/godot/servers/navigation_server_3d.cpp b/godot/servers/navigation_server_3d.cpp index 702439e4..a34eeb36 100644 --- a/godot/servers/navigation_server_3d.cpp +++ b/godot/servers/navigation_server_3d.cpp @@ -106,6 +106,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer3D::region_get_closest_point); ClassDB::bind_method(D_METHOD("region_get_closest_point_normal", "region", "to_point"), &NavigationServer3D::region_get_closest_point_normal); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point); + ClassDB::bind_method(D_METHOD("region_get_bounds", "region"), &NavigationServer3D::region_get_bounds); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map); diff --git a/godot/servers/navigation_server_3d.h b/godot/servers/navigation_server_3d.h index 5a9896a8..e94ca5a4 100644 --- a/godot/servers/navigation_server_3d.h +++ b/godot/servers/navigation_server_3d.h @@ -176,6 +176,8 @@ class NavigationServer3D : public Object { virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const = 0; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + virtual AABB region_get_bounds(RID p_region) const = 0; + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; diff --git a/godot/servers/navigation_server_3d_dummy.h b/godot/servers/navigation_server_3d_dummy.h index 2ab4c6e6..c3f1420b 100644 --- a/godot/servers/navigation_server_3d_dummy.h +++ b/godot/servers/navigation_server_3d_dummy.h @@ -99,6 +99,7 @@ class NavigationServer3DDummy : public NavigationServer3D { Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override { return Vector3(); } Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override { return Vector3(); } Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } + AABB region_get_bounds(RID p_region) const override { return AABB(); } RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} diff --git a/godot/servers/rendering/dummy/storage/mesh_storage.h b/godot/servers/rendering/dummy/storage/mesh_storage.h index 97789d3a..ffffa4ef 100644 --- a/godot/servers/rendering/dummy/storage/mesh_storage.h +++ b/godot/servers/rendering/dummy/storage/mesh_storage.h @@ -151,7 +151,7 @@ class MeshStorage : public RendererMeshStorage { virtual void _multimesh_initialize(RID p_rid) override; virtual void _multimesh_free(RID p_rid) override; - virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override {} virtual int _multimesh_get_instance_count(RID p_multimesh) const override { return 0; } virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} @@ -171,6 +171,7 @@ class MeshStorage : public RendererMeshStorage { virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } virtual void _multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) override; + virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override { return RID(); } virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override { return RID(); } virtual Vector _multimesh_get_buffer(RID p_multimesh) const override; diff --git a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 207538c7..70b3a89c 100644 --- a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -599,7 +599,11 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p instance_count /= surf->owner->trail_steps; } - RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); + if (bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT)) { + RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), mesh_storage->_multimesh_get_command_buffer_rd_rid(surf->owner->data->base), surf->surface_index * sizeof(uint32_t) * mesh_storage->INDIRECT_MULTIMESH_COMMAND_STRIDE, 1, 0); + } else { + RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); + } } i += element_info.repeat - 1; //skip equal elements @@ -1083,6 +1087,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con } else { surf->sort.lod_index = 0; if (p_render_data->render_info) { + // This does not include primitives rendered via indirect draw calls. uint32_t to_draw = mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface); to_draw = _indices_to_primitives(surf->primitive, to_draw); to_draw *= inst->instance_count; @@ -4205,9 +4210,9 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p ginstance->base_flags = 0; bool store_transform = true; - if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; + if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; } @@ -4217,6 +4222,9 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; } + if (mesh_storage->multimesh_uses_indirect(ginstance->data->base)) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT; + } ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); diff --git a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 42c1da1a..567ca5d8 100644 --- a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -255,6 +255,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { + INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT = 1 << 2, INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, diff --git a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 6d217964..1651e822 100644 --- a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2380,7 +2380,11 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr instance_count /= surf->owner->trail_steps; } - RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); + if (bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT)) { + RD::get_singleton()->draw_list_draw_indirect(draw_list, index_array_rd.is_valid(), mesh_storage->_multimesh_get_command_buffer_rd_rid(surf->owner->data->base), surf->surface_index * sizeof(uint32_t) * mesh_storage->INDIRECT_MULTIMESH_COMMAND_STRIDE, 1, 0); + } else { + RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count); + } } } @@ -2801,6 +2805,7 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; + if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; } @@ -2810,6 +2815,9 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) { ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; } + if (mesh_storage->multimesh_uses_indirect(ginstance->data->base)) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT; + } ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); diff --git a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 77242c8c..e6aec975 100644 --- a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -358,6 +358,7 @@ class RenderForwardMobile : public RendererSceneRenderRD { // When changing any of these enums, remember to change the corresponding enums in the shader files as well. enum { + INSTANCE_DATA_FLAG_MULTIMESH_INDIRECT = 1 << 2, INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, diff --git a/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index bd5f8dc7..84f4b452 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1485,7 +1485,7 @@ void MeshStorage::_multimesh_free(RID p_rid) { multimesh_owner.free(p_rid); } -void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -1521,6 +1521,9 @@ void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS: multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0); multimesh->buffer_set = false; + multimesh->indirect = p_use_indirect; + multimesh->command_buffer = RID(); + //print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances)); multimesh->data_cache = Vector(); multimesh->aabb = AABB(); @@ -1609,6 +1612,30 @@ void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) { } multimesh->mesh = p_mesh; + if (multimesh->indirect) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL(mesh); + if (mesh->surface_count > 0) { + if (multimesh->command_buffer.is_valid()) { + RD::get_singleton()->free(multimesh->command_buffer); + } + + Vector newVector; + newVector.resize_zeroed(sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE * mesh->surface_count); + + for (uint32_t i = 0; i < mesh->surface_count; i++) { + uint32_t count = mesh_surface_get_vertices_drawn_count(mesh->surfaces[i]); + newVector.set(i * sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE, static_cast(count)); + newVector.set(i * sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE + 1, static_cast(count >> 8)); + newVector.set(i * sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE + 2, static_cast(count >> 16)); + newVector.set(i * sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE + 3, static_cast(count >> 24)); + } + + RID newBuffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE * mesh->surface_count, newVector, RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + multimesh->command_buffer = newBuffer; + } + } + if (multimesh->instances == 0) { return; } @@ -2064,6 +2091,12 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector &p_ } } +RID MeshStorage::_multimesh_get_command_buffer_rd_rid(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_NULL_V(multimesh, RID()); + return multimesh->command_buffer; +} + RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, RID()); @@ -2111,6 +2144,15 @@ void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visibl multimesh->visible_instances = p_visible; + if (multimesh->indirect) { //we have to update the command buffer for the instance counts, in each stride this will be the second integer. + Mesh *mesh = mesh_owner.get_or_null(multimesh->mesh); + if (mesh != nullptr) { + for (uint32_t i = 0; i < mesh->surface_count; i++) { + RD::get_singleton()->buffer_update(multimesh->command_buffer, (i * sizeof(uint32_t) * INDIRECT_MULTIMESH_COMMAND_STRIDE) + sizeof(uint32_t), sizeof(uint32_t), &p_visible); + } + } + } + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } diff --git a/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 7a8f0c8e..5b8ed343 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/godot/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -59,6 +59,10 @@ class MeshStorage : public RendererMeshStorage { DEFAULT_RD_BUFFER_MAX, }; + enum IndirectMultiMesh : uint32_t { + INDIRECT_MULTIMESH_COMMAND_STRIDE = 5 + }; + private: static MeshStorage *singleton; @@ -226,6 +230,7 @@ class MeshStorage : public RendererMeshStorage { AABB custom_aabb; bool aabb_dirty = false; bool buffer_set = false; + bool indirect = false; bool motion_vectors_enabled = false; uint32_t motion_vectors_current_offset = 0; uint32_t motion_vectors_previous_offset = 0; @@ -243,6 +248,7 @@ class MeshStorage : public RendererMeshStorage { RID buffer; //storage buffer RID uniform_set_3d; RID uniform_set_2d; + RID command_buffer; //used if indirect setting is used bool dirty = false; MultiMesh *dirty_list = nullptr; @@ -637,7 +643,7 @@ class MeshStorage : public RendererMeshStorage { virtual void _multimesh_initialize(RID p_multimesh) override; virtual void _multimesh_free(RID p_rid) override; - virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override; virtual int _multimesh_get_instance_count(RID p_multimesh) const override; virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; @@ -654,6 +660,7 @@ class MeshStorage : public RendererMeshStorage { virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; virtual void _multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) override; + virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override; virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override; virtual Vector _multimesh_get_buffer(RID p_multimesh) const override; @@ -672,6 +679,11 @@ class MeshStorage : public RendererMeshStorage { bool _multimesh_uses_motion_vectors_offsets(RID p_multimesh); bool _multimesh_uses_motion_vectors(RID p_multimesh); + _FORCE_INLINE_ bool multimesh_uses_indirect(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->indirect; + } + _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); return multimesh->xform_format; diff --git a/godot/servers/rendering/rendering_device.compat.inc b/godot/servers/rendering/rendering_device.compat.inc index 11aede71..c5a1e4ff 100644 --- a/godot/servers/rendering/rendering_device.compat.inc +++ b/godot/servers/rendering/rendering_device.compat.inc @@ -143,6 +143,18 @@ RenderingDevice::FramebufferFormatID RenderingDevice::_screen_get_framebuffer_fo return screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID); } +RID RenderingDevice::_uniform_buffer_create_bind_compat_100062(uint32_t p_size_bytes, const Vector &p_data) { + return uniform_buffer_create(p_size_bytes, p_data, false); +} + +RID RenderingDevice::_vertex_buffer_create_bind_compat_100062(uint32_t p_size_bytes, const Vector &p_data, bool p_use_as_storage) { + return vertex_buffer_create(p_size_bytes, p_data, p_use_as_storage, false); +} + +RID RenderingDevice::_index_buffer_create_bind_compat_100062(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector &p_data, bool p_use_restart_indices) { + return index_buffer_create(p_size_indices, p_format, p_data, p_use_restart_indices, false); +} + 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); @@ -166,6 +178,10 @@ void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::_draw_list_begin_bind_compat_90993, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2())); ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::_draw_list_begin_bind_compat_98670, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0)); + + ClassDB::bind_compatibility_method(D_METHOD("uniform_buffer_create"), &RenderingDevice::_uniform_buffer_create_bind_compat_100062, DEFVAL(Vector())); + ClassDB::bind_compatibility_method(D_METHOD("vertex_buffer_create"), &RenderingDevice::_vertex_buffer_create_bind_compat_100062, DEFVAL(Vector()), DEFVAL(false)); + ClassDB::bind_compatibility_method(D_METHOD("index_buffer_create"), &RenderingDevice::_index_buffer_create_bind_compat_100062, DEFVAL(Vector()), DEFVAL(false)); } #endif diff --git a/godot/servers/rendering/rendering_device.cpp b/godot/servers/rendering/rendering_device.cpp index d4140e14..9716a42c 100644 --- a/godot/servers/rendering/rendering_device.cpp +++ b/godot/servers/rendering/rendering_device.cpp @@ -786,6 +786,16 @@ Error RenderingDevice::buffer_get_data_async(RID p_buffer, const Callable &p_cal return OK; } +uint64_t RenderingDevice::buffer_get_device_address(RID p_buffer) { + ERR_RENDER_THREAD_GUARD_V(0); + + Buffer *buffer = _get_buffer_from_owner(p_buffer); + ERR_FAIL_NULL_V_MSG(buffer, 0, "Buffer argument is not a valid buffer of any type."); + ERR_FAIL_COND_V_MSG(!buffer->usage.has_flag(RDD::BUFFER_USAGE_DEVICE_ADDRESS_BIT), 0, "Buffer was not created with device address flag."); + + return driver->buffer_get_device_address(buffer->driver_id); +} + RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector &p_data, BitField p_usage) { ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); @@ -795,6 +805,14 @@ RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vectorbuffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); @@ -2960,7 +2978,7 @@ bool RenderingDevice::sampler_is_format_supported_for_filter(DataFormat p_format /**** VERTEX BUFFER ****/ /***********************/ -RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector &p_data, bool p_use_as_storage) { +RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector &p_data, bool p_use_as_storage, bool p_enable_device_address) { ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); Buffer buffer; @@ -2969,6 +2987,9 @@ RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vectorbuffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); @@ -3099,7 +3120,7 @@ RID RenderingDevice::vertex_array_create(uint32_t p_vertex_count, VertexFormatID return id; } -RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector &p_data, bool p_use_restart_indices) { +RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector &p_data, bool p_use_restart_indices, bool p_enable_device_address) { ERR_FAIL_COND_V(p_index_count == 0, RID()); IndexBuffer index_buffer; @@ -3138,6 +3159,9 @@ RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferForm #endif index_buffer.size = size_bytes; index_buffer.usage = (RDD::BUFFER_USAGE_TRANSFER_FROM_BIT | RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_INDEX_BIT); + if (p_enable_device_address) { + index_buffer.usage.set_flag(RDD::BUFFER_USAGE_DEVICE_ADDRESS_BIT); + } index_buffer.driver_id = driver->buffer_create(index_buffer.size, index_buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!index_buffer.driver_id, RID()); @@ -3346,12 +3370,15 @@ uint64_t RenderingDevice::shader_get_vertex_input_attribute_mask(RID p_shader) { /**** UNIFORMS ****/ /******************/ -RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector &p_data) { +RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector &p_data, bool p_enable_device_address) { ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); Buffer buffer; buffer.size = p_size_bytes; buffer.usage = (RDD::BUFFER_USAGE_TRANSFER_TO_BIT | RDD::BUFFER_USAGE_UNIFORM_BIT); + if (p_enable_device_address) { + buffer.usage.set_flag(RDD::BUFFER_USAGE_DEVICE_ADDRESS_BIT); + } buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); @@ -7267,11 +7294,11 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("sampler_create", "state"), &RenderingDevice::_sampler_create); ClassDB::bind_method(D_METHOD("sampler_is_format_supported_for_filter", "format", "sampler_filter"), &RenderingDevice::sampler_is_format_supported_for_filter); - ClassDB::bind_method(D_METHOD("vertex_buffer_create", "size_bytes", "data", "use_as_storage"), &RenderingDevice::vertex_buffer_create, DEFVAL(Vector()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("vertex_buffer_create", "size_bytes", "data", "use_as_storage", "enable_device_address"), &RenderingDevice::vertex_buffer_create, DEFVAL(Vector()), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("vertex_format_create", "vertex_descriptions"), &RenderingDevice::_vertex_format_create); ClassDB::bind_method(D_METHOD("vertex_array_create", "vertex_count", "vertex_format", "src_buffers", "offsets"), &RenderingDevice::_vertex_array_create, DEFVAL(Vector())); - ClassDB::bind_method(D_METHOD("index_buffer_create", "size_indices", "format", "data", "use_restart_indices"), &RenderingDevice::index_buffer_create, DEFVAL(Vector()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("index_buffer_create", "size_indices", "format", "data", "use_restart_indices", "enable_device_address"), &RenderingDevice::index_buffer_create, DEFVAL(Vector()), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("index_array_create", "index_buffer", "index_offset", "index_count"), &RenderingDevice::index_array_create); ClassDB::bind_method(D_METHOD("shader_compile_spirv_from_source", "shader_source", "allow_cache"), &RenderingDevice::_shader_compile_spirv_from_source, DEFVAL(true)); @@ -7282,7 +7309,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("shader_get_vertex_input_attribute_mask", "shader"), &RenderingDevice::shader_get_vertex_input_attribute_mask); - ClassDB::bind_method(D_METHOD("uniform_buffer_create", "size_bytes", "data"), &RenderingDevice::uniform_buffer_create, DEFVAL(Vector())); + ClassDB::bind_method(D_METHOD("uniform_buffer_create", "size_bytes", "data", "enable_device_address"), &RenderingDevice::uniform_buffer_create, DEFVAL(Vector()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("storage_buffer_create", "size_bytes", "data", "usage"), &RenderingDevice::storage_buffer_create, DEFVAL(Vector()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("texture_buffer_create", "size_bytes", "format", "data"), &RenderingDevice::texture_buffer_create, DEFVAL(Vector())); @@ -7294,6 +7321,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes"), &RenderingDevice::buffer_clear); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("buffer_get_data_async", "buffer", "callback", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data_async, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("buffer_get_device_address", "buffer"), &RenderingDevice::buffer_get_device_address); ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass", "specialization_constants"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0), DEFVAL(TypedArray())); ClassDB::bind_method(D_METHOD("render_pipeline_is_valid", "render_pipeline"), &RenderingDevice::render_pipeline_is_valid); @@ -7350,6 +7378,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("get_captured_timestamp_cpu_time", "index"), &RenderingDevice::get_captured_timestamp_cpu_time); ClassDB::bind_method(D_METHOD("get_captured_timestamp_name", "index"), &RenderingDevice::get_captured_timestamp_name); + ClassDB::bind_method(D_METHOD("has_feature", "feature"), &RenderingDevice::has_feature); ClassDB::bind_method(D_METHOD("limit_get", "limit"), &RenderingDevice::limit_get); ClassDB::bind_method(D_METHOD("get_frame_delay"), &RenderingDevice::get_frame_delay); ClassDB::bind_method(D_METHOD("submit"), &RenderingDevice::submit); @@ -7724,6 +7753,7 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(INDEX_BUFFER_FORMAT_UINT32); BIND_BITFIELD_FLAG(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + BIND_BITFIELD_FLAG(STORAGE_BUFFER_USAGE_DEVICE_ADDRESS); BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER); //for sampling only (sampler GLSL type) BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER_WITH_TEXTURE); // for sampling only); but includes a texture); (samplerXX GLSL type)); first a sampler then a texture @@ -7868,6 +7898,8 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT); BIND_ENUM_CONSTANT(PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT); + BIND_ENUM_CONSTANT(SUPPORTS_BUFFER_DEVICE_ADDRESS); + BIND_ENUM_CONSTANT(LIMIT_MAX_BOUND_UNIFORM_SETS); BIND_ENUM_CONSTANT(LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS); BIND_ENUM_CONSTANT(LIMIT_MAX_TEXTURES_PER_UNIFORM_SET); diff --git a/godot/servers/rendering/rendering_device.h b/godot/servers/rendering/rendering_device.h index a4aa4df6..10723392 100644 --- a/godot/servers/rendering/rendering_device.h +++ b/godot/servers/rendering/rendering_device.h @@ -222,6 +222,7 @@ class RenderingDevice : public RenderingDeviceCommons { Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size); Vector buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); // This causes stall, only use to retrieve large buffers for saving. Error buffer_get_data_async(RID p_buffer, const Callable &p_callback, uint32_t p_offset = 0, uint32_t p_size = 0); + uint64_t buffer_get_device_address(RID p_buffer); private: /******************/ @@ -753,13 +754,13 @@ class RenderingDevice : public RenderingDeviceCommons { RID_Owner index_array_owner; public: - RID vertex_buffer_create(uint32_t p_size_bytes, const Vector &p_data = Vector(), bool p_use_as_storage = false); + RID vertex_buffer_create(uint32_t p_size_bytes, const Vector &p_data = Vector(), bool p_use_as_storage = false, bool p_enable_device_address = false); // This ID is warranted to be unique for the same formats, does not need to be freed VertexFormatID vertex_format_create(const Vector &p_vertex_descriptions); RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector &p_src_buffers, const Vector &p_offsets = Vector()); - RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector &p_data = Vector(), bool p_use_restart_indices = false); + RID index_buffer_create(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector &p_data = Vector(), bool p_use_restart_indices = false, bool p_enable_device_address = false); RID index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count); /****************/ @@ -893,6 +894,10 @@ class RenderingDevice : public RenderingDeviceCommons { DrawListID _draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region); DrawListID _draw_list_begin_bind_compat_98670(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, uint32_t p_breadcrumb); + + RID _uniform_buffer_create_bind_compat_100062(uint32_t p_size_bytes, const Vector &p_data); + RID _vertex_buffer_create_bind_compat_100062(uint32_t p_size_bytes, const Vector &p_data, bool p_use_as_storage); + RID _index_buffer_create_bind_compat_100062(uint32_t p_size_indices, IndexBufferFormat p_format, const Vector &p_data, bool p_use_restart_indices); #endif public: @@ -926,14 +931,15 @@ class RenderingDevice : public RenderingDeviceCommons { String get_perf_report() const; enum StorageBufferUsage { - STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1, + STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = (1 << 0), + STORAGE_BUFFER_USAGE_DEVICE_ADDRESS = (1 << 1), }; /*****************/ /**** BUFFERS ****/ /*****************/ - RID uniform_buffer_create(uint32_t p_size_bytes, const Vector &p_data = Vector()); + RID uniform_buffer_create(uint32_t p_size_bytes, const Vector &p_data = Vector(), bool p_enable_device_address = false); RID storage_buffer_create(uint32_t p_size, const Vector &p_data = Vector(), BitField p_usage = 0); RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector &p_data = Vector()); diff --git a/godot/servers/rendering/rendering_device_commons.h b/godot/servers/rendering/rendering_device_commons.h index 2be675c4..67946fc4 100644 --- a/godot/servers/rendering/rendering_device_commons.h +++ b/godot/servers/rendering/rendering_device_commons.h @@ -885,6 +885,7 @@ class RenderingDeviceCommons : public Object { SUPPORTS_METALFX_TEMPORAL, // If not supported, a fragment shader with only side effects (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver. SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS, + SUPPORTS_BUFFER_DEVICE_ADDRESS, }; enum SubgroupOperations { diff --git a/godot/servers/rendering/rendering_device_driver.h b/godot/servers/rendering/rendering_device_driver.h index 2f6ea793..1f65cdca 100644 --- a/godot/servers/rendering/rendering_device_driver.h +++ b/godot/servers/rendering/rendering_device_driver.h @@ -197,6 +197,7 @@ class RenderingDeviceDriver : public RenderingDeviceCommons { BUFFER_USAGE_INDEX_BIT = (1 << 6), BUFFER_USAGE_VERTEX_BIT = (1 << 7), BUFFER_USAGE_INDIRECT_BIT = (1 << 8), + BUFFER_USAGE_DEVICE_ADDRESS_BIT = (1 << 17), }; enum { @@ -210,6 +211,8 @@ class RenderingDeviceDriver : public RenderingDeviceCommons { virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) = 0; virtual uint8_t *buffer_map(BufferID p_buffer) = 0; virtual void buffer_unmap(BufferID p_buffer) = 0; + // Only for a buffer with BUFFER_USAGE_DEVICE_ADDRESS_BIT. + virtual uint64_t buffer_get_device_address(BufferID p_buffer) = 0; /*****************/ /**** TEXTURE ****/ diff --git a/godot/servers/rendering/rendering_server_default.h b/godot/servers/rendering/rendering_server_default.h index 16d32f00..14794f7b 100644 --- a/godot/servers/rendering/rendering_server_default.h +++ b/godot/servers/rendering/rendering_server_default.h @@ -382,7 +382,7 @@ class RenderingServerDefault : public RenderingServer { FUNCRIDSPLIT(multimesh) - FUNC5(multimesh_allocate_data, RID, int, MultimeshTransformFormat, bool, bool) + FUNC6(multimesh_allocate_data, RID, int, MultimeshTransformFormat, bool, bool, bool) FUNC1RC(int, multimesh_get_instance_count, RID) FUNC2(multimesh_set_mesh, RID, RID) @@ -403,6 +403,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2RC(Color, multimesh_instance_get_custom_data, RID, int) FUNC2(multimesh_set_buffer, RID, const Vector &) + FUNC1RC(RID, multimesh_get_command_buffer_rd_rid, RID) FUNC1RC(RID, multimesh_get_buffer_rd_rid, RID) FUNC1RC(Vector, multimesh_get_buffer, RID) diff --git a/godot/servers/rendering/storage/mesh_storage.cpp b/godot/servers/rendering/storage/mesh_storage.cpp index 4a6746e6..5b899878 100644 --- a/godot/servers/rendering/storage/mesh_storage.cpp +++ b/godot/servers/rendering/storage/mesh_storage.cpp @@ -48,7 +48,7 @@ void RendererMeshStorage::multimesh_free(RID p_rid) { _multimesh_free(p_rid); } -void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) { MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); if (mmi) { mmi->_transform_format = p_transform_format; @@ -68,7 +68,7 @@ void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instanc mmi->_data_interpolated.resize_zeroed(size_in_floats); } - _multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data); + _multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data, p_use_indirect); } int RendererMeshStorage::multimesh_get_instance_count(RID p_multimesh) const { @@ -223,6 +223,10 @@ void RendererMeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer); + virtual RID multimesh_get_command_buffer_rd_rid(RID p_multimesh) const; virtual RID multimesh_get_buffer_rd_rid(RID p_multimesh) const; virtual Vector multimesh_get_buffer(RID p_multimesh) const; @@ -159,7 +160,7 @@ class RendererMeshStorage { virtual void _multimesh_initialize(RID p_rid) = 0; virtual void _multimesh_free(RID p_rid) = 0; - virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) = 0; virtual int _multimesh_get_instance_count(RID p_multimesh) const = 0; @@ -180,6 +181,7 @@ class RendererMeshStorage { virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; virtual void _multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) = 0; + virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const = 0; virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const = 0; virtual Vector _multimesh_get_buffer(RID p_multimesh) const = 0; diff --git a/godot/servers/rendering_server.compat.inc b/godot/servers/rendering_server.compat.inc index 99f2de9a..2ae2f3c8 100644 --- a/godot/servers/rendering_server.compat.inc +++ b/godot/servers/rendering_server.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +void RenderingServer::_multimesh_allocate_data_bind_compat_99455(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { + multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data, false); +} + void RenderingServer::_environment_set_fog_bind_compat_84792(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) { environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective, p_sky_affect, RS::EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL); } @@ -47,6 +51,7 @@ void RenderingServer::_canvas_item_add_circle_bind_compat_84523(RID p_item, cons } void RenderingServer::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("multimesh_allocate_data", "multimesh", "instances", "transform_format", "color_format", "custom_data_format"), &RenderingServer::_multimesh_allocate_data_bind_compat_99455, DEFVAL(false), DEFVAL(false)); ClassDB::bind_compatibility_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect"), &RenderingServer::_environment_set_fog_bind_compat_84792); ClassDB::bind_compatibility_method(D_METHOD("canvas_item_add_multiline", "item", "points", "colors", "width"), &RenderingServer::_canvas_item_add_multiline_bind_compat_84523, DEFVAL(-1.0)); ClassDB::bind_compatibility_method(D_METHOD("canvas_item_add_rect", "item", "rect", "color"), &RenderingServer::_canvas_item_add_rect_bind_compat_84523); diff --git a/godot/servers/rendering_server.cpp b/godot/servers/rendering_server.cpp index a38d9a4b..422faa13 100644 --- a/godot/servers/rendering_server.cpp +++ b/godot/servers/rendering_server.cpp @@ -2441,7 +2441,7 @@ void RenderingServer::_bind_methods() { /* MULTIMESH API */ ClassDB::bind_method(D_METHOD("multimesh_create"), &RenderingServer::multimesh_create); - ClassDB::bind_method(D_METHOD("multimesh_allocate_data", "multimesh", "instances", "transform_format", "color_format", "custom_data_format"), &RenderingServer::multimesh_allocate_data, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("multimesh_allocate_data", "multimesh", "instances", "transform_format", "color_format", "custom_data_format", "use_indirect"), &RenderingServer::multimesh_allocate_data, DEFVAL(false), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("multimesh_get_instance_count", "multimesh"), &RenderingServer::multimesh_get_instance_count); ClassDB::bind_method(D_METHOD("multimesh_set_mesh", "multimesh", "mesh"), &RenderingServer::multimesh_set_mesh); ClassDB::bind_method(D_METHOD("multimesh_instance_set_transform", "multimesh", "index", "transform"), &RenderingServer::multimesh_instance_set_transform); @@ -2459,6 +2459,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("multimesh_set_visible_instances", "multimesh", "visible"), &RenderingServer::multimesh_set_visible_instances); ClassDB::bind_method(D_METHOD("multimesh_get_visible_instances", "multimesh"), &RenderingServer::multimesh_get_visible_instances); ClassDB::bind_method(D_METHOD("multimesh_set_buffer", "multimesh", "buffer"), &RenderingServer::multimesh_set_buffer); + ClassDB::bind_method(D_METHOD("multimesh_get_command_buffer_rd_rid", "multimesh"), &RenderingServer::multimesh_get_command_buffer_rd_rid); ClassDB::bind_method(D_METHOD("multimesh_get_buffer_rd_rid", "multimesh"), &RenderingServer::multimesh_get_buffer_rd_rid); ClassDB::bind_method(D_METHOD("multimesh_get_buffer", "multimesh"), &RenderingServer::multimesh_get_buffer); diff --git a/godot/servers/rendering_server.h b/godot/servers/rendering_server.h index 94447baf..1206911e 100644 --- a/godot/servers/rendering_server.h +++ b/godot/servers/rendering_server.h @@ -457,7 +457,12 @@ class RenderingServer : public Object { MULTIMESH_INTERP_QUALITY_HIGH, }; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; +protected: +#ifndef DISABLE_DEPRECATED + void _multimesh_allocate_data_bind_compat_99455(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data); +#endif +public: + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) = 0; virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; @@ -478,6 +483,7 @@ class RenderingServer : public Object { virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; virtual void multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) = 0; + virtual RID multimesh_get_command_buffer_rd_rid(RID p_multimesh) const = 0; virtual RID multimesh_get_buffer_rd_rid(RID p_multimesh) const = 0; virtual Vector multimesh_get_buffer(RID p_multimesh) const = 0; diff --git a/godot/version.py b/godot/version.py index 22704a08..d176efb4 100644 --- a/godot/version.py +++ b/godot/version.py @@ -3,7 +3,7 @@ major = 4 minor = 4 patch = 0 -status = "dev" +status = "beta" module_config = "" website = "https://godotengine.org" docs = "latest"