diff options
23 files changed, 155 insertions, 147 deletions
diff --git a/core/math/transform_interpolator.cpp b/core/math/transform_interpolator.cpp index 7cfe880b5a..6a564b0ca7 100644 --- a/core/math/transform_interpolator.cpp +++ b/core/math/transform_interpolator.cpp @@ -33,44 +33,14 @@ #include "core/math/transform_2d.h" void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction) { - // Extract parameters. - Vector2 p1 = p_prev.get_origin(); - Vector2 p2 = p_curr.get_origin(); - // Special case for physics interpolation, if flipping, don't interpolate basis. // If the determinant polarity changes, the handedness of the coordinate system changes. if (_sign(p_prev.determinant()) != _sign(p_curr.determinant())) { r_result.columns[0] = p_curr.columns[0]; r_result.columns[1] = p_curr.columns[1]; - r_result.set_origin(p1.lerp(p2, p_fraction)); + r_result.set_origin(p_prev.get_origin().lerp(p_curr.get_origin(), p_fraction)); return; } - real_t r1 = p_prev.get_rotation(); - real_t r2 = p_curr.get_rotation(); - - Size2 s1 = p_prev.get_scale(); - Size2 s2 = p_curr.get_scale(); - - // Slerp rotation. - Vector2 v1(Math::cos(r1), Math::sin(r1)); - Vector2 v2(Math::cos(r2), Math::sin(r2)); - - real_t dot = v1.dot(v2); - - dot = CLAMP(dot, -1, 1); - - Vector2 v; - - if (dot > 0.9995f) { - v = v1.lerp(v2, p_fraction).normalized(); // Linearly interpolate to avoid numerical precision issues. - } else { - real_t angle = p_fraction * Math::acos(dot); - Vector2 v3 = (v2 - v1 * dot).normalized(); - v = v1 * Math::cos(angle) + v3 * Math::sin(angle); - } - - // Construct matrix. - r_result = Transform2D(Math::atan2(v.y, v.x), p1.lerp(p2, p_fraction)); - r_result.scale_basis(s1.lerp(s2, p_fraction)); + r_result = p_prev.interpolate_with(p_curr, p_fraction); } diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 30a8facd67..c1ef31c784 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -951,7 +951,7 @@ bool Variant::is_zero() const { return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID(); } case OBJECT: { - return get_validated_object() == nullptr; + return _get_obj().obj == nullptr; } case CALLABLE: { return reinterpret_cast<const Callable *>(_data._mem)->is_null(); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index 0b94d79a97..ac39a4135f 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -548,14 +548,14 @@ public: class OperatorEvaluatorEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const ObjectID &a = VariantInternal::get_object_id(&p_left); - const ObjectID &b = VariantInternal::get_object_id(&p_right); + const Object *a = p_left.get_validated_object(); + const Object *b = p_right.get_validated_object(); *r_ret = a == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const ObjectID &a = VariantInternal::get_object_id(left); - const ObjectID &b = VariantInternal::get_object_id(right); + const Object *a = left->get_validated_object(); + const Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -567,12 +567,12 @@ public: class OperatorEvaluatorEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *a = p_left.operator Object *(); + const Object *a = p_left.get_validated_object(); *r_ret = a == nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *a = left->operator Object *(); + const Object *a = left->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -584,12 +584,12 @@ public: class OperatorEvaluatorEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *b = p_right.operator Object *(); + const Object *b = p_right.get_validated_object(); *r_ret = nullptr == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *b = right->operator Object *(); + const Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -619,14 +619,14 @@ public: class OperatorEvaluatorNotEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const ObjectID &a = VariantInternal::get_object_id(&p_left); - const ObjectID &b = VariantInternal::get_object_id(&p_right); + Object *a = p_left.get_validated_object(); + Object *b = p_right.get_validated_object(); *r_ret = a != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const ObjectID &a = VariantInternal::get_object_id(left); - const ObjectID &b = VariantInternal::get_object_id(right); + Object *a = left->get_validated_object(); + Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -638,12 +638,12 @@ public: class OperatorEvaluatorNotEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *a = p_left.operator Object *(); + Object *a = p_left.get_validated_object(); *r_ret = a != nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *a = left->operator Object *(); + Object *a = left->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -655,12 +655,12 @@ public: class OperatorEvaluatorNotEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *b = p_right.operator Object *(); + Object *b = p_right.get_validated_object(); *r_ret = nullptr != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *b = right->operator Object *(); + Object *b = right->get_validated_object(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index c635eba2ab..8a493ce91f 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -27,6 +27,13 @@ <param index="1" name="library" type="AnimationLibrary" /> <description> Adds [param library] to the animation player, under the key [param name]. + AnimationMixer has a global library by default with an empty string as key. For adding an animation to the global library: + [codeblocks] + [gdscript] + var global_library = mixer.get_animation_library("") + global_library.add_animation("animation_name", animation_resource) + [/gdscript] + [/codeblocks] </description> </method> <method name="advance"> @@ -182,10 +189,10 @@ func _process(delta): if Input.is_action_just_pressed("animate"): state_machine.travel("Animate") - var current_root_motion_rotation_accumulator: Quaternion = animation_tree.get_root_motion_Quaternion_accumulator() + var current_root_motion_rotation_accumulator: Quaternion = animation_tree.get_root_motion_rotation_accumulator() var difference: Quaternion = prev_root_motion_rotation_accumulator.inverse() * current_root_motion_rotation_accumulator prev_root_motion_rotation_accumulator = current_root_motion_rotation_accumulator - transform.basis *= difference + transform.basis *= Basis(difference) [/gdscript] [/codeblocks] However, if the animation loops, an unintended discrete change may occur, so this is only useful for some simple use cases. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 41bda1033d..322d2ab9d4 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -8,7 +8,7 @@ A [Basis] is composed by 3 axis vectors, each representing a column of the matrix: [member x], [member y], and [member z]. The length of each axis ([method Vector3.length]) influences the basis's scale, while the direction of all axes influence the rotation. Usually, these axes are perpendicular to one another. However, when you rotate any axis individually, the basis becomes sheared. Applying a sheared basis to a 3D model will make the model appear distorted. A [Basis] is [b]orthogonal[/b] if its axes are perpendicular to each other. A basis is [b]normalized[/b] if the length of every axis is [code]1[/code]. A basis is [b]uniform[/b] if all axes share the same length (see [method get_scale]). A basis is [b]orthonormal[/b] if it is both orthogonal and normalized, which allows it to only represent rotations. A basis is [b]conformal[/b] if it is both orthogonal and uniform, which ensures it is not distorted. For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial. - [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#d-asset-direction-conventions]Importing 3D Scenes[/url] tutorial. + [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions]3D asset direction conventions[/url] tutorial. [b]Note:[/b] The basis matrices are exposed as [url=https://www.mindcontrol.org/~hplus/graphics/matrix-layout.html]column-major[/url] order, which is the same as OpenGL. However, they are stored internally in row-major order, which is the same as DirectX. </description> <tutorials> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 207045b065..8bee6c3470 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -623,8 +623,8 @@ [b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead. </member> <member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false"> - If [code]true[/code], this and child [CanvasItem] nodes with a lower Y position are rendered in front of nodes with a higher Y position. If [code]false[/code], this and child [CanvasItem] nodes are rendered normally in scene tree order. - With Y-sorting enabled on a parent node ('A') but disabled on a child node ('B'), the child node ('B') is sorted but its children ('C1', 'C2', etc) render together on the same Y position as the child node 'B'. This allows you to organize the render order of a scene without changing the scene tree. + If [code]true[/code], this and child [CanvasItem] nodes with a higher Y position are rendered in front of nodes with a lower Y position. If [code]false[/code], this and child [CanvasItem] nodes are rendered normally in scene tree order. + With Y-sorting enabled on a parent node ('A') but disabled on a child node ('B'), the child node ('B') is sorted but its children ('C1', 'C2', etc) render together on the same Y position as the child node ('B'). This allows you to organize the render order of a scene without changing the scene tree. Nodes sort relative to each other only if they are on the same [member z_index]. </member> <member name="z_as_relative" type="bool" setter="set_z_as_relative" getter="is_z_relative" default="true"> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 3e2b3ea111..4d304cf5fd 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -17,7 +17,7 @@ <param index="1" name="features" type="PackedStringArray" /> <description> Return [code]true[/code] if this plugin will customize resources based on the platform and features used. - When enabled, [method _get_customization_configuration_hash], [method _customize_resource] and [method _customize_scene] will be called and must be implemented. + When enabled, [method _get_customization_configuration_hash] and [method _customize_resource] will be called and must be implemented. </description> </method> <method name="_begin_customize_scenes" qualifiers="virtual const"> @@ -25,7 +25,8 @@ <param index="0" name="platform" type="EditorExportPlatform" /> <param index="1" name="features" type="PackedStringArray" /> <description> - Return true if this plugin will customize scenes based on the platform and features used. + Return [code]true[/code] if this plugin will customize scenes based on the platform and features used. + When enabled, [method _get_customization_configuration_hash] and [method _customize_scene] will be called and must be implemented. </description> </method> <method name="_customize_resource" qualifiers="virtual"> @@ -35,6 +36,7 @@ <description> Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code]. The [i]path[/i] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty. + Calling [method skip] inside this callback will make the file not included in the export. Implementing this method is required if [method _begin_customize_resources] returns [code]true[/code]. </description> </method> @@ -44,6 +46,7 @@ <param index="1" name="path" type="String" /> <description> Customize a scene. If changes are made to it, return the same or a new scene. Otherwise, return [code]null[/code]. If a new scene is returned, it is up to you to dispose of the old one. + Calling [method skip] inside this callback will make the file not included in the export. Implementing this method is required if [method _begin_customize_scenes] returns [code]true[/code]. </description> </method> @@ -81,8 +84,9 @@ <param index="1" name="type" type="String" /> <param index="2" name="features" type="PackedStringArray" /> <description> - Virtual method to be overridden by the user. Called for each exported file, providing arguments that can be used to identify the file. [param path] is the path of the file, [param type] is the [Resource] represented by the file (e.g. [PackedScene]) and [param features] is the list of features for the export. + Virtual method to be overridden by the user. Called for each exported file, except for imported resources (resources that have an associated [code].import[/code] file). The arguments can be used to identify the file. [param path] is the path of the file, [param type] is the [Resource] represented by the file (e.g. [PackedScene]), and [param features] is the list of features for the export. Calling [method skip] inside this callback will make the file not included in the export. + Use [method _customize_resource] for imported resources that are not handled by this function. </description> </method> <method name="_get_android_dependencies" qualifiers="virtual const"> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 2a0bbc46af..30c141659a 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -6,7 +6,7 @@ <description> The [Transform3D] built-in [Variant] type is a 3×4 matrix representing a transformation in 3D space. It contains a [Basis], which on its own can represent rotation, scale, and shear. Additionally, combined with its own [member origin], the transform can also represent a translation. For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial. - [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#d-asset-direction-conventions]Importing 3D Scenes[/url] tutorial. + [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions]3D asset direction conventions[/url] tutorial. </description> <tutorials> <link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link> diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index ca2b392e66..a6bce1d79a 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -872,6 +872,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel D3D12MA::ALLOCATION_DESC allocation_desc = {}; allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_STATES initial_state = D3D12_RESOURCE_STATE_COMMON; switch (p_allocation_type) { case MEMORY_ALLOCATION_TYPE_CPU: { bool is_src = p_usage.has_flag(BUFFER_USAGE_TRANSFER_FROM_BIT); @@ -879,10 +880,12 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel if (is_src && !is_dst) { // Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM. allocation_desc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + initial_state = D3D12_RESOURCE_STATE_GENERIC_READ; } if (is_dst && !is_src) { // Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads. allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK; + initial_state = D3D12_RESOURCE_STATE_COPY_DEST; } } break; case MEMORY_ALLOCATION_TYPE_GPU: { @@ -911,7 +914,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel res = allocator->CreateResource( &allocation_desc, reinterpret_cast<const D3D12_RESOURCE_DESC *>(&resource_desc), - D3D12_RESOURCE_STATE_COMMON, + initial_state, nullptr, allocation.GetAddressOf(), IID_PPV_ARGS(buffer.GetAddressOf())); @@ -925,7 +928,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel buf_info->resource = buffer.Get(); buf_info->owner_info.resource = buffer; buf_info->owner_info.allocation = allocation; - buf_info->owner_info.states.subresource_states.push_back(D3D12_RESOURCE_STATE_COMMON); + buf_info->owner_info.states.subresource_states.push_back(initial_state); buf_info->states_ptr = &buf_info->owner_info.states; buf_info->size = p_size; buf_info->flags.usable_as_uav = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index d192ce0356..ad7598202f 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -872,14 +872,14 @@ void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::Hand } void AnimationBezierTrackEdit::_clear_selection_for_anim(const Ref<Animation> &p_anim) { - if (!(animation == p_anim)) { + if (!(animation == p_anim) || !is_visible()) { return; } _clear_selection(); } void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int p_track, real_t p_pos, bool p_single) { - if (!(animation == p_anim)) { + if (!(animation == p_anim) || !is_visible()) { return; } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 5b4341bbe1..4e2940a8cb 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -780,19 +780,24 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po return still_selected; } -List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) const { +List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool p_retrieve_locked, bool p_remove_canvas_item_if_parent_in_selection, bool *r_has_locked_items) const { List<CanvasItem *> selection; for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) { CanvasItem *ci = Object::cast_to<CanvasItem>(E.key); - if (ci && ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(ci))) { - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); - if (se) { - selection.push_back(ci); + if (ci) { + if (ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (p_retrieve_locked || !_is_node_locked(ci))) { + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); + if (se) { + selection.push_back(ci); + } + } else if (r_has_locked_items) { + // CanvasItem is selected, but can't be interacted with. + *r_has_locked_items = true; } } } - if (remove_canvas_item_if_parent_in_selection) { + if (p_remove_canvas_item_if_parent_in_selection) { List<CanvasItem *> filtered_selection; for (CanvasItem *E : selection) { if (!selection.find(E->get_parent())) { @@ -1454,7 +1459,8 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { if ((b->is_command_or_control_pressed() && !b->is_alt_pressed() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); // Remove not movable nodes for (CanvasItem *E : selection) { @@ -1477,6 +1483,11 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { } _save_canvas_item_state(drag_selection); return true; + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } @@ -1917,7 +1928,8 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Drag resize handles if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); if (selection.size() == 1) { CanvasItem *ci = selection.front()->get(); @@ -1946,6 +1958,11 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { _save_canvas_item_state(drag_selection); return true; } + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } @@ -2056,7 +2073,8 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); if (selection.size() > 0) { drag_selection.clear(); @@ -2089,6 +2107,11 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { _save_canvas_item_state(drag_selection); return true; + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index a9de5e9a0b..bae9efebc9 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -188,6 +188,8 @@ private: GRID_VISIBILITY_HIDE, }; + const String locked_transform_warning = TTRC("All selected CanvasItems are either invisible or locked in some way and can't be transformed."); + bool selection_menu_additive_selection = false; Tool tool = TOOL_SELECT; @@ -430,7 +432,7 @@ private: ThemePreviewMode theme_preview = THEME_PREVIEW_PROJECT; void _switch_theme_preview(int p_mode); - List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const; + List<CanvasItem *> _get_edited_canvas_items(bool p_retrieve_locked = false, bool p_remove_canvas_item_if_parent_in_selection = true, bool *r_has_locked_items = nullptr) const; Rect2 _get_encompassing_rect_from_list(const List<CanvasItem *> &p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index f6e4c6f951..d3afd25502 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -764,12 +764,13 @@ bool TileMapLayerEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEven } else { // Released. - drag_erasing = false; if (drag_type == DRAG_TYPE_NONE) { + drag_erasing = false; return false; } else { _stop_dragging(); } + drag_erasing = false; } CanvasItemEditor::get_singleton()->update_viewport(); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index c56ae0fb14..7c9fba799d 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -42,6 +42,10 @@ GDScriptParserRef::Status GDScriptParserRef::get_status() const { return status; } +String GDScriptParserRef::get_path() const { + return path; +} + uint32_t GDScriptParserRef::get_source_hash() const { return source_hash; } @@ -131,9 +135,7 @@ void GDScriptParserRef::clear() { GDScriptParserRef::~GDScriptParserRef() { clear(); - - MutexLock lock(GDScriptCache::singleton->mutex); - GDScriptCache::singleton->parser_map.erase(path); + GDScriptCache::remove_parser(path); } GDScriptCache *GDScriptCache::singleton = nullptr; @@ -154,6 +156,11 @@ void GDScriptCache::move_script(const String &p_from, const String &p_to) { } singleton->parser_map.erase(p_from); + if (singleton->parser_inverse_dependencies.has(p_from) && !p_from.is_empty()) { + singleton->parser_inverse_dependencies[p_to] = singleton->parser_inverse_dependencies[p_from]; + } + singleton->parser_inverse_dependencies.erase(p_from); + if (singleton->shallow_gdscript_cache.has(p_from) && !p_from.is_empty()) { singleton->shallow_gdscript_cache[p_to] = singleton->shallow_gdscript_cache[p_from]; } @@ -177,13 +184,11 @@ void GDScriptCache::remove_script(const String &p_path) { } if (singleton->parser_map.has(p_path)) { - // Keep a local reference until it goes out of scope. - // Clearing it can trigger a reference to itself to go out of scope, destructing it before clear finishes. - Ref<GDScriptParserRef> parser_ref = singleton->parser_map[p_path]; - singleton->parser_map.erase(p_path); - parser_ref->clear(); + singleton->parser_map[p_path]->clear(); } + remove_parser(p_path); + singleton->dependencies.erase(p_path); singleton->shallow_gdscript_cache.erase(p_path); singleton->full_gdscript_cache.erase(p_path); @@ -194,6 +199,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP Ref<GDScriptParserRef> ref; if (!p_owner.is_empty()) { singleton->dependencies[p_owner].insert(p_path); + singleton->parser_inverse_dependencies[p_path].insert(p_owner); } if (singleton->parser_map.has(p_path)) { ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]); @@ -225,6 +231,13 @@ void GDScriptCache::remove_parser(const String &p_path) { MutexLock lock(singleton->mutex); // Can't clear the parser because some other parser might be currently using it in the chain of calls. singleton->parser_map.erase(p_path); + + // Have to copy while iterating, because parser_inverse_dependencies is modified. + HashSet<String> ideps = singleton->parser_inverse_dependencies[p_path]; + singleton->parser_inverse_dependencies.erase(p_path); + for (String idep_path : ideps) { + remove_parser(idep_path); + } } String GDScriptCache::get_source_code(const String &p_path) { @@ -417,6 +430,8 @@ void GDScriptCache::clear() { } singleton->cleared = true; + singleton->parser_inverse_dependencies.clear(); + RBSet<Ref<GDScriptParserRef>> parser_map_refs; for (KeyValue<String, GDScriptParserRef *> &E : singleton->parser_map) { parser_map_refs.insert(E.value); diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 16121cc082..c927317e19 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -65,6 +65,7 @@ private: public: Status get_status() const; + String get_path() const; uint32_t get_source_hash() const; GDScriptParser *get_parser(); GDScriptAnalyzer *get_analyzer(); @@ -82,6 +83,7 @@ class GDScriptCache { HashMap<String, Ref<GDScript>> full_gdscript_cache; HashMap<String, Ref<GDScript>> static_gdscript_cache; HashMap<String, HashSet<String>> dependencies; + HashMap<String, HashSet<String>> parser_inverse_dependencies; friend class GDScript; friend class GDScriptParserRef; diff --git a/platform/web/SCsub b/platform/web/SCsub index 3d36a888d6..e81f2ec516 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -71,8 +71,6 @@ if env["dlink_enabled"]: sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"]) sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"]) - # Force exporting the standard library (printf, malloc, etc.) - sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi" sys_env["CCFLAGS"].remove("-fvisibility=hidden") sys_env["LINKFLAGS"].remove("-fvisibility=hidden") diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 4bca13d2d6..531dbdaeab 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -142,7 +142,7 @@ class Sample { * @returns {void} */ clear() { - this.audioBuffer = null; + this.setAudioBuffer(null); GodotAudio.Sample.delete(this.id); } @@ -432,7 +432,7 @@ class SampleNode { /** @type {number} */ this._playbackRate = 44100; /** @type {LoopMode} */ - this.loopMode = 'disabled'; + this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled'; /** @type {number} */ this._pitchScale = 1; /** @type {number} */ @@ -445,7 +445,6 @@ class SampleNode { this._onended = null; this.setPlaybackRate(options.playbackRate ?? 44100); - this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled'; this._source.buffer = this.getSample().getAudioBuffer(); this._addEndedListener(); @@ -777,8 +776,7 @@ class Bus { */ static move(fromIndex, toIndex) { const movedBus = GodotAudio.Bus.getBus(fromIndex); - let buses = GodotAudio.buses; - buses = buses.filter((_, i) => i !== fromIndex); + const buses = GodotAudio.buses.filter((_, i) => i !== fromIndex); // Inserts at index. buses.splice(toIndex - 1, 0, movedBus); GodotAudio.buses = buses; @@ -1369,7 +1367,7 @@ const _GodotAudio = { */ set_sample_bus_volume_db: function (busIndex, volumeDb) { const bus = GodotAudio.Bus.getBus(busIndex); - bus.volumeDb = volumeDb; + bus.setVolumeDb(volumeDb); }, /** diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 8e1abba3bb..fee306a25c 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -500,6 +500,14 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if env["arch"] == "x86_64": env.AppendUnique(CPPDEFINES=["_WIN64"]) + # Sanitizers + prebuilt_lib_extra_suffix = "" + if env["use_asan"]: + env.extra_suffix += ".san" + prebuilt_lib_extra_suffix = ".san" + env.Append(CCFLAGS=["/fsanitize=address"]) + env.Append(LINKFLAGS=["/INFERASANLIBS"]) + ## Libs LIBS = [ @@ -567,7 +575,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): LIBS += ["WinPixEventRuntime"] env.Append(LIBPATH=[env["mesa_libs"] + "/bin"]) - LIBS += ["libNIR.windows." + env["arch"]] + LIBS += ["libNIR.windows." + env["arch"] + prebuilt_lib_extra_suffix] if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) @@ -575,9 +583,9 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.AppendUnique(CPPDEFINES=["EGL_STATIC"]) env.Append(LIBPATH=[env["angle_libs"]]) LIBS += [ - "libANGLE.windows." + env["arch"], - "libEGL.windows." + env["arch"], - "libGLES.windows." + env["arch"], + "libANGLE.windows." + env["arch"] + prebuilt_lib_extra_suffix, + "libEGL.windows." + env["arch"] + prebuilt_lib_extra_suffix, + "libGLES.windows." + env["arch"] + prebuilt_lib_extra_suffix, ] LIBS += ["dxgi", "d3d9", "d3d11"] env.Prepend(CPPPATH=["#thirdparty/angle/include"]) @@ -613,12 +621,6 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")]) env.Append(LIBPATH=[p for p in str(os.getenv("LIB")).split(";")]) - # Sanitizers - if env["use_asan"]: - env.extra_suffix += ".san" - env.Append(LINKFLAGS=["/INFERASANLIBS"]) - env.Append(CCFLAGS=["/fsanitize=address"]) - # Incremental linking fix env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"] env["BUILDERS"]["Program"] = methods.precious_program diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index c52f463905..972a832985 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -30,7 +30,6 @@ #include "texture_rect.h" -#include "scene/resources/atlas_texture.h" #include "servers/rendering_server.h" void TextureRect::_notification(int p_what) { @@ -92,15 +91,6 @@ void TextureRect::_notification(int p_what) { } break; } - Ref<AtlasTexture> p_atlas = texture; - - if (p_atlas.is_valid() && !region.has_area()) { - Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height()); - - offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0; - offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0; - } - size.width *= hflip ? -1.0f : 1.0f; size.height *= vflip ? -1.0f : 1.0f; diff --git a/scene/resources/atlas_texture.cpp b/scene/resources/atlas_texture.cpp index ef2f1eb135..28e4186048 100644 --- a/scene/resources/atlas_texture.cpp +++ b/scene/resources/atlas_texture.cpp @@ -164,20 +164,13 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile return; } - Rect2 rc = region; + Rect2 src_rect = Rect2(0, 0, get_width(), get_height()); - if (rc.size.width == 0) { - rc.size.width = atlas->get_width(); - } - - if (rc.size.height == 0) { - rc.size.height = atlas->get_height(); + Rect2 dr; + Rect2 src_c; + if (get_rect_region(p_rect, src_rect, dr, src_c)) { + atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip); } - - Vector2 scale = p_rect.size / (region.size + margin.size); - Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale); - - atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip); } void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { @@ -188,9 +181,9 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons Rect2 dr; Rect2 src_c; - get_rect_region(p_rect, p_src_rect, dr, src_c); - - atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip); + if (get_rect_region(p_rect, p_src_rect, dr, src_c)) { + atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip); + } } bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const { diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h index 7e4a517c1d..60422c16cc 100644 --- a/servers/display_server_headless.h +++ b/servers/display_server_headless.h @@ -51,7 +51,18 @@ private: return memnew(DisplayServerHeadless()); } + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerHeadless *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + if (input_event_callback.is_valid()) { + input_event_callback.call(p_event); + } + } + NativeMenu *native_menu = nullptr; + Callable input_event_callback; public: bool has_feature(Feature p_feature) const override { return false; } @@ -86,7 +97,11 @@ public: void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} - void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} + + void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} @@ -137,7 +152,9 @@ public: int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override { return 0; } - void process_events() override {} + void process_events() override { + Input::get_singleton()->flush_buffered_events(); + } void set_native_icon(const String &p_filename) override {} void set_icon(const Ref<Image> &p_icon) override {} @@ -179,7 +196,9 @@ public: DisplayServerHeadless() { native_menu = memnew(NativeMenu); + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); } + ~DisplayServerHeadless() { if (native_menu) { memdelete(native_menu); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 5f1643229c..f5e0b811a2 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -10837,4 +10837,5 @@ ShaderLanguage::ShaderLanguage() { ShaderLanguage::~ShaderLanguage() { clear(); + global_func_set.clear(); } diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h index e4946995a7..b44ff06b35 100644 --- a/tests/display_server_mock.h +++ b/tests/display_server_mock.h @@ -36,7 +36,7 @@ #include "servers/rendering/dummy/rasterizer_dummy.h" // Specialized DisplayServer for unittests based on DisplayServerHeadless, that -// additionally supports rudimentary InputEvent handling and mouse position. +// additionally supports things like mouse enter/exit events and clipboard. class DisplayServerMock : public DisplayServerHeadless { private: friend class DisplayServer; @@ -45,7 +45,6 @@ private: CursorShape cursor_shape = CursorShape::CURSOR_ARROW; bool window_over = false; Callable event_callback; - Callable input_event_callback; String clipboard_text; String primary_clipboard_text; @@ -62,16 +61,6 @@ private: return memnew(DisplayServerMock()); } - static void _dispatch_input_events(const Ref<InputEvent> &p_event) { - static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); - } - - void _dispatch_input_event(const Ref<InputEvent> &p_event) { - if (input_event_callback.is_valid()) { - input_event_callback.call(p_event); - } - } - void _set_mouse_position(const Point2i &p_position) { if (mouse_position == p_position) { return; @@ -153,18 +142,9 @@ public: event_callback = p_callable; } - virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { - input_event_callback = p_callable; - } - static void register_mock_driver() { register_create_function("mock", create_func, get_rendering_drivers_func); } - - DisplayServerMock() { - Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); - } - ~DisplayServerMock() {} }; #endif // DISPLAY_SERVER_MOCK_H |