diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 193942a1bb3a..6b786a7b5849 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -140,6 +140,18 @@ jobs: python-version: 3.8 scons-version: 4.0 + - name: Force remove preinstalled .NET SDKs + if: matrix.build-mono + run: | + sudo rm -rf /usr/share/dotnet/sdk/* + + - name: Setup older .NET SDK as baseline + if: matrix.build-mono + uses: actions/setup-dotnet@v4 + with: + # Targeting the oldest version we want to support to ensure it still builds. + dotnet-version: '8.0.100' + - name: Compilation uses: ./.github/actions/godot-build with: @@ -163,6 +175,7 @@ jobs: - name: Build .NET solutions if: matrix.build-mono run: | + dotnet --info ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd - name: Prepare artifact diff --git a/core/object/object.h b/core/object/object.h index cc577653f271..1d23311dd22c 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -65,7 +65,7 @@ enum PropertyHint { PROPERTY_HINT_DIR, ///< a directory path must be passed PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed - PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type + PROPERTY_HINT_RESOURCE_TYPE, ///< a comma-separated resource object type, e.g. "NoiseTexture,GradientTexture2D". Subclasses can be excluded with a "-" prefix if placed *after* the base class, e.g. "Texture2D,-MeshTexture". PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index e23b36e623b2..eb22fa087adf 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2030,16 +2030,16 @@ Help key. - Media back key. Not to be confused with the Back button on an Android device. + Back key. - Media forward key. + Forward key. Media stop key. - Media refresh key. + Refresh key. Volume down key. diff --git a/doc/classes/AStar3D.xml b/doc/classes/AStar3D.xml index 2e8ae37a20bd..31ee6d8e3c4e 100644 --- a/doc/classes/AStar3D.xml +++ b/doc/classes/AStar3D.xml @@ -6,29 +6,42 @@ A* (A star) is a computer algorithm used in pathfinding and graph traversal, the process of plotting short paths among vertices (points), passing through a given set of edges (segments). It enjoys widespread use due to its performance and accuracy. Godot's A* implementation uses points in 3D space and Euclidean distances by default. You must add points manually with [method add_point] and create segments manually with [method connect_points]. Once done, you can test if there is a path between two points with the [method are_points_connected] function, get a path containing indices by [method get_id_path], or one containing actual coordinates with [method get_point_path]. - It is also possible to use non-Euclidean distances. To do so, create a class that extends [AStar3D] and override methods [method _compute_cost] and [method _estimate_cost]. Both take two indices and return a length, as is shown in the following example. + It is also possible to use non-Euclidean distances. To do so, create a script that extends [AStar3D] and override the methods [method _compute_cost] and [method _estimate_cost]. Both should take two point IDs and return the distance between the corresponding points. + [b]Example:[/b] Use Manhattan distance instead of Euclidean distance: [codeblocks] [gdscript] - class MyAStar: - extends AStar3D + class_name MyAStar3D + extends AStar3D - func _compute_cost(u, v): - return abs(u - v) + func _compute_cost(u, v): + var u_pos = get_point_position(u) + var v_pos = get_point_position(v) + return abs(u_pos.x - v_pos.x) + abs(u_pos.y - v_pos.y) + abs(u_pos.z - v_pos.z) - func _estimate_cost(u, v): - return min(0, abs(u - v) - 1) + func _estimate_cost(u, v): + var u_pos = get_point_position(u) + var v_pos = get_point_position(v) + return abs(u_pos.x - v_pos.x) + abs(u_pos.y - v_pos.y) + abs(u_pos.z - v_pos.z) [/gdscript] [csharp] - public partial class MyAStar : AStar3D + using Godot; + + [GlobalClass] + public partial class MyAStar3D : AStar3D { public override float _ComputeCost(long fromId, long toId) { - return Mathf.Abs((int)(fromId - toId)); + Vector3 fromPoint = GetPointPosition(fromId); + Vector3 toPoint = GetPointPosition(toId); + + return Mathf.Abs(fromPoint.X - toPoint.X) + Mathf.Abs(fromPoint.Y - toPoint.Y) + Mathf.Abs(fromPoint.Z - toPoint.Z); } public override float _EstimateCost(long fromId, long toId) { - return Mathf.Min(0, Mathf.Abs((int)(fromId - toId)) - 1); + Vector3 fromPoint = GetPointPosition(fromId); + Vector3 toPoint = GetPointPosition(toId); + return Mathf.Abs(fromPoint.X - toPoint.X) + Mathf.Abs(fromPoint.Y - toPoint.Y) + Mathf.Abs(fromPoint.Z - toPoint.Z); } } [/csharp] diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml index 5677b3ff5c5b..f3e563097150 100644 --- a/doc/classes/CameraFeed.xml +++ b/doc/classes/CameraFeed.xml @@ -11,6 +11,18 @@ + + + + Called when the camera feed is activated. + + + + + + Called when the camera feed is deactivated. + + diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index e84143e47b0a..7cd1e3b9171a 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -199,7 +199,7 @@ Returns the OS theme base color (default control background). Returns [code]Color(0, 0, 0, 0)[/code] if the base color is unknown. - [b]Note:[/b] This method is implemented on macOS and Windows. + [b]Note:[/b] This method is implemented on macOS, Windows, and Android. @@ -1836,7 +1836,7 @@ Starts a drag operation on the window with the given [param window_id], using the current mouse position. Call this method when handling a mouse button being pressed to simulate a pressed event on the window's title bar. Using this method allows the window to participate in space switching, tiling, and other system features. - [b]Note:[/b] This method is implemented only on macOS. + [b]Note:[/b] This method is implemented on Linux(X11/Wayland), macOS, and Windows. diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index 41f7b02ca6c6..fa7935f78a18 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -97,6 +97,7 @@ Particle system starts as if it had already run for this many seconds. + [b]Note:[/b] This can be very expensive if set to a high number as it requires running the particle shader a number of times equal to the [member fixed_fps] (or 30, if [member fixed_fps] is 0) for every second. In extreme cases it can even lead to a GPU crash due to the volume of work done in a single frame. [Material] for processing particles. Can be a [ParticleProcessMaterial] or a [ShaderMaterial]. diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 89958ed21774..9cd0ea88ec13 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -128,6 +128,7 @@ Amount of time to preprocess the particles before animation starts. Lets you start the animation some time after particles have started emitting. + [b]Note:[/b] This can be very expensive if set to a high number as it requires running the particle shader a number of times equal to the [member fixed_fps] (or 30, if [member fixed_fps] is 0) for every second. In extreme cases it can even lead to a GPU crash due to the volume of work done in a single frame. [Material] for processing particles. Can be a [ParticleProcessMaterial] or a [ShaderMaterial]. diff --git a/doc/classes/Texture2D.xml b/doc/classes/Texture2D.xml index 7254a92e36d2..c153a796578d 100644 --- a/doc/classes/Texture2D.xml +++ b/doc/classes/Texture2D.xml @@ -124,7 +124,7 @@ Returns an [Image] that is a copy of data from this [Texture2D] (a new [Image] is created each time). [Image]s can be accessed and manipulated directly. [b]Note:[/b] This will return [code]null[/code] if this [Texture2D] is invalid. - [b]Note:[/b] This will fetch the texture data from the GPU, which might cause performance problems when overused. + [b]Note:[/b] This will fetch the texture data from the GPU, which might cause performance problems when overused. Avoid calling [method get_image] every frame, especially on large textures. diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index f6840d6b0952..89169c53ea10 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -8,11 +8,12 @@ To get a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport. [b]Note:[/b] A [ViewportTexture] is always local to its scene (see [member Resource.resource_local_to_scene]). If the scene root is not ready, it may return incorrect data (see [signal Node.ready]). [b]Note:[/b] Instantiating scenes containing a high-resolution [ViewportTexture] may cause noticeable stutter. - [b]Note:[/b] When using a [Viewport] with [member Viewport.use_hdr_2d] set to [code]true[/code] the returned texture will be an HDR image encoded in linear space. This may look darker than normal when displayed directly on screen. To convert to gamma space, you can do the following: + [b]Note:[/b] When using a [Viewport] with [member Viewport.use_hdr_2d] set to [code]true[/code], the returned texture will be an HDR image encoded in linear space. This may look darker than normal when displayed directly on screen. To convert to gamma space, you can do the following: [codeblock] img.convert(Image.FORMAT_RGBA8) imb.linear_to_srgb() [/codeblock] + [b]Note:[/b] Some nodes such as [Decal], [Light3D], and [PointLight2D] do not support using [ViewportTexture] directly. To use texture data from a [ViewportTexture] in these nodes, you need to create an [ImageTexture] by calling [method Texture2D.get_image] on the [ViewportTexture] and passing the result to [method ImageTexture.create_from_image]. This conversion is a slow operation, so it should not be performed every frame. https://godotengine.org/asset-library/asset/2807 diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 00fc3876a97e..cd5df56fbb92 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2292,6 +2292,19 @@ bool RenderingDeviceDriverD3D12::command_pool_reset(CommandPoolID p_cmd_pool) { void RenderingDeviceDriverD3D12::command_pool_free(CommandPoolID p_cmd_pool) { CommandPoolInfo *command_pool = (CommandPoolInfo *)(p_cmd_pool.id); + + // Destroy all command buffers associated with this command pool, mirroring Vulkan's behavior. + SelfList *cmd_buf_elem = command_pool->command_buffers.first(); + while (cmd_buf_elem != nullptr) { + CommandBufferInfo *cmd_buf_info = cmd_buf_elem->self(); + cmd_buf_elem = cmd_buf_elem->next(); + + cmd_buf_info->cmd_list.Reset(); + cmd_buf_info->cmd_allocator.Reset(); + + VersatileResource::free(resources_allocator, cmd_buf_info); + } + memdelete(command_pool); } @@ -2300,7 +2313,7 @@ void RenderingDeviceDriverD3D12::command_pool_free(CommandPoolID p_cmd_pool) { RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandPoolID p_cmd_pool) { DEV_ASSERT(p_cmd_pool); - const CommandPoolInfo *command_pool = (CommandPoolInfo *)(p_cmd_pool.id); + CommandPoolInfo *command_pool = (CommandPoolInfo *)(p_cmd_pool.id); D3D12_COMMAND_LIST_TYPE list_type; if (command_pool->buffer_type == COMMAND_BUFFER_TYPE_SECONDARY) { list_type = D3D12_COMMAND_LIST_TYPE_BUNDLE; @@ -2336,6 +2349,9 @@ RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandPo cmd_buf_info->cmd_allocator = cmd_allocator; cmd_buf_info->cmd_list = cmd_list; + // Add this command buffer to the command pool's list of command buffers. + command_pool->command_buffers.add(&cmd_buf_info->command_buffer_info_elem); + return CommandBufferID(cmd_buf_info); } diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 59c81db726bb..5bcef0664210 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -430,6 +430,9 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { struct CommandPoolInfo { CommandQueueFamilyID queue_family; CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY; + // Since there are no command pools in D3D12, we need to track the command buffers created by this pool + // so that we can free them when the pool is freed. + SelfList::List command_buffers; }; public: @@ -458,6 +461,9 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { // Leveraging knowledge of actual usage and D3D12 specifics (namely, command lists from the same allocator // can't be freely begun and ended), an allocator per list works better. struct CommandBufferInfo { + // Store a self list reference to be used by the command pool. + SelfList command_buffer_info_elem{ this }; + ComPtr cmd_allocator; ComPtr cmd_list; diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 5fd90744a428..5d7ce6418d2e 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1636,7 +1636,7 @@ void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { cl->shadow.enabled = p_enable; } -void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { +void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) { GLES3::Config *config = GLES3::Config::get_singleton(); CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index aa6171580fbe..cc84797bc35f 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -343,7 +343,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { RID light_create() override; void light_set_texture(RID p_rid, RID p_texture) override; void light_set_use_shadow(RID p_rid, bool p_enable) override; - void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override; + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override; void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override; void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override; diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 5a1a26e87452..d58dbced03b4 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#if defined(UNIX_ENABLED) +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) #include "ip_unix.h" diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index 7e496629ef4c..0d404e276cb0 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -31,7 +31,7 @@ #ifndef IP_UNIX_H #define IP_UNIX_H -#if defined(UNIX_ENABLED) +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) #include "core/io/ip.h" diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 1fc7816774cd..4a5c4dab6940 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -169,8 +169,8 @@ void OS_Unix::initialize_core() { #ifndef UNIX_SOCKET_UNAVAILABLE NetSocketUnix::make_default(); -#endif IPUnix::make_default(); +#endif process_map = memnew((HashMap)); _setup_clock(); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 348986b64e62..de7b123e4d1c 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1119,12 +1119,8 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_code_hint_draw_below(EDITOR_GET("text_editor/completion/put_callhint_tooltip_below_current_line")); code_complete_enabled = EDITOR_GET("text_editor/completion/code_complete_enabled"); code_complete_timer->set_wait_time(EDITOR_GET("text_editor/completion/code_complete_delay")); - bool first_time = idle_time == 0.0; idle_time = EDITOR_GET("text_editor/completion/idle_parse_delay"); idle_time_with_errors = EDITOR_GET("text_editor/completion/idle_parse_delay_with_errors_found"); - if (first_time) { - idle->set_wait_time(idle_time); - } // Appearance: Guidelines if (EDITOR_GET("text_editor/appearance/guidelines/show_line_length_guidelines")) { @@ -1604,6 +1600,11 @@ void CodeTextEditor::_error_pressed(const Ref &p_event) { void CodeTextEditor::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { + set_error_count(0); + set_warning_count(0); + } break; + case NOTIFICATION_THEME_CHANGED: { if (toggle_scripts_button->is_visible()) { update_toggle_scripts_button(); @@ -1845,7 +1846,6 @@ CodeTextEditor::CodeTextEditor() { error_button->set_default_cursor_shape(CURSOR_POINTING_HAND); error_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_error_button_pressed)); error_button->set_tooltip_text(TTR("Errors")); - set_error_count(0); // Warnings warning_button = memnew(Button); @@ -1855,7 +1855,6 @@ CodeTextEditor::CodeTextEditor() { warning_button->set_default_cursor_shape(CURSOR_POINTING_HAND); warning_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_warning_button_pressed)); warning_button->set_tooltip_text(TTR("Warnings")); - set_warning_count(0); status_bar->add_child(memnew(VSeparator)); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 897e897f2d4f..464b9d7373f0 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -578,9 +578,9 @@ String EditorResourcePicker::_get_resource_type(const Ref &p_resource) return res_type; } -static void _add_allowed_type(const StringName &p_type, HashSet *p_vector) { - if (p_vector->has(p_type)) { - // Already added +static void _add_allowed_type(const StringName &p_type, List *p_vector) { + if (p_vector->find(p_type)) { + // Already added. return; } @@ -588,7 +588,7 @@ static void _add_allowed_type(const StringName &p_type, HashSet *p_v // Engine class, if (!ClassDB::is_virtual(p_type)) { - p_vector->insert(p_type); + p_vector->push_back(p_type); } List inheriters; @@ -598,7 +598,7 @@ static void _add_allowed_type(const StringName &p_type, HashSet *p_v } } else { // Script class. - p_vector->insert(p_type); + p_vector->push_back(p_type); } List inheriters; @@ -613,12 +613,22 @@ void EditorResourcePicker::_ensure_allowed_types() const { return; } + List final_allowed; + Vector allowed_types = base_type.split(","); int size = allowed_types.size(); - for (int i = 0; i < size; i++) { - const String base = allowed_types[i].strip_edges(); - _add_allowed_type(base, &allowed_types_without_convert); + for (const String &S : allowed_types) { + const String base = S.strip_edges(); + if (base.begins_with("-")) { + final_allowed.erase(base.right(-1)); + continue; + } + _add_allowed_type(base, &final_allowed); + } + + for (const StringName &SN : final_allowed) { + allowed_types_without_convert.insert(SN); } allowed_types_with_convert = HashSet(allowed_types_without_convert); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index 515e6b44529f..63a98db70f08 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -188,12 +188,12 @@ void MeshInstance3DEditor::_create_collision_shape() { CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(shape); cshape->set_name("CollisionShape3D"); - cshape->set_transform(node->get_transform()); + cshape->set_transform(instance->get_transform()); ur->add_do_method(E, "add_sibling", cshape, true); ur->add_do_method(cshape, "set_owner", owner); ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape); ur->add_do_reference(cshape); - ur->add_undo_method(node->get_parent(), "remove_child", cshape); + ur->add_undo_method(instance->get_parent(), "remove_child", cshape); } } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 73c542d4e7e1..d85983a3198c 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -4266,8 +4266,14 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KeyModifierMask::ALT | Key::LEFT), WINDOW_PREV); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT); + file_menu->get_popup()->add_shortcut( + ED_SHORTCUT_ARRAY("script_editor/history_previous", TTR("History Previous"), + { int32_t(KeyModifierMask::ALT | Key::LEFT), int32_t(Key::BACK) }), + WINDOW_PREV); + file_menu->get_popup()->add_shortcut( + ED_SHORTCUT_ARRAY("script_editor/history_next", TTR("History Next"), + { int32_t(KeyModifierMask::ALT | Key::RIGHT), int32_t(Key::FORWARD) }), + WINDOW_NEXT); ED_SHORTCUT_OVERRIDE("script_editor/history_previous", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::LEFT); ED_SHORTCUT_OVERRIDE("script_editor/history_next", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::RIGHT); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index bd653e4eedaa..0dcc89f6c3df 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -1107,12 +1107,34 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { return p_target; } +void TextureRegionEditor::shortcut_input(const Ref &p_event) { + const Ref k = p_event; + if (k.is_valid() && k->is_pressed()) { + bool handled = false; + + if (ED_IS_SHORTCUT("ui_undo", p_event)) { + EditorNode::get_singleton()->undo(); + handled = true; + } + + if (ED_IS_SHORTCUT("ui_redo", p_event)) { + EditorNode::get_singleton()->redo(); + handled = true; + } + + if (handled) { + set_input_as_handled(); + } + } +} + void TextureRegionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_rect"), &TextureRegionEditor::_update_rect); } TextureRegionEditor::TextureRegionEditor() { set_title(TTR("Region Editor")); + set_process_shortcut_input(true); set_ok_button_text(TTR("Close")); // A power-of-two value works better as a default grid size. diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index d3f006536ed5..ae1f8381c600 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -141,6 +141,7 @@ class TextureRegionEditor : public AcceptDialog { protected: void _notification(int p_what); + virtual void shortcut_input(const Ref &p_event) override; static void _bind_methods(); void _texture_preview_draw(); diff --git a/editor/run_instances_dialog.cpp b/editor/run_instances_dialog.cpp index 94f6983f0b22..73e6ebdc1ad1 100644 --- a/editor/run_instances_dialog.cpp +++ b/editor/run_instances_dialog.cpp @@ -37,6 +37,7 @@ #include "scene/gui/grid_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" +#include "scene/gui/popup_menu.h" #include "scene/gui/separator.h" #include "scene/gui/spin_box.h" #include "scene/gui/tree.h" @@ -63,8 +64,8 @@ void RunInstancesDialog::_refresh_argument_count() { while (instance_count->get_value() > stored_data.size()) { stored_data.append(Dictionary()); } - stored_data.resize(instance_count->get_value()); - instances_data.resize(stored_data.size()); + + instances_data.resize(instance_count->get_value()); InstanceData *instances_write = instances_data.ptrw(); for (int i = 0; i < instances_data.size(); i++) { @@ -105,8 +106,6 @@ void RunInstancesDialog::_save_main_args() { } void RunInstancesDialog::_save_arguments() { - stored_data.resize(instances_data.size()); - for (int i = 0; i < instances_data.size(); i++) { const InstanceData &instance = instances_data[i]; Dictionary dict; @@ -117,6 +116,7 @@ void RunInstancesDialog::_save_arguments() { stored_data[i] = dict; } EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_instances_config", stored_data); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_instance_count", instance_count->get_value()); } Vector RunInstancesDialog::_split_cmdline_args(const String &p_arg_string) const { @@ -155,6 +155,45 @@ Vector RunInstancesDialog::_split_cmdline_args(const String &p_arg_strin return split_args; } +void RunInstancesDialog::_instance_menu_id_pressed(int p_option) { + switch (p_option) { + case CLEAR_ITEM: { + int item_to_clear = popup_menu->get_item_metadata(0); + if (item_to_clear >= 0 && item_to_clear < stored_data.size()) { + stored_data[item_to_clear] = Dictionary(); + } + } break; + case CLEAR_ALL: { + stored_data.clear(); + stored_data.resize(instance_count->get_value()); + } break; + } + + _start_instance_timer(); + _refresh_argument_count(); +} + +void RunInstancesDialog::_instance_tree_rmb(const Vector2 &p_pos, MouseButton p_button) { + if (p_button != MouseButton::RIGHT) { + return; + } + + popup_menu->clear(); + popup_menu->add_item(TTR("Clear"), CLEAR_ITEM); + + TreeItem *item = instance_tree->get_item_at_position(p_pos); + if (item) { + popup_menu->set_item_metadata(0, item->get_index()); + } else { + popup_menu->set_item_disabled(0, true); + } + + popup_menu->add_item(TTR("Clear All"), CLEAR_ALL); + popup_menu->set_position(instance_tree->get_screen_position() + p_pos); + popup_menu->reset_size(); + popup_menu->popup(); +} + void RunInstancesDialog::popup_dialog() { popup_centered(Vector2(1200, 600) * EDSCALE); } @@ -300,7 +339,8 @@ RunInstancesDialog::RunInstancesDialog() { instance_count = memnew(SpinBox); instance_count->set_min(1); instance_count->set_max(20); - instance_count->set_value(stored_data.size()); + instance_count->set_value(EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_instance_count", stored_data.size())); + args_gc->add_child(instance_count); instance_count->connect(SceneStringName(value_changed), callable_mp(this, &RunInstancesDialog::_start_instance_timer).unbind(1)); instance_count->connect(SceneStringName(value_changed), callable_mp(this, &RunInstancesDialog::_refresh_argument_count).unbind(1)); @@ -342,6 +382,14 @@ RunInstancesDialog::RunInstancesDialog() { instance_tree->set_column_expand(COLUMN_OVERRIDE_FEATURES, false); instance_tree->set_column_title(COLUMN_FEATURE_TAGS, TTR("Feature Tags")); instance_tree->set_hide_root(true); + instance_tree->set_allow_rmb_select(true); + + popup_menu = memnew(PopupMenu); + popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &RunInstancesDialog::_instance_menu_id_pressed)); + instance_tree->add_child(popup_menu); + + instance_tree->connect("item_mouse_selected", callable_mp(this, &RunInstancesDialog::_instance_tree_rmb)); + instance_tree->connect("empty_clicked", callable_mp(this, &RunInstancesDialog::_instance_tree_rmb)); main_vb->add_child(instance_tree); _refresh_argument_count(); diff --git a/editor/run_instances_dialog.h b/editor/run_instances_dialog.h index 8e0007dc8e7b..a7cca88bed3e 100644 --- a/editor/run_instances_dialog.h +++ b/editor/run_instances_dialog.h @@ -39,6 +39,7 @@ class SpinBox; class Timer; class Tree; class TreeItem; +class PopupMenu; class RunInstancesDialog : public AcceptDialog { GDCLASS(RunInstancesDialog, AcceptDialog); @@ -59,6 +60,12 @@ class RunInstancesDialog : public AcceptDialog { String get_feature_tags() const; }; + // Right-click popup menu. + enum { + CLEAR_ITEM, + CLEAR_ALL, + }; + inline static RunInstancesDialog *singleton = nullptr; TypedArray stored_data; @@ -72,6 +79,7 @@ class RunInstancesDialog : public AcceptDialog { SpinBox *instance_count = nullptr; CheckBox *enable_multiple_instances_checkbox = nullptr; Tree *instance_tree = nullptr; + PopupMenu *popup_menu = nullptr; void _fetch_main_args(); // These 2 methods are necessary due to callable_mp() not supporting default arguments. @@ -84,6 +92,8 @@ class RunInstancesDialog : public AcceptDialog { void _save_arguments(); // Separates command line arguments without splitting up quoted strings. Vector _split_cmdline_args(const String &p_arg_string) const; + void _instance_menu_id_pressed(int p_option); + void _instance_tree_rmb(const Vector2 &p_pos, MouseButton p_button); public: void popup_dialog(); diff --git a/methods.py b/methods.py index 0a75b94752b7..8f8095bbbb63 100644 --- a/methods.py +++ b/methods.py @@ -857,10 +857,8 @@ def using_emcc(env): def show_progress(env): - # Progress reporting is not available in non-TTY environments since it messes with the output - # (for example, when writing to a file). Ninja has its own progress/tracking tool that clashes - # with ours. - if not env["progress"] or not IS_TTY or env["ninja"]: + # Ninja has its own progress/tracking tool that clashes with ours. + if env["ninja"]: return NODE_COUNT_FILENAME = f"{base_folder_path}.scons_node_count" @@ -877,30 +875,33 @@ def __init__(self): if self.max == 0: print("NOTE: Performing initial build, progress percentage unavailable!") + # Progress reporting is not available in non-TTY environments since it + # messes with the output (for example, when writing to a file). + self.display = cast(bool, self.max and env["progress"] and IS_TTY) + def __call__(self, node, *args, **kw): self.count += 1 - if self.max != 0: + if self.display: percent = int(min(self.count * 100 / self.max, 100)) sys.stdout.write(f"\r[{percent:3d}%] ") sys.stdout.flush() from SCons.Script import Progress + from SCons.Script.Main import GetBuildFailures progressor = ShowProgress() Progress(progressor) - def progress_finish(target, source, env): + def progress_finish(): + if len(GetBuildFailures()): + return try: with open(NODE_COUNT_FILENAME, "w", encoding="utf-8", newline="\n") as f: f.write(f"{progressor.count}\n") except OSError: pass - env.AlwaysBuild( - env.CommandNoCache( - "progress_finish", [], env.Action(progress_finish, "Building node count database .scons_node_count") - ) - ) + atexit.register(progress_finish) def convert_size(size_bytes: int) -> str: diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index 5c6781dfc985..f347c95b2c6d 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/misc/extension_api_validation/4.3-stable.expected @@ -243,3 +243,21 @@ GH-97449 Validate extension JSON: Error: Field 'classes/GraphEdit/methods/connect_node/arguments': size changed value in new API, from 4 to 5. Added optional argument to connect_node to specify whether the connection should be automatically deleted if invalid. Compatibility method registered. + + +GH-88349 +-------- +Validate extension JSON: Error: Field 'classes/CSGMesh3D/properties/mesh': type changed value in new API, from "Mesh" to "Mesh,-PlaneMesh,-PointMesh,-QuadMesh,-RibbonTrailMesh". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_albedo': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_emission': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_normal': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_orm': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_albedo': type changed value in new API, from "Texture" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_emission': type changed value in new API, from "Texture" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_normal': type changed value in new API, from "Texture" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Decal/properties/texture_orm': type changed value in new API, from "Texture" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/Light3D/properties/light_projector': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". +Validate extension JSON: Error: Field 'classes/PointLight2D/properties/texture': type changed value in new API, from "Texture2D" to "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture". + +Property hints modified to disallow resource types that don't work. The types allowed are now more restricted, but this change only impacts the editor and not the actual exposed API. No adjustments should be necessary. +Decal properties were previously changed from Texture to Texture2D in 4.2, so we need to silence those warnings too. diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 753173913080..a908b1441251 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -446,6 +446,7 @@ CSGBrush *CSGShape3D::_get_brush() { node_aabb = aabb; brush = n; dirty = false; + update_configuration_warnings(); return brush; } @@ -1207,7 +1208,8 @@ void CSGMesh3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh3D::set_material); ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh3D::get_material); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + // Hide PrimitiveMeshes that are always non-manifold and therefore can't be used as CSG meshes. + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh,-PlaneMesh,-PointMesh,-QuadMesh,-RibbonTrailMesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); } diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml index 96d89ff48604..3dad3ef767ff 100644 --- a/modules/csg/doc_classes/CSGMesh3D.xml +++ b/modules/csg/doc_classes/CSGMesh3D.xml @@ -4,7 +4,7 @@ A CSG Mesh shape that uses a mesh resource. - This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes. + This CSG node allows you to use any mesh resource as a CSG shape, provided it is [i]manifold[/i]. A manifold shape is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes. [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. @@ -16,6 +16,7 @@ The [Mesh] resource to use as a CSG shape. + [b]Note:[/b] Some [Mesh] types such as [PlaneMesh], [PointMesh], [QuadMesh], and [RibbonTrailMesh] are excluded from the type hint for this property, as these primitives are non-[i]manifold[/i] and thus not compatible with the CSG algorithm. [b]Note:[/b] When using an [ArrayMesh], all vertex attributes except [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL] and [constant Mesh.ARRAY_TEX_UV] are left unused. Only [constant Mesh.ARRAY_VERTEX] and [constant Mesh.ARRAY_TEX_UV] will be passed to the GPU. [constant Mesh.ARRAY_NORMAL] is only used to determine which faces require the use of flat shading. By default, CSGMesh will ignore the mesh's vertex normals, recalculate them for each vertex and use a smooth shader. If a flat shader is required for a face, ensure that all vertex normals of the face are approximately equal. diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 29634a0a7502..df97821ece22 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -850,7 +850,7 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) { g.navigation_debug_edge_connections_instance = RID(); } if (g.navigation_debug_edge_connections_mesh.is_valid()) { - RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid()); + g.navigation_debug_edge_connections_mesh.unref(); } } #endif // DEBUG_ENABLED @@ -891,7 +891,7 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) { g.navigation_debug_edge_connections_instance = RID(); } if (g.navigation_debug_edge_connections_mesh.is_valid()) { - RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid()); + g.navigation_debug_edge_connections_mesh.unref(); } } #endif // DEBUG_ENABLED diff --git a/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp index 21fb76464b27..85cf08d4c921 100644 --- a/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp +++ b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp @@ -407,24 +407,28 @@ void JoltContactListener3D::_flush_contacts() { const JPH::SubShapeIDPair &shape_pair = E.key; Manifold &manifold = E.value; - const JPH::BodyID body_ids[2] = { shape_pair.GetBody1ID(), shape_pair.GetBody2ID() }; - const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2); + const JoltReadableBody3D jolt_body1 = space->read_body(shape_pair.GetBody1ID()); + const JoltReadableBody3D jolt_body2 = space->read_body(shape_pair.GetBody2ID()); - JoltBody3D *body1 = jolt_bodies[0].as_body(); + JoltBody3D *body1 = jolt_body1.as_body(); ERR_FAIL_NULL(body1); - JoltBody3D *body2 = jolt_bodies[1].as_body(); + JoltBody3D *body2 = jolt_body2.as_body(); ERR_FAIL_NULL(body2); const int shape_index1 = body1->find_shape_index(shape_pair.GetSubShapeID1()); const int shape_index2 = body2->find_shape_index(shape_pair.GetSubShapeID2()); - for (const Contact &contact : manifold.contacts1) { - body1->add_contact(body2, manifold.depth, shape_index1, shape_index2, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse); + if (jolt_body1->IsActive()) { + for (const Contact &contact : manifold.contacts1) { + body1->add_contact(body2, manifold.depth, shape_index1, shape_index2, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse); + } } - for (const Contact &contact : manifold.contacts2) { - body2->add_contact(body1, manifold.depth, shape_index2, shape_index1, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse); + if (jolt_body2->IsActive()) { + for (const Contact &contact : manifold.contacts2) { + body2->add_contact(body1, manifold.depth, shape_index2, shape_index1, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse); + } } manifold.contacts1.clear(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 6fd84d383498..9332d08ed42b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -323,7 +323,17 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long { if (platform == OS.Platforms.Android) { - if (IsSharedObject(Path.GetFileName(path))) + string fileName = Path.GetFileName(path); + + if (fileName.EndsWith(".jar")) + { + // We exclude jar files from the export since they should + // already be included in the Godot templates, adding them + // again would cause conflicts. + return; + } + + if (IsSharedObject(fileName)) { AddSharedObject(path, tags: new string[] { arch }, Path.Join(projectDataDirName, @@ -336,7 +346,7 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long static bool IsSharedObject(string fileName) { if (fileName.EndsWith(".so") || fileName.EndsWith(".a") - || fileName.EndsWith(".jar") || fileName.EndsWith(".dex")) + || fileName.EndsWith(".dex")) { return true; } diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj index ee607ff279e2..d291199ad04d 100644 --- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj +++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj @@ -8,7 +8,7 @@ - + diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 38f6931c8a86..ee666bdce963 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -209,6 +209,12 @@ Color DisplayServerAndroid::get_accent_color() const { return godot_java->get_accent_color(); } +Color DisplayServerAndroid::get_base_color() const { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_NULL_V(godot_java, Color(0, 0, 0, 0)); + return godot_java->get_base_color(); +} + TypedArray DisplayServerAndroid::get_display_cutouts() const { GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java(); ERR_FAIL_NULL_V(godot_io_java, Array()); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 1744ad306907..b26e963798c0 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -126,6 +126,7 @@ class DisplayServerAndroid : public DisplayServer { void emit_file_picker_callback(bool p_ok, const Vector &p_selected_paths); virtual Color get_accent_color() const override; + virtual Color get_base_color() const override; virtual TypedArray get_display_cutouts() const override; virtual Rect2i get_display_safe_area() const override; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index cc0f75802b5f..9d2bc917044e 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1979,7 +1979,7 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor } if (p_option == "custom_template/debug" || p_option == "custom_template/release") { // The APK templates are ignored if Gradle build is enabled. - return !bool(p_preset->get("gradle_build/use_gradle_build")); + return advanced_options_enabled && !bool(p_preset->get("gradle_build/use_gradle_build")); } // Hide .NET embedding option (always enabled). diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 308f126d5d15..661e350dae20 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -70,10 +70,7 @@ dependencies { } // .NET dependencies - String jar = '../../../../modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar' - if (file(jar).exists()) { - monoImplementation files(jar) - } + monoImplementation fileTree(dir: 'monoLibs', include: ['*.jar']) } android { diff --git a/modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar b/platform/android/java/app/monoLibs/libSystem.Security.Cryptography.Native.Android.jar similarity index 100% rename from modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar rename to platform/android/java/app/monoLibs/libSystem.Security.Cryptography.Native.Android.jar diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 38df7ada3ef6..dd05559f59a3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -933,6 +933,13 @@ class Godot(private val context: Context) { return value.data } + @Keep + private fun getBaseColor(): Int { + val value = TypedValue() + context.theme.resolveAttribute(android.R.attr.colorBackground, value, true) + return value.data + } + /** * Destroys the Godot Engine and kill the process it's running in. */ @@ -970,15 +977,10 @@ class Godot(private val context: Context) { } fun onBackPressed() { - var shouldQuit = true for (plugin in pluginRegistry.allPlugins) { - if (plugin.onMainBackPressed()) { - shouldQuit = false - } - } - if (shouldQuit) { - renderView?.queueOnRenderThread { GodotLib.back() } + plugin.onMainBackPressed() } + renderView?.queueOnRenderThread { GodotLib.back() } } /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index fb41cd00c025..36bee6ef2241 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -154,10 +154,6 @@ public void onPointerCaptureChange(boolean hasCapture) { } public boolean onKeyUp(final int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - return true; - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { return false; } @@ -183,13 +179,6 @@ public boolean onKeyUp(final int keyCode, KeyEvent event) { } public boolean onKeyDown(final int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - godot.onBackPressed(); - // press 'back' button should not terminate program - //normal handle 'back' event in game logic - return true; - } - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { return false; } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 5ecd789d43b6..d96ca5710378 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -65,6 +65,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _is_dark_mode_supported = p_env->GetMethodID(godot_class, "isDarkModeSupported", "()Z"); _is_dark_mode = p_env->GetMethodID(godot_class, "isDarkMode", "()Z"); _get_accent_color = p_env->GetMethodID(godot_class, "getAccentColor", "()I"); + _get_base_color = p_env->GetMethodID(godot_class, "getBaseColor", "()I"); _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"); @@ -215,18 +216,32 @@ bool GodotJavaWrapper::is_dark_mode() { } } +// Convert ARGB to RGBA. +static Color _argb_to_rgba(int p_color) { + int alpha = (p_color >> 24) & 0xFF; + int red = (p_color >> 16) & 0xFF; + int green = (p_color >> 8) & 0xFF; + int blue = p_color & 0xFF; + return Color(red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f); +} + Color GodotJavaWrapper::get_accent_color() { if (_get_accent_color) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, Color(0, 0, 0, 0)); int accent_color = env->CallIntMethod(godot_instance, _get_accent_color); + return _argb_to_rgba(accent_color); + } else { + return Color(0, 0, 0, 0); + } +} - // Convert ARGB to RGBA. - int alpha = (accent_color >> 24) & 0xFF; - int red = (accent_color >> 16) & 0xFF; - int green = (accent_color >> 8) & 0xFF; - int blue = accent_color & 0xFF; - return Color(red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f); +Color GodotJavaWrapper::get_base_color() { + if (_get_base_color) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, Color(0, 0, 0, 0)); + int base_color = env->CallIntMethod(godot_instance, _get_base_color); + return _argb_to_rgba(base_color); } else { return Color(0, 0, 0, 0); } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 512779169a4d..2b2613fd2178 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -57,6 +57,7 @@ class GodotJavaWrapper { jmethodID _is_dark_mode_supported = nullptr; jmethodID _is_dark_mode = nullptr; jmethodID _get_accent_color = nullptr; + jmethodID _get_base_color = nullptr; jmethodID _get_clipboard = nullptr; jmethodID _set_clipboard = nullptr; jmethodID _has_clipboard = nullptr; @@ -102,6 +103,7 @@ class GodotJavaWrapper { bool is_dark_mode_supported(); bool is_dark_mode(); Color get_accent_color(); + Color get_base_color(); bool has_get_clipboard(); String get_clipboard(); bool has_set_clipboard(); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 667c883e386a..ad002ae8a621 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -254,7 +254,15 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre } bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); - if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing" || (p_option.begins_with("icons/") && !p_option.begins_with("icons/icon") && !p_option.begins_with("icons/app_store"))) { + if (p_option.begins_with("privacy") || + p_option == "application/generate_simulator_library_if_missing" || + (p_option.begins_with("icons/") && !p_option.begins_with("icons/icon") && !p_option.begins_with("icons/app_store")) || + p_option == "custom_template/debug" || + p_option == "custom_template/release" || + p_option == "application/additional_plist_content" || + p_option == "application/delete_old_export_files_unconditionally" || + p_option == "application/icon_interpolation" || + p_option == "application/signature") { return advanced_options_enabled; } diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 9362aee5e4cf..2803f7849e7a 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -171,7 +171,9 @@ bool EditorExportPlatformLinuxBSD::get_export_option_visibility(const EditorExpo if (!ssh && p_option != "ssh_remote_deploy/enabled" && p_option.begins_with("ssh_remote_deploy/")) { return false; } - if (p_option == "dotnet/embed_build_outputs") { + if (p_option == "dotnet/embed_build_outputs" || + p_option == "custom_template/debug" || + p_option == "custom_template/release") { return advanced_options_enabled; } return true; diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index e321e4d4b94e..bae1cfd444e4 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -986,6 +986,12 @@ DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServ return DisplayServer::VSYNC_ENABLED; } +void DisplayServerWayland::window_start_drag(WindowID p_window) { + MutexLock mutex_lock(wayland_thread.mutex); + + wayland_thread.window_start_drag(p_window); +} + void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index d6f51b4a7e95..5046097d7cec 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -272,6 +272,8 @@ class DisplayServerWayland : public DisplayServer { virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window_id = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_window_id) const override; + virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override; + virtual void cursor_set_shape(CursorShape p_shape) override; virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override; diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index 1130472e9472..11a8c6adad80 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -3338,6 +3338,22 @@ void WaylandThread::beep() const { } } +void WaylandThread::window_start_drag(DisplayServer::WindowID p_window_id) { + // TODO: Use window IDs for multiwindow support. + WindowState &ws = main_window; + SeatState *ss = wl_seat_get_seat_state(wl_seat_current); + + if (ss && ws.xdg_toplevel) { + xdg_toplevel_move(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial); + } + +#ifdef LIBDECOR_ENABLED + if (ws.libdecor_frame) { + libdecor_frame_move(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial); + } +#endif +} + void WaylandThread::window_set_max_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) { // TODO: Use window IDs for multiwindow support. WindowState &ws = main_window; diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index 8f1605b82027..1996e0e359db 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -971,6 +971,8 @@ class WaylandThread { // Optional - requires xdg_activation_v1 void window_request_attention(DisplayServer::WindowID p_window_id); + void window_start_drag(DisplayServer::WindowID p_window_id); + // Optional - require idle_inhibit_unstable_v1 void window_set_idle_inhibition(DisplayServer::WindowID p_window_id, bool p_enable); bool window_get_idle_inhibition(DisplayServer::WindowID p_window_id) const; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index be66ea30ae38..f8716c6f34cc 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -69,6 +69,8 @@ #define _NET_WM_STATE_REMOVE 0L // remove/unset property #define _NET_WM_STATE_ADD 1L // add/set property +#define _NET_WM_MOVERESIZE_MOVE 8L + // 2.2 is the first release with multitouch #define XINPUT_CLIENT_VERSION_MAJOR 2 #define XINPUT_CLIENT_VERSION_MINOR 2 @@ -5422,6 +5424,40 @@ DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_wind return DisplayServer::VSYNC_ENABLED; } +void DisplayServerX11::window_start_drag(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + + XUngrabPointer(x11_display, CurrentTime); + + Window root_return, child_return; + int root_x, root_y, win_x, win_y; + unsigned int mask_return; + + Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y, &win_x, &win_y, &mask_return); + + m.type = ClientMessage; + m.window = wd.x11_window; + m.message_type = XInternAtom(x11_display, "_NET_WM_MOVERESIZE", True); + m.format = 32; + if (xquerypointer_result) { + m.data.l[0] = root_x; + m.data.l[1] = root_y; + m.data.l[3] = Button1; + } + m.data.l[2] = _NET_WM_MOVERESIZE_MOVE; + m.data.l[4] = 1; // Source - normal application. + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&m); + + XSync(x11_display, 0); +} + Vector DisplayServerX11::get_rendering_drivers_func() { Vector drivers; diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 31e4d548b9ab..cbb29182d668 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -512,6 +512,8 @@ class DisplayServerX11 : public DisplayServer { virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; + virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override; + virtual void cursor_set_shape(CursorShape p_shape) override; virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override; diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index d795adbe33b2..7c7ddd8ab54f 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -332,7 +332,21 @@ bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportP } bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); - if (p_option.begins_with("privacy") || p_option == "codesign/entitlements/additional") { + if (p_option.begins_with("privacy") || + p_option == "codesign/entitlements/additional" || + p_option == "custom_template/debug" || + p_option == "custom_template/release" || + p_option == "application/additional_plist_content" || + p_option == "application/export_angle" || + p_option == "application/icon_interpolation" || + p_option == "application/signature" || + p_option == "display/high_res" || + p_option == "xcode/platform_build" || + p_option == "xcode/sdk_build" || + p_option == "xcode/sdk_name" || + p_option == "xcode/sdk_version" || + p_option == "xcode/xcode_build" || + p_option == "xcode/xcode_version") { return advanced_options_enabled; } } diff --git a/platform/web/SCsub b/platform/web/SCsub index 7ba12eecb96b..be443b44399e 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -27,6 +27,8 @@ web_files = [ "http_client_web.cpp", "javascript_bridge_singleton.cpp", "web_main.cpp", + "ip_web.cpp", + "net_socket_web.cpp", "os_web.cpp", ] diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 9d212d81ffe9..142a8deb86d7 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -372,6 +372,16 @@ void EditorExportPlatformWeb::get_export_options(List *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); } +bool EditorExportPlatformWeb::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const { + bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); + if (p_option == "custom_template/debug" || + p_option == "custom_template/release") { + return advanced_options_enabled; + } + + return true; +} + String EditorExportPlatformWeb::get_name() const { return "Web"; } diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 3c743e2e74a2..bd449bb16ded 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -112,6 +112,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { virtual void get_preset_features(const Ref &p_preset, List *r_features) const override; virtual void get_export_options(List *r_options) const override; + virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override; virtual String get_name() const override; virtual String get_os_name() const override; diff --git a/platform/web/ip_web.cpp b/platform/web/ip_web.cpp new file mode 100644 index 000000000000..3d1fc5618fcc --- /dev/null +++ b/platform/web/ip_web.cpp @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* ip_web.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 "ip_web.h" + +void IPWeb::_resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type) const { +} + +void IPWeb::get_local_interfaces(HashMap *r_interfaces) const { +} + +void IPWeb::make_default() { + _create = _create_web; +} + +IP *IPWeb::_create_web() { + return memnew(IPWeb); +} + +IPWeb::IPWeb() { +} diff --git a/platform/web/ip_web.h b/platform/web/ip_web.h new file mode 100644 index 000000000000..2f231cc6cc3e --- /dev/null +++ b/platform/web/ip_web.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* ip_web.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 IP_WEB_H +#define IP_WEB_H + +#include "core/io/ip.h" + +class IPWeb : public IP { + GDCLASS(IPWeb, IP); + + virtual void _resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override; + +private: + static IP *_create_web(); + +public: + virtual void get_local_interfaces(HashMap *r_interfaces) const override; + + static void make_default(); + IPWeb(); +}; + +#endif // IP_WEB_H diff --git a/platform/web/net_socket_web.cpp b/platform/web/net_socket_web.cpp new file mode 100644 index 000000000000..db3bac065bda --- /dev/null +++ b/platform/web/net_socket_web.cpp @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* net_socket_web.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 "net_socket_web.h" + +NetSocket *NetSocketWeb::_create_func() { + return memnew(NetSocketWeb); +} + +void NetSocketWeb::make_default() { + _create = _create_func; +} diff --git a/platform/web/net_socket_web.h b/platform/web/net_socket_web.h new file mode 100644 index 000000000000..8a785cf69539 --- /dev/null +++ b/platform/web/net_socket_web.h @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* net_socket_web.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 NET_SOCKET_WEB_H +#define NET_SOCKET_WEB_H + +#include "core/io/net_socket.h" + +#include + +class NetSocketWeb : public NetSocket { +protected: + static NetSocket *_create_func(); + +public: + static void make_default(); + + virtual Error open(Type p_sock_type, IP::Type &ip_type) override { return ERR_UNAVAILABLE; } + virtual void close() override {} + virtual Error bind(IPAddress p_addr, uint16_t p_port) override { return ERR_UNAVAILABLE; } + virtual Error listen(int p_max_pending) override { return ERR_UNAVAILABLE; } + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override { return ERR_UNAVAILABLE; } + virtual Error poll(PollType p_type, int timeout) const override { return ERR_UNAVAILABLE; } + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override { return ERR_UNAVAILABLE; } + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override { return ERR_UNAVAILABLE; } + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override { return ERR_UNAVAILABLE; } + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override { return ERR_UNAVAILABLE; } + virtual Ref accept(IPAddress &r_ip, uint16_t &r_port) override { return Ref(); } + + virtual bool is_open() const override { return false; } + virtual int get_available_bytes() const override { return -1; } + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override { return ERR_UNAVAILABLE; } + + virtual Error set_broadcasting_enabled(bool p_enabled) override { return ERR_UNAVAILABLE; } + virtual void set_blocking_enabled(bool p_enabled) override {} + virtual void set_ipv6_only_enabled(bool p_enabled) override {} + virtual void set_tcp_no_delay_enabled(bool p_enabled) override {} + virtual void set_reuse_address_enabled(bool p_enabled) override {} + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override { return ERR_UNAVAILABLE; } + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override { return ERR_UNAVAILABLE; } + + NetSocketWeb() {} +}; + +#endif // NET_SOCKET_WEB_H diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index c7ca613f4e6b..231f153ea5f9 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -33,6 +33,8 @@ #include "api/javascript_bridge_singleton.h" #include "display_server_web.h" #include "godot_js.h" +#include "ip_web.h" +#include "net_socket_web.h" #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" @@ -53,6 +55,8 @@ void OS_Web::alert(const String &p_alert, const String &p_title) { // Lifecycle void OS_Web::initialize() { OS_Unix::initialize_core(); + IPWeb::make_default(); + NetSocketWeb::make_default(); DisplayServerWeb::register_web_driver(); } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 2fa0ad8effd9..60b72cdbce02 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3803,6 +3803,21 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_ return DisplayServer::VSYNC_ENABLED; } +void DisplayServerWindows::window_start_drag(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + ReleaseCapture(); + + POINT coords; + GetCursorPos(&coords); + ScreenToClient(wd.hWnd, &coords); + + SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(coords.x, coords.y)); +} + void DisplayServerWindows::set_context(Context p_context) { } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 2ee6bd2e7302..579a23eb0e13 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -805,6 +805,8 @@ class DisplayServerWindows : public DisplayServer { virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; + virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override; + virtual void cursor_set_shape(CursorShape p_shape) override; virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index e4dbb4be8f81..162a2a1e2817 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -398,7 +398,13 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor return false; } - if (p_option == "dotnet/embed_build_outputs") { + if (p_option == "dotnet/embed_build_outputs" || + p_option == "custom_template/debug" || + p_option == "custom_template/release" || + p_option == "application/d3d12_agility_sdk_multiarch" || + p_option == "application/export_angle" || + p_option == "application/export_d3d12" || + p_option == "application/icon_interpolation") { return advanced_options_enabled; } return true; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 35ddc7535b1b..6eeb2ba0615f 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1294,7 +1294,7 @@ void CPUParticles2D::_bind_methods() { ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,suffix:s"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 87777db81bec..f673b2a78f7c 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -830,7 +830,7 @@ void GPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.001"), "set_interp_to_end", "get_interp_to_end"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 0b54d1e60462..4438e2fbe5a0 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -395,6 +395,19 @@ Rect2 PointLight2D::get_anchorable_rect() const { void PointLight2D::set_texture(const Ref &p_texture) { texture = p_texture; if (texture.is_valid()) { +#ifdef DEBUG_ENABLED + if ( + p_texture->is_class("AnimatedTexture") || + p_texture->is_class("AtlasTexture") || + p_texture->is_class("CameraTexture") || + p_texture->is_class("CanvasTexture") || + p_texture->is_class("MeshTexture") || + p_texture->is_class("Texture2DRD") || + p_texture->is_class("ViewportTexture")) { + WARN_PRINT(vformat("%s cannot be used as a PointLight2D texture (%s). As a workaround, assign the value returned by %s's `get_image()` instead.", p_texture->get_class(), get_path(), p_texture->get_class())); + } +#endif + RS::get_singleton()->canvas_light_set_texture(_get_light(), texture->get_rid()); } else { RS::get_singleton()->canvas_light_set_texture(_get_light(), RID()); @@ -462,7 +475,8 @@ void PointLight2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &PointLight2D::set_texture_scale); ClassDB::bind_method(D_METHOD("get_texture_scale"), &PointLight2D::get_texture_scale); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); + // Only allow texture types that display correctly. + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1024,1,or_greater,suffix:px"), "set_height", "get_height"); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 37e9e5824059..87a0970a6369 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1490,7 +1490,7 @@ void CPUParticles3D::_bind_methods() { ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 41403a269c5d..df987a5ed020 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -44,6 +44,20 @@ void Decal::set_texture(DecalTexture p_type, const Ref &p_texture) { ERR_FAIL_INDEX(p_type, TEXTURE_MAX); textures[p_type] = p_texture; RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + +#ifdef DEBUG_ENABLED + if ( + p_texture->is_class("AnimatedTexture") || + p_texture->is_class("AtlasTexture") || + p_texture->is_class("CameraTexture") || + p_texture->is_class("CanvasTexture") || + p_texture->is_class("MeshTexture") || + p_texture->is_class("Texture2DRD") || + p_texture->is_class("ViewportTexture")) { + WARN_PRINT(vformat("%s cannot be used as a Decal texture (%s). As a workaround, assign the value returned by %s's `get_image()` instead.", p_texture->get_class(), get_path(), p_texture->get_class())); + } +#endif + RS::get_singleton()->decal_set_texture(decal, RS::DecalTexture(p_type), texture_rid); update_configuration_warnings(); } @@ -225,10 +239,12 @@ void Decal::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_size", "get_size"); ADD_GROUP("Textures", "texture_"); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ALBEDO); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_NORMAL); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ORM); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION); + // Only allow texture types that display correctly. + const String texture_hint = "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture"; + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, texture_hint), "set_texture", "get_texture", TEXTURE_ALBEDO); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, texture_hint), "set_texture", "get_texture", TEXTURE_NORMAL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, texture_hint), "set_texture", "get_texture", TEXTURE_ORM); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, texture_hint), "set_texture", "get_texture", TEXTURE_EMISSION); ADD_GROUP("Parameters", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy"); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 18f51fe7351c..e428729c2e37 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -757,7 +757,7 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.01"), "set_interp_to_end", "get_interp_to_end"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index a8b4dfbf6b5e..d1eca0f405aa 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "core/config/project_settings.h" - #include "light_3d.h" +#include "core/config/project_settings.h" + void Light3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); param[p_param] = p_value; @@ -200,6 +200,20 @@ Light3D::BakeMode Light3D::get_bake_mode() const { void Light3D::set_projector(const Ref &p_texture) { projector = p_texture; RID tex_id = projector.is_valid() ? projector->get_rid() : RID(); + +#ifdef DEBUG_ENABLED + if ( + p_texture->is_class("AnimatedTexture") || + p_texture->is_class("AtlasTexture") || + p_texture->is_class("CameraTexture") || + p_texture->is_class("CanvasTexture") || + p_texture->is_class("MeshTexture") || + p_texture->is_class("Texture2DRD") || + p_texture->is_class("ViewportTexture")) { + WARN_PRINT(vformat("%s cannot be used as a Light3D projector texture (%s). As a workaround, assign the value returned by %s's `get_image()` instead.", p_texture->get_class(), get_path(), p_texture->get_class())); + } +#endif + RS::get_singleton()->light_set_projector(light, tex_id); update_configuration_warnings(); } @@ -384,7 +398,8 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_volumetric_fog_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_VOLUMETRIC_FOG_ENERGY); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); + // Only allow texture types that display correctly. + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture"), "set_projector", "get_projector"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index ff19eb8c12e5..03e6c4cff51e 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -374,21 +374,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM } if (new_closest != cur_closest && new_closest != -1) { - NodeTimeInfo from; if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { - //for ping-pong loop + NodeTimeInfo from; + // For ping-pong loop. Ref na_c = static_cast>(blend_points[cur_closest].node); Ref na_n = static_cast>(blend_points[new_closest].node); if (!na_c.is_null() && !na_n.is_null()) { na_n->set_backward(na_c->is_backward()); } - //see how much animation remains + // See how much animation remains. pi.seeked = false; pi.weight = 0; - from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); + from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); + pi.time = from.position; } - - pi.time = from.position; pi.seeked = true; pi.weight = 1.0; mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 0a6546f7da64..7863902029b0 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -551,21 +551,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM } if (new_closest != cur_closest && new_closest != -1) { - NodeTimeInfo from; if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { - //for ping-pong loop + NodeTimeInfo from; + // For ping-pong loop. Ref na_c = static_cast>(blend_points[cur_closest].node); Ref na_n = static_cast>(blend_points[new_closest].node); if (!na_c.is_null() && !na_n.is_null()) { na_n->set_backward(na_c->is_backward()); } - //see how much animation remains + // See how much animation remains. pi.seeked = false; pi.weight = 0; - from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); + from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); + pi.time = from.position; } - - pi.time = from.position; pi.seeked = true; pi.weight = 1.0; mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index b2f847a1480e..36cbaaf814d4 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -44,6 +44,18 @@ Vector (*AnimationNodeAnimation::get_editable_animation_list)() = nullpt void AnimationNodeAnimation::get_parameter_list(List *r_list) const { AnimationNode::get_parameter_list(r_list); + r_list->push_back(PropertyInfo(Variant::BOOL, backward, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); +} + +Variant AnimationNodeAnimation::get_parameter_default_value(const StringName &p_parameter) const { + Variant ret = AnimationNode::get_parameter_default_value(p_parameter); + if (ret != Variant()) { + return ret; + } + if (p_parameter == backward) { + return false; + } + return 0; } AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const { @@ -97,7 +109,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer pi.delta = get_node_time_info().position - p_playback_info.time; } } else { - pi.time = get_node_time_info().position + (backward ? -p_playback_info.delta : p_playback_info.delta); + pi.time = get_node_time_info().position + (get_parameter(backward) ? -p_playback_info.delta : p_playback_info.delta); } NodeTimeInfo nti = _process(pi, p_test_only); @@ -130,6 +142,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe double cur_len = cur_nti.length; double cur_time = p_playback_info.time; double cur_delta = p_playback_info.delta; + bool cur_backward = get_parameter(backward); Animation::LoopMode cur_loop_mode = cur_nti.loop_mode; double prev_time = cur_nti.position; @@ -153,13 +166,13 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe if (!Math::is_zero_approx(cur_len)) { cur_time = Math::fposmod(cur_time, cur_len); } - backward = false; + cur_backward = false; } else { if (!Math::is_zero_approx(cur_len)) { if (Animation::is_greater_or_equal_approx(prev_time, 0) && Animation::is_less_approx(cur_time, 0)) { - backward = !backward; + cur_backward = !cur_backward; } else if (Animation::is_less_or_equal_approx(prev_time, cur_len) && Animation::is_greater_approx(cur_time, cur_len)) { - backward = !backward; + cur_backward = !cur_backward; } cur_time = Math::pingpong(cur_time, cur_len); } @@ -172,7 +185,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe cur_delta += cur_time - cur_len; cur_time = cur_len; } - backward = false; + cur_backward = false; // If ended, don't progress AnimationNode. So set delta to 0. if (!Math::is_zero_approx(cur_delta)) { if (play_mode == PLAY_MODE_FORWARD) { @@ -267,6 +280,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe blend_animation(animation, pi); } + set_parameter(backward, cur_backward); + return nti; } @@ -283,11 +298,11 @@ AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const { } void AnimationNodeAnimation::set_backward(bool p_backward) { - backward = p_backward; + set_parameter(backward, p_backward); } bool AnimationNodeAnimation::is_backward() const { - return backward; + return get_parameter(backward); } void AnimationNodeAnimation::set_advance_on_start(bool p_advance_on_start) { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index c48d799eea6f..10217e3deb00 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -36,6 +36,8 @@ class AnimationNodeAnimation : public AnimationRootNode { GDCLASS(AnimationNodeAnimation, AnimationRootNode); + StringName backward = "backward"; // Only used by pingpong animation. + StringName animation; bool advance_on_start = false; @@ -56,6 +58,7 @@ class AnimationNodeAnimation : public AnimationRootNode { }; void get_parameter_list(List *r_list) const override; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual NodeTimeInfo get_node_time_info() const override; // Wrapper of get_parameter(). @@ -100,7 +103,6 @@ class AnimationNodeAnimation : public AnimationRootNode { private: PlayMode play_mode = PLAY_MODE_FORWARD; - bool backward = false; // Only used by pingpong animation. }; VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode) diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index 15e3f0f806b5..0043e076e42b 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -587,7 +587,7 @@ void AudioStreamWAV::set_data(const Vector &p_data) { Vector AudioStreamWAV::get_data() const { Vector pv; - if (!data.is_empty()) { + if (data_bytes) { pv.resize(data_bytes); memcpy(pv.ptrw(), data.ptr() + DATA_PAD, data_bytes); } diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp index e93ab3a54425..18d898785c58 100644 --- a/servers/camera/camera_feed.cpp +++ b/servers/camera/camera_feed.cpp @@ -58,6 +58,9 @@ void CameraFeed::_bind_methods() { ClassDB::bind_method(D_METHOD("get_formats"), &CameraFeed::get_formats); ClassDB::bind_method(D_METHOD("set_format", "index", "parameters"), &CameraFeed::set_format); + GDVIRTUAL_BIND(_activate_feed); + GDVIRTUAL_BIND(_deactivate_feed); + ADD_SIGNAL(MethodInfo("frame_changed")); ADD_SIGNAL(MethodInfo("format_changed")); @@ -273,12 +276,13 @@ void CameraFeed::set_external(int p_width, int p_height) { } bool CameraFeed::activate_feed() { - // nothing to do here - return true; + bool ret = true; + GDVIRTUAL_CALL(_activate_feed, ret); + return ret; } void CameraFeed::deactivate_feed() { - // nothing to do here + GDVIRTUAL_CALL(_deactivate_feed); } bool CameraFeed::set_format(int p_index, const Dictionary &p_parameters) { diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h index 365ed7c6357b..2518c333f35d 100644 --- a/servers/camera/camera_feed.h +++ b/servers/camera/camera_feed.h @@ -123,6 +123,9 @@ class CameraFeed : public RefCounted { virtual bool activate_feed(); virtual void deactivate_feed(); + + GDVIRTUAL0R(bool, _activate_feed) + GDVIRTUAL0(_deactivate_feed) }; VARIANT_ENUM_CAST(CameraFeed::FeedDataType); diff --git a/servers/rendering/dummy/rasterizer_canvas_dummy.h b/servers/rendering/dummy/rasterizer_canvas_dummy.h index d61ee1bdb6e3..93f517828a9a 100644 --- a/servers/rendering/dummy/rasterizer_canvas_dummy.h +++ b/servers/rendering/dummy/rasterizer_canvas_dummy.h @@ -43,7 +43,7 @@ class RasterizerCanvasDummy : public RendererCanvasRender { RID light_create() override { return RID(); } void light_set_texture(RID p_rid, RID p_texture) override {} void light_set_use_shadow(RID p_rid, bool p_enable) override {} - void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override {} + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override {} void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override {} void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override {} diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index 328fe32ea6c4..e91eb7683434 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -531,7 +531,7 @@ class RendererCanvasRender { virtual RID light_create() = 0; virtual void light_set_texture(RID p_rid, RID p_texture) = 0; virtual void light_set_use_shadow(RID p_rid, bool p_enable) = 0; - virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0; + virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) = 0; virtual void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) = 0; virtual void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) = 0; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 85b0a0763b87..8940c1d110c1 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -985,7 +985,34 @@ void RendererCanvasRenderRD::_update_shadow_atlas() { } } -void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { +void RendererCanvasRenderRD::_update_occluder_buffer(uint32_t p_size) { + bool needs_update = state.shadow_occluder_buffer.is_null(); + + if (p_size > state.shadow_occluder_buffer_size) { + needs_update = true; + state.shadow_occluder_buffer_size = next_power_of_2(p_size); + if (state.shadow_occluder_buffer.is_valid()) { + RD::get_singleton()->free(state.shadow_occluder_buffer); + } + } + + if (needs_update) { + state.shadow_occluder_buffer = RD::get_singleton()->storage_buffer_create(state.shadow_occluder_buffer_size); + + Vector uniforms; + + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.append_id(state.shadow_occluder_buffer); + uniforms.push_back(u); + } + state.shadow_ocluder_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_POSITIONAL_SHADOW), 0); + } +} + +void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) { CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); ERR_FAIL_COND(!cl->shadow.enabled); @@ -996,75 +1023,97 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, Vector cc; cc.push_back(Color(p_far, p_far, p_far, 1.0)); - Projection projection; - { - real_t fov = 90; - real_t nearp = p_near; - real_t farp = p_far; - real_t aspect = 1.0; + // First, do a culling pass and record what occluders need to be drawn for this light. + static thread_local LocalVector occluders; + static thread_local LocalVector occluder_indices; + occluders.clear(); + occluder_indices.clear(); + + uint32_t occluder_count = 0; + + LightOccluderInstance *instance = p_occluders; + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + + if (!co || co->index_array.is_null()) { + instance = instance->next; + continue; + } + + occluder_count++; - real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); - real_t ymin = -ymax; - real_t xmin = ymin * aspect; - real_t xmax = ymax * aspect; + if (!(p_light_mask & instance->light_mask) || !p_light_rect.intersects(instance->aabb_cache)) { + instance = instance->next; + continue; + } + + occluders.push_back(co); + occluder_indices.push_back(occluder_count - 1); - projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + instance = instance->next; } - // Precomputed: - // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); - // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse()); - const Projection projections[4] = { - projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), + // Then, upload all the occluder transforms to a shared buffer. + // We only do this for the first light so we can avoid uploading the same + // Transforms over and over again. + if (p_shadow_index == 0) { + static thread_local LocalVector transforms; + transforms.clear(); + transforms.resize(occluder_count * 8); - projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), + instance = p_occluders; + uint32_t index = 0; + while (instance) { + _update_transform_2d_to_mat2x4(instance->xform_cache, &transforms[index * 8]); + index++; + instance = instance->next; + } - projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), + _update_occluder_buffer(occluder_count * 8 * sizeof(float)); + RD::get_singleton()->buffer_update(state.shadow_occluder_buffer, 0, transforms.size() * sizeof(float), transforms.ptr()); + } - projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)) + Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect); - }; + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[SHADOW_RENDER_MODE_POSITIONAL_SHADOW]); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.shadow_ocluder_uniform_set, 0); for (int i = 0; i < 4; i++) { - Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect); + Rect2i sub_rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); + RD::get_singleton()->draw_list_set_viewport(draw_list, sub_rect); - ShadowRenderPushConstant push_constant; - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - push_constant.projection[y * 4 + x] = projections[i].columns[y][x]; - } - } static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; + static const Vector4 rotations[4] = { Vector4(0, -1, 1, 0), Vector4(-1, 0, 0, -1), Vector4(0, 1, -1, 0), Vector4(1, 0, 0, 1) }; + + PositionalShadowRenderPushConstant push_constant; + _update_transform_2d_to_mat2x4(p_light_xform, push_constant.modelview); push_constant.direction[0] = directions[i].x; push_constant.direction[1] = directions[i].y; + push_constant.rotation[0] = rotations[i].x; + push_constant.rotation[1] = rotations[i].y; + push_constant.rotation[2] = rotations[i].z; + push_constant.rotation[3] = rotations[i].w; push_constant.z_far = p_far; - push_constant.pad = 0; + push_constant.z_near = p_near; - LightOccluderInstance *instance = p_occluders; - - while (instance) { - OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - - if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) { - instance = instance->next; - continue; - } + for (uint32_t j = 0; j < occluders.size(); j++) { + OccluderPolygon *co = occluders[j]; - _update_transform_2d_to_mat2x4(p_light_xform * instance->xform_cache, push_constant.modelview); + push_constant.pad = occluder_indices[j]; + push_constant.cull_mode = uint32_t(co->cull_mode); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); + // The slowest part about this whole function is that we have to draw the occluders one by one, 4 times. + // We can optimize this so that all occluders draw at once if we store vertices and indices in a giant + // SSBO and just save an index into that SSBO for each occluder. RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(PositionalShadowRenderPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, true); - - instance = instance->next; } - - RD::get_singleton()->draw_list_end(); } + RD::get_singleton()->draw_list_end(); } void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) { @@ -1099,6 +1148,7 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[SHADOW_RENDER_MODE_DIRECTIONAL_SHADOW]); Projection projection; projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); @@ -1114,7 +1164,6 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh push_constant.direction[0] = 0.0; push_constant.direction[1] = 1.0; push_constant.z_far = distance; - push_constant.pad = 0; LightOccluderInstance *instance = p_occluders; @@ -1127,8 +1176,8 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh } _update_transform_2d_to_mat2x4(to_light_xform * instance->xform_cache, push_constant.modelview); + push_constant.cull_mode = uint32_t(co->cull_mode); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]); RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array); RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array); RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant)); @@ -1182,7 +1231,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan push_constant.direction[0] = 0.0; push_constant.direction[1] = 0.0; push_constant.z_far = 0; - push_constant.pad = 0; + push_constant.cull_mode = 0; LightOccluderInstance *instance = p_occluders; @@ -1791,8 +1840,9 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { { //shadow rendering Vector versions; - versions.push_back("\n#define MODE_SHADOW\n"); //shadow - versions.push_back("\n#define MODE_SDF\n"); //sdf + versions.push_back("\n#define MODE_SHADOW\n"); // Shadow. + versions.push_back("\n#define MODE_SHADOW\n#define POSITIONAL_SHADOW\n"); // Positional shadow. + versions.push_back("\n#define MODE_SDF\n"); // SDF. shadow_render.shader.initialize(versions); { @@ -1843,14 +1893,13 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { shadow_render.shader_version = shadow_render.shader.version_create(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 2; i++) { RD::PipelineRasterizationState rs; - rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK); RD::PipelineDepthStencilState ds; ds.enable_depth_write = true; ds.enable_depth_test = true; ds.depth_compare_operator = RD::COMPARE_OP_LESS; - shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SHADOW), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, ShadowRenderMode(i)), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); } for (int i = 0; i < 2; i++) { @@ -1858,7 +1907,8 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { } // Unload shader modules to save memory. - RD::get_singleton()->shader_destroy_modules(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SHADOW)); + RD::get_singleton()->shader_destroy_modules(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_DIRECTIONAL_SHADOW)); + RD::get_singleton()->shader_destroy_modules(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_POSITIONAL_SHADOW)); RD::get_singleton()->shader_destroy_modules(shadow_render.shader.version_get_shader(shadow_render.shader_version, SHADOW_RENDER_MODE_SDF)); } @@ -3251,6 +3301,10 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() { } RD::get_singleton()->free(state.shadow_texture); + if (state.shadow_occluder_buffer.is_valid()) { + RD::get_singleton()->free(state.shadow_occluder_buffer); + } + memdelete_arr(state.instance_data_array); for (uint32_t i = 0; i < BATCH_DATA_BUFFER_COUNT; i++) { for (uint32_t j = 0; j < state.canvas_instance_data_buffers[i].instance_buffers.size(); j++) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 6d8275db2e33..238db8f28648 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -268,12 +268,23 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID_Owner canvas_light_owner; + struct PositionalShadowRenderPushConstant { + float modelview[8]; + float rotation[4]; + float direction[2]; + float z_far; + uint32_t pad; + float z_near; + uint32_t cull_mode; + float pad2[2]; + }; + struct ShadowRenderPushConstant { float projection[16]; float modelview[8]; float direction[2]; float z_far; - float pad; + uint32_t cull_mode; }; struct OccluderPolygon { @@ -313,7 +324,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID_Owner occluder_polygon_owner; enum ShadowRenderMode { - SHADOW_RENDER_MODE_SHADOW, + SHADOW_RENDER_MODE_DIRECTIONAL_SHADOW, + SHADOW_RENDER_MODE_POSITIONAL_SHADOW, SHADOW_RENDER_MODE_SDF, }; @@ -325,7 +337,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { struct { CanvasOcclusionShaderRD shader; RID shader_version; - RID render_pipelines[3]; + RID render_pipelines[2]; RID sdf_render_pipelines[2]; RD::VertexFormatID vertex_format; RD::VertexFormatID sdf_vertex_format; @@ -564,6 +576,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID shadow_fb; int shadow_texture_size = 2048; + RID shadow_occluder_buffer; + uint32_t shadow_occluder_buffer_size; + RID shadow_ocluder_uniform_set; + RID default_transforms_uniform_set; uint32_t max_lights_per_render; @@ -617,6 +633,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { _FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4); void _update_shadow_atlas(); + void _update_occluder_buffer(uint32_t p_size); public: PolygonID request_polygon(const Vector &p_indices, const Vector &p_points, const Vector &p_colors, const Vector &p_uvs = Vector(), const Vector &p_bones = Vector(), const Vector &p_weights = Vector()) override; @@ -625,7 +642,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID light_create() override; void light_set_texture(RID p_rid, RID p_texture) override; void light_set_use_shadow(RID p_rid, bool p_enable) override; - void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override; + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override; void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override; virtual void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override; diff --git a/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl b/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl index 930cf792cbb9..c3162a9c1f87 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl @@ -6,26 +6,69 @@ layout(location = 0) in highp vec3 vertex; +#ifdef POSITIONAL_SHADOW +layout(push_constant, std430) uniform Constants { + mat2x4 modelview; + vec4 rotation; + vec2 direction; + float z_far; + uint pad; + float z_near; + uint cull_mode; + float pad3; + float pad4; +} +constants; + +layout(set = 0, binding = 0, std430) restrict readonly buffer OccluderTransforms { + mat2x4 transforms[]; +} +occluder_transforms; + +#else + layout(push_constant, std430) uniform Constants { mat4 projection; mat2x4 modelview; vec2 direction; float z_far; - float pad; + uint cull_mode; } constants; +#endif + #ifdef MODE_SHADOW layout(location = 0) out highp float depth; #endif void main() { - highp vec4 vtx = vec4(vertex, 1.0) * mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); +#ifdef POSITIONAL_SHADOW + float c = -(constants.z_far + constants.z_near) / (constants.z_far - constants.z_near); + float d = -2.0 * constants.z_far * constants.z_near / (constants.z_far - constants.z_near); + + mat4 projection = mat4(vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, c, -1.0), + vec4(0.0, 0.0, d, 0.0)); + + // Precomputed: + // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse()); + projection *= mat4(vec4(constants.rotation.x, 0.0, constants.rotation.y, 0.0), vec4(constants.rotation.z, 0.0, constants.rotation.w, 0.0), vec4(0.0, -1.0, 0.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + mat4 modelview = mat4(occluder_transforms.transforms[constants.pad]) * mat4(constants.modelview); +#else + mat4 projection = constants.projection; + mat4 modelview = mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); +#endif + + highp vec4 vtx = vec4(vertex, 1.0) * modelview; #ifdef MODE_SHADOW depth = dot(constants.direction, vtx.xy); #endif - gl_Position = constants.projection * vtx; + + gl_Position = projection * vtx; } #[fragment] @@ -34,15 +77,33 @@ void main() { #VERSION_DEFINES +#ifdef POSITIONAL_SHADOW +layout(push_constant, std430) uniform Constants { + mat2x4 modelview; + vec4 rotation; + vec2 direction; + float z_far; + uint pad; + float z_near; + uint cull_mode; + float pad3; + float pad4; +} +constants; + +#else + layout(push_constant, std430) uniform Constants { mat4 projection; mat2x4 modelview; vec2 direction; float z_far; - float pad; + uint cull_mode; } constants; +#endif + #ifdef MODE_SHADOW layout(location = 0) in highp float depth; layout(location = 0) out highp float distance_buf; @@ -50,8 +111,18 @@ layout(location = 0) out highp float distance_buf; layout(location = 0) out highp float sdf_buf; #endif +#define POLYGON_CULL_DISABLED 0 +#define POLYGON_CULL_FRONT 1 +#define POLYGON_CULL_BACK 2 + void main() { #ifdef MODE_SHADOW + bool front_facing = gl_FrontFacing; + if (constants.cull_mode == POLYGON_CULL_BACK && !front_facing) { + discard; + } else if (constants.cull_mode == POLYGON_CULL_FRONT && front_facing) { + discard; + } distance_buf = depth / constants.z_far; #else sdf_buf = 1.0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index bdc72b524595..342772c40118 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -499,7 +499,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { while (light) { RENDER_TIMESTAMP("Render PointLight2D Shadow"); - RSG::canvas_render->light_update_shadow(light->light_internal, shadow_count++, light->xform_cache.affine_inverse(), light->item_shadow_mask, light->radius_cache / 1000.0, light->radius_cache * 1.1, occluders); + RSG::canvas_render->light_update_shadow(light->light_internal, shadow_count++, light->xform_cache.affine_inverse(), light->item_shadow_mask, light->radius_cache / 1000.0, light->radius_cache * 1.1, occluders, light->rect_cache); light = light->shadows_next_ptr; }