diff options
79 files changed, 1261 insertions, 307 deletions
diff --git a/.clang-format b/.clang-format index f7527b8927..1df6c35bfb 100644 --- a/.clang-format +++ b/.clang-format @@ -19,7 +19,7 @@ AllowAllParametersOfDeclarationOnNextLine: false # AllowShortEnumsOnASingleLine: true # AllowShortBlocksOnASingleLine: Never # AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline +# AllowShortFunctionsOnASingleLine: All # AllowShortLambdasOnASingleLine: All # AllowShortIfStatementsOnASingleLine: Never # AllowShortLoopsOnASingleLine: false diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 2cf6e8a025..56130134a0 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -933,6 +933,7 @@ Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) { void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle); + ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &Geometry2D::segment_intersects_circle); ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &Geometry2D::segment_intersects_segment); ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &Geometry2D::line_intersects_line); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index fc4177004b..3e595557f9 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -819,6 +819,12 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem if (r_translation_remapped) { *r_translation_remapped = true; } + + // Fallback to p_path if new_path does not exist. + if (!FileAccess::exists(new_path)) { + WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path)); + new_path = p_path; + } } if (path_remaps.has(new_path)) { diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index bb8bf9f569..226076029b 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -217,34 +217,48 @@ Transform2D Transform2D::operator*(const Transform2D &p_transform) const { return t; } -Transform2D Transform2D::scaled(const Size2 &p_scale) const { +Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { Transform2D copy = *this; - copy.scale(p_scale); + copy.scale_basis(p_scale); return copy; } -Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { +Transform2D Transform2D::scaled(const Size2 &p_scale) const { + // Equivalent to left multiplication Transform2D copy = *this; - copy.scale_basis(p_scale); + copy.scale(p_scale); return copy; } +Transform2D Transform2D::scaled_local(const Size2 &p_scale) const { + // Equivalent to right multiplication + return Transform2D(columns[0] * p_scale.x, columns[1] * p_scale.y, columns[2]); +} + Transform2D Transform2D::untranslated() const { Transform2D copy = *this; copy.columns[2] = Vector2(); return copy; } +Transform2D Transform2D::translated(const Vector2 &p_offset) const { + // Equivalent to left multiplication + return Transform2D(columns[0], columns[1], columns[2] + p_offset); +} + Transform2D Transform2D::translated_local(const Vector2 &p_offset) const { - Transform2D copy = *this; - copy.translate_local(p_offset); - return copy; + // Equivalent to right multiplication + return Transform2D(columns[0], columns[1], columns[2] + basis_xform(p_offset)); } Transform2D Transform2D::rotated(const real_t p_angle) const { - Transform2D copy = *this; - copy.rotate(p_angle); - return copy; + // Equivalent to left multiplication + return Transform2D(p_angle, Vector2()) * (*this); +} + +Transform2D Transform2D::rotated_local(const real_t p_angle) const { + // Equivalent to right multiplication + return (*this) * Transform2D(p_angle, Vector2()); // Could be optimized, because origin transform can be skipped. } real_t Transform2D::basis_determinant() const { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index e64d050f0c..f23f32867a 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -85,10 +85,13 @@ struct _NO_DISCARD_ Transform2D { _FORCE_INLINE_ const Vector2 &get_origin() const { return columns[2]; } _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { columns[2] = p_origin; } - Transform2D scaled(const Size2 &p_scale) const; Transform2D basis_scaled(const Size2 &p_scale) const; + Transform2D scaled(const Size2 &p_scale) const; + Transform2D scaled_local(const Size2 &p_scale) const; + Transform2D translated(const Vector2 &p_offset) const; Transform2D translated_local(const Vector2 &p_offset) const; Transform2D rotated(const real_t p_angle) const; + Transform2D rotated_local(const real_t p_angle) const; Transform2D untranslated() const; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index c497a276f3..a634faca9a 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -62,7 +62,15 @@ void Transform3D::rotate(const Vector3 &p_axis, real_t p_angle) { } Transform3D Transform3D::rotated(const Vector3 &p_axis, real_t p_angle) const { - return Transform3D(Basis(p_axis, p_angle), Vector3()) * (*this); + // Equivalent to left multiplication + Basis p_basis(p_axis, p_angle); + return Transform3D(p_basis * basis, p_basis.xform(origin)); +} + +Transform3D Transform3D::rotated_local(const Vector3 &p_axis, real_t p_angle) const { + // Equivalent to right multiplication + Basis p_basis(p_axis, p_angle); + return Transform3D(basis * p_basis, origin); } void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_angle) { @@ -120,9 +128,13 @@ void Transform3D::scale(const Vector3 &p_scale) { } Transform3D Transform3D::scaled(const Vector3 &p_scale) const { - Transform3D t = *this; - t.scale(p_scale); - return t; + // Equivalent to left multiplication + return Transform3D(basis.scaled(p_scale), origin * p_scale); +} + +Transform3D Transform3D::scaled_local(const Vector3 &p_scale) const { + // Equivalent to right multiplication + return Transform3D(basis.scaled_local(p_scale), origin); } void Transform3D::scale_basis(const Vector3 &p_scale) { @@ -139,10 +151,14 @@ void Transform3D::translate_local(const Vector3 &p_translation) { } } +Transform3D Transform3D::translated(const Vector3 &p_translation) const { + // Equivalent to left multiplication + return Transform3D(basis, origin + p_translation); +} + Transform3D Transform3D::translated_local(const Vector3 &p_translation) const { - Transform3D t = *this; - t.translate_local(p_translation); - return t; + // Equivalent to right multiplication + return Transform3D(basis, origin + basis.xform(p_translation)); } void Transform3D::orthonormalize() { diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index 1f8026043f..b572e90859 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -46,6 +46,7 @@ struct _NO_DISCARD_ Transform3D { Transform3D affine_inverse() const; Transform3D rotated(const Vector3 &p_axis, real_t p_angle) const; + Transform3D rotated_local(const Vector3 &p_axis, real_t p_angle) const; void rotate(const Vector3 &p_axis, real_t p_angle); void rotate_basis(const Vector3 &p_axis, real_t p_angle); @@ -55,9 +56,11 @@ struct _NO_DISCARD_ Transform3D { void scale(const Vector3 &p_scale); Transform3D scaled(const Vector3 &p_scale) const; + Transform3D scaled_local(const Vector3 &p_scale) const; void scale_basis(const Vector3 &p_scale); void translate_local(real_t p_tx, real_t p_ty, real_t p_tz); void translate_local(const Vector3 &p_translation); + Transform3D translated(const Vector3 &p_translation) const; Transform3D translated_local(const Vector3 &p_translation) const; const Basis &get_basis() const { return basis; } diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index d67315f20d..9790cc44e3 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -963,8 +963,11 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro ERR_FAIL_COND(!type->property_map.has(p_property)); ERR_FAIL_COND(!type->property_map.has(p_linked_property)); - PropertyInfo &pinfo = type->property_map[p_property]; - pinfo.linked_properties.push_back(p_linked_property); + if (!type->linked_properties.has(p_property)) { + type->linked_properties.insert(p_property, List<StringName>()); + } + type->linked_properties[p_property].push_back(p_linked_property); + #endif } @@ -992,6 +995,25 @@ void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p } } +void ClassDB::get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance) { +#ifdef TOOLS_ENABLED + ClassInfo *check = classes.getptr(p_class); + while (check) { + if (!check->linked_properties.has(p_property)) { + return; + } + for (const StringName &E : check->linked_properties[p_property]) { + r_properties->push_back(E); + } + + if (p_no_inheritance) { + break; + } + check = check->inherits_ptr; + } +#endif +} + bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index 8b6a260d86..5fba52e23e 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -120,6 +120,7 @@ public: List<MethodInfo> virtual_methods; HashMap<StringName, MethodInfo> virtual_methods_map; HashMap<StringName, Vector<Error>> method_error_values; + HashMap<StringName, List<StringName>> linked_properties; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -312,6 +313,7 @@ public: static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property); static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static void get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance = false); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); diff --git a/core/object/object.h b/core/object/object.h index 649f42c9a0..35d0aaaa7d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -154,9 +154,7 @@ struct PropertyInfo { String hint_string; uint32_t usage = PROPERTY_USAGE_DEFAULT; -#ifdef TOOLS_ENABLED - Vector<String> linked_properties; -#endif + // If you are thinking about adding another member to this class, ask the maintainer (Juan) first. _FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const { PropertyInfo pi = *this; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index b933a90a48..a774aac52f 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1882,7 +1882,10 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, get_skew, sarray(), varray()); bind_method(Transform2D, orthonormalized, sarray(), varray()); bind_method(Transform2D, rotated, sarray("angle"), varray()); + bind_method(Transform2D, rotated_local, sarray("angle"), varray()); bind_method(Transform2D, scaled, sarray("scale"), varray()); + bind_method(Transform2D, scaled_local, sarray("scale"), varray()); + bind_method(Transform2D, translated, sarray("offset"), varray()); bind_method(Transform2D, translated_local, sarray("offset"), varray()); bind_method(Transform2D, basis_xform, sarray("v"), varray()); bind_method(Transform2D, basis_xform_inv, sarray("v"), varray()); @@ -1947,7 +1950,10 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, affine_inverse, sarray(), varray()); bind_method(Transform3D, orthonormalized, sarray(), varray()); bind_method(Transform3D, rotated, sarray("axis", "angle"), varray()); + bind_method(Transform3D, rotated_local, sarray("axis", "angle"), varray()); bind_method(Transform3D, scaled, sarray("scale"), varray()); + bind_method(Transform3D, scaled_local, sarray("scale"), varray()); + bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, translated_local, sarray("offset"), varray()); bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); bind_method(Transform3D, spherical_interpolate_with, sarray("xform", "weight"), varray()); diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index dacdca1cee..b0282e4107 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -192,8 +192,8 @@ </member> <member name="linear_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> </member> - <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="true"> - If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. + <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="false"> + If [code]true[/code], particles use the parent node's coordinate space (known as local coordinates). This will cause particles to move and rotate along the [CPUParticles2D] node (and its parents) when it is moved or rotated. If [code]false[/code], particles use global coordinates; they will not move or rotate along the [CPUParticles2D] node (and its parents) when it is moved or rotated. </member> <member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot" default="false"> If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index f2a0040ed4..d8faf8e91d 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -224,8 +224,8 @@ <member name="linear_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> Minimum linear acceleration. </member> - <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="true"> - If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. + <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="false"> + If [code]true[/code], particles use the parent node's coordinate space (known as local coordinates). This will cause particles to move and rotate along the [CPUParticles3D] node (and its parents) when it is moved or rotated. If [code]false[/code], particles use global coordinates; they will not move or rotate along the [CPUParticles3D] node (and its parents) when it is moved or rotated. </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> The [Mesh] used for each particle. If [code]null[/code], particles will be spheres. diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 3aedbbd1e6..5595abc02a 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -113,7 +113,7 @@ <argument index="2" name="z_near" type="float" /> <argument index="3" name="z_far" type="float" /> <description> - Sets the camera projection to frustum mode (see [constant PROJECTION_FRUSTUM]), by specifying a [code]size[/code], an [code]offset[/code], and the [code]z_near[/code] and [code]z_far[/code] clip planes in world space units. + Sets the camera projection to frustum mode (see [constant PROJECTION_FRUSTUM]), by specifying a [code]size[/code], an [code]offset[/code], and the [code]z_near[/code] and [code]z_far[/code] clip planes in world space units. See also [member frustum_offset]. </description> </method> <method name="set_orthogonal"> @@ -179,6 +179,7 @@ </member> <member name="frustum_offset" type="Vector2" setter="set_frustum_offset" getter="get_frustum_offset" default="Vector2(0, 0)"> The camera's frustum offset. This can be changed from the default to create "tilted frustum" effects such as [url=https://zdoom.org/wiki/Y-shearing]Y-shearing[/url]. + [b]Note:[/b] Only effective if [member projection] is [constant PROJECTION_FRUSTUM]. </member> <member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset" default="0.0"> The horizontal (X) offset of the camera viewport. diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index cec504584c..d5db7da1f0 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1049,7 +1049,7 @@ <return type="int" enum="DisplayServer.VSyncMode" /> <argument index="0" name="window_id" type="int" default="0" /> <description> - Returns the VSync mode of the given window. + Returns the V-Sync mode of the given window. </description> </method> <method name="window_move_to_foreground"> @@ -1234,7 +1234,7 @@ <argument index="0" name="vsync_mode" type="int" enum="DisplayServer.VSyncMode" /> <argument index="1" name="window_id" type="int" default="0" /> <description> - Sets the VSync mode of the given window. + Sets the V-Sync mode of the given window. See [enum DisplayServer.VSyncMode] for possible values and how they affect the behavior of your application. Depending on the platform and used renderer, the engine will fall back to [constant VSYNC_ENABLED], if the desired mode is not supported. </description> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 687c3d70ca..fea4085019 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -347,7 +347,7 @@ </member> <member name="editors/visual_editors/minimap_opacity" type="float" setter="" getter=""> </member> - <member name="editors/visual_editors/visualshader/port_preview_size" type="int" setter="" getter=""> + <member name="editors/visual_editors/visual_shader/port_preview_size" type="int" setter="" getter=""> </member> <member name="filesystem/directories/autoscan_project_path" type="String" setter="" getter=""> </member> diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index 53894bad87..e60ab094c6 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -63,8 +63,8 @@ <member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime" default="1.0"> Amount of time each particle will exist. </member> - <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="true"> - If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. + <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="false"> + If [code]true[/code], particles use the parent node's coordinate space (known as local coordinates). This will cause particles to move and rotate along the [GPUParticles2D] node (and its parents) when it is moved or rotated. If [code]false[/code], particles use global coordinates; they will not move or rotate along the [GPUParticles2D] node (and its parents) when it is moved or rotated. </member> <member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot" default="false"> If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index c4bd18db69..b415c56154 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -95,8 +95,8 @@ <member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime" default="1.0"> Amount of time each particle will exist. </member> - <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="true"> - If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. + <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="false"> + If [code]true[/code], particles use the parent node's coordinate space (known as local coordinates). This will cause particles to move and rotate along the [GPUParticles3D] node (and its parents) when it is moved or rotated. If [code]false[/code], particles use global coordinates; they will not move or rotate along the [GPUParticles3D] node (and its parents) when it is moved or rotated. </member> <member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot" default="false"> If [code]true[/code], only [code]amount[/code] particles will be emitted. diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index 195c481187..a4cfa1ddff 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -188,6 +188,16 @@ Returns if [code]point[/code] is inside the triangle specified by [code]a[/code], [code]b[/code] and [code]c[/code]. </description> </method> + <method name="segment_intersects_circle"> + <return type="float" /> + <argument index="0" name="segment_from" type="Vector2" /> + <argument index="1" name="segment_to" type="Vector2" /> + <argument index="2" name="circle_position" type="Vector2" /> + <argument index="3" name="circle_radius" type="float" /> + <description> + Given the 2D segment ([code]segment_from[/code], [code]segment_to[/code]), returns the position on the segment (as a number between 0 and 1) at which the segment hits the circle that is located at position [code]circle_position[/code] and has radius [code]circle_radius[/code]. If the segment does not intersect the circle, -1 is returned (this is also the case if the line extending the segment would intersect the circle, but the segment does not). + </description> + </method> <method name="segment_intersects_segment"> <return type="Variant" /> <argument index="0" name="from_a" type="Vector2" /> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index a7b1f0ea33..737662fe69 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -194,6 +194,10 @@ <members> <member name="action_mode" type="int" setter="set_action_mode" getter="get_action_mode" overrides="BaseButton" enum="BaseButton.ActionMode" default="0" /> <member name="alignment" type="int" setter="set_text_alignment" getter="get_text_alignment" overrides="Button" enum="HorizontalAlignment" default="0" /> + <member name="fit_to_longest_item" type="bool" setter="set_fit_to_longest_item" getter="is_fit_to_longest_item" default="true"> + If [code]true[/code], minimum size will be determined by the longest item's text, instead of the currently selected one's. + [b]Note:[/b] For performance reasons, the minimum size doesn't update immediately when adding, removing or modifying items. + </member> <member name="item_count" type="int" setter="set_item_count" getter="get_item_count" default="0"> The number of items to select from. </member> diff --git a/doc/classes/PhysicsRayQueryParameters2D.xml b/doc/classes/PhysicsRayQueryParameters2D.xml index 36933ef9a2..1cfc6caadf 100644 --- a/doc/classes/PhysicsRayQueryParameters2D.xml +++ b/doc/classes/PhysicsRayQueryParameters2D.xml @@ -8,6 +8,22 @@ </description> <tutorials> </tutorials> + <methods> + <method name="create" qualifiers="static"> + <return type="PhysicsRayQueryParameters2D" /> + <argument index="0" name="from" type="Vector2" /> + <argument index="1" name="to" type="Vector2" /> + <argument index="2" name="collision_mask" type="int" default="4294967295" /> + <argument index="3" name="exclude" type="Array" default="[]" /> + <description> + Returns a new, pre-configured [PhysicsRayQueryParameters2D] object. Use it to quickly create query parameters using the most common options. + [codeblock] + var query = PhysicsRayQueryParameters2D.create(global_position, global_position + Vector2(0, 100)) + var collision = get_world_2d().direct_space_state.intersect_ray(query) + [/codeblock] + </description> + </method> + </methods> <members> <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false"> If [code]true[/code], the query will take [Area2D]s into account. diff --git a/doc/classes/PhysicsRayQueryParameters3D.xml b/doc/classes/PhysicsRayQueryParameters3D.xml index 4244ec785f..e9216a8300 100644 --- a/doc/classes/PhysicsRayQueryParameters3D.xml +++ b/doc/classes/PhysicsRayQueryParameters3D.xml @@ -8,6 +8,22 @@ </description> <tutorials> </tutorials> + <methods> + <method name="create" qualifiers="static"> + <return type="PhysicsRayQueryParameters3D" /> + <argument index="0" name="from" type="Vector3" /> + <argument index="1" name="to" type="Vector3" /> + <argument index="2" name="collision_mask" type="int" default="4294967295" /> + <argument index="3" name="exclude" type="Array" default="[]" /> + <description> + Returns a new, pre-configured [PhysicsRayQueryParameters3D] object. Use it to quickly create query parameters using the most common options. + [codeblock] + var query = PhysicsRayQueryParameters3D.create(position, position + Vector3(0, -10, 0)) + var collision = get_world_3d().direct_space_state.intersect_ray(query) + [/codeblock] + </description> + </method> + </methods> <members> <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false"> If [code]true[/code], the query will take [Area3D]s into account. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b1e3d2f628..40477d27d4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -588,7 +588,7 @@ [b]Note:[/b] By default, or when set to 0, the initial window width is the viewport [member display/window/size/viewport_width]. This setting is ignored on iOS, Android, and HTML5. </member> <member name="display/window/vsync/vsync_mode" type="int" setter="" getter="" default="1"> - Sets the VSync mode for the main game window. + Sets the V-Sync mode for the main game window. See [enum DisplayServer.VSyncMode] for possible values and how they affect the behavior of your application. Depending on the platform and used renderer, the engine will fall back to [code]Enabled[/code], if the desired mode is not supported. </member> @@ -1962,9 +1962,11 @@ Lower-end override for [member rendering/shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. </member> <member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3"> + The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level]. </member> <member name="rendering/textures/default_filters/anisotropic_filtering_level" type="int" setter="" getter="" default="2"> Sets the maximum number of samples to take when using anisotropic filtering on textures (as a power of two). A higher sample count will result in sharper textures at oblique angles, but is more expensive to compute. A value of [code]0[/code] forcibly disables anisotropic filtering, even on materials where it is enabled. + The anisotropic filtering level also affects decals and light projectors if they are configured to use anisotropic filtering. See [member rendering/textures/decals/filter] and [member rendering/textures/light_projectors/filter]. [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time. </member> <member name="rendering/textures/default_filters/texture_mipmap_bias" type="float" setter="" getter="" default="0.0"> @@ -1977,6 +1979,7 @@ [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time. </member> <member name="rendering/textures/light_projectors/filter" type="int" setter="" getter="" default="3"> + The filtering quality to use for [OmniLight3D] and [SpotLight3D] projectors. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level]. </member> <member name="rendering/textures/lossless_compression/force_png" type="bool" setter="" getter="" default="false"> If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 9616ab3515..9a398b1f33 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3747,14 +3747,22 @@ Use [Transform3D] to store MultiMesh transform. </constant> <constant name="LIGHT_PROJECTOR_FILTER_NEAREST" value="0" enum="LightProjectorFilter"> + Nearest-neighbor filter for light projectors (use for pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps. </constant> - <constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS" value="1" enum="LightProjectorFilter"> + <constant name="LIGHT_PROJECTOR_FILTER_LINEAR" value="1" enum="LightProjectorFilter"> + Linear filter for light projectors (use for non-pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as using mipmaps. </constant> - <constant name="LIGHT_PROJECTOR_FILTER_LINEAR" value="2" enum="LightProjectorFilter"> + <constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS" value="2" enum="LightProjectorFilter"> + Nearest-neighbor filter for light projectors (use for pixel art light projectors). Isotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps. </constant> <constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS" value="3" enum="LightProjectorFilter"> + Linear filter for light projectors (use for non-pixel art light projectors). Isotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps. </constant> - <constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="4" enum="LightProjectorFilter"> + <constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC" value="4" enum="LightProjectorFilter"> + Nearest-neighbor filter for light projectors (use for pixel art light projectors). Anisotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + </constant> + <constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="5" enum="LightProjectorFilter"> + Linear filter for light projectors (use for non-pixel art light projectors). Anisotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. </constant> <constant name="LIGHT_DIRECTIONAL" value="0" enum="LightType"> Is a directional (sun) light. @@ -3896,14 +3904,22 @@ <constant name="DECAL_TEXTURE_MAX" value="4" enum="DecalTexture"> </constant> <constant name="DECAL_FILTER_NEAREST" value="0" enum="DecalFilter"> + Nearest-neighbor filter for decals (use for pixel art decals). No mipmaps are used for rendering, which means decals at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps. </constant> - <constant name="DECAL_FILTER_NEAREST_MIPMAPS" value="1" enum="DecalFilter"> + <constant name="DECAL_FILTER_LINEAR" value="1" enum="DecalFilter"> + Linear filter for decals (use for non-pixel art decals). No mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as using mipmaps. </constant> - <constant name="DECAL_FILTER_LINEAR" value="2" enum="DecalFilter"> + <constant name="DECAL_FILTER_NEAREST_MIPMAPS" value="2" enum="DecalFilter"> + Nearest-neighbor filter for decals (use for pixel art decals). Isotropic mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps. </constant> <constant name="DECAL_FILTER_LINEAR_MIPMAPS" value="3" enum="DecalFilter"> + Linear filter for decals (use for non-pixel art decals). Isotropic mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps. + </constant> + <constant name="DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC" value="4" enum="DecalFilter"> + Nearest-neighbor filter for decals (use for pixel art decals). Anisotropic mipmaps are used for rendering, which means decals at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. </constant> - <constant name="DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="4" enum="DecalFilter"> + <constant name="DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="5" enum="DecalFilter"> + Linear filter for decals (use for non-pixel art decals). Anisotropic mipmaps are used for rendering, which means decals at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. </constant> <constant name="VOXEL_GI_QUALITY_LOW" value="0" enum="VoxelGIQuality"> </constant> diff --git a/doc/classes/RootMotionView.xml b/doc/classes/RootMotionView.xml index 88b8f2cd03..3f3b00e2cb 100644 --- a/doc/classes/RootMotionView.xml +++ b/doc/classes/RootMotionView.xml @@ -5,25 +5,25 @@ </brief_description> <description> [i]Root motion[/i] refers to an animation technique where a mesh's skeleton is used to give impulse to a character. When working with 3D animations, a popular technique is for animators to use the root skeleton bone to give motion to the rest of the skeleton. This allows animating characters in a way where steps actually match the floor below. It also allows precise interaction with objects during cinematics. See also [AnimationTree]. - [b]Note:[/b] [RootMotionView] is only visible in the editor. It will be hidden automatically in the running project, and will also be converted to a plain [Node] in the running project. This means a script attached to a [RootMotionView] node [i]must[/i] have [code]extends Node[/code] instead of [code]extends RootMotionView[/code]. Additionally, it must not be a [code]@tool[/code] script. + [b]Note:[/b] [RootMotionView] is only visible in the editor. It will be hidden automatically in the running project. </description> <tutorials> <link title="Using AnimationTree - Root motion">$DOCS_URL/tutorials/animation/animation_tree.html#root-motion</link> </tutorials> <members> - <member name="animation_path" type="NodePath" setter="set_animation_path" getter="get_animation_path"> + <member name="animation_path" type="NodePath" setter="set_animation_path" getter="get_animation_path" default="NodePath("")"> Path to an [AnimationTree] node to use as a basis for root motion. </member> - <member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size"> + <member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="1.0"> The grid's cell size in 3D units. </member> - <member name="color" type="Color" setter="set_color" getter="get_color"> + <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0.5, 0.5, 1, 1)"> The grid's color. </member> - <member name="radius" type="float" setter="set_radius" getter="get_radius"> + <member name="radius" type="float" setter="set_radius" getter="get_radius" default="10.0"> The grid's radius in 3D units. The grid's opacity will fade gradually as the distance from the origin increases until this [member radius] is reached. </member> - <member name="zero_y" type="bool" setter="set_zero_y" getter="get_zero_y"> + <member name="zero_y" type="bool" setter="set_zero_y" getter="get_zero_y" default="true"> If [code]true[/code], the grid's points will all be on the same Y coordinate ([i]local[/i] Y = 0). If [code]false[/code], the points' original Y coordinate is preserved. </member> </members> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 924b4cd8e4..9979a73527 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -141,14 +141,40 @@ <return type="Transform2D" /> <argument index="0" name="angle" type="float" /> <description> - Returns a copy of the transform rotated by the given [code]angle[/code] (in radians), using matrix multiplication. + Returns a copy of the transform rotated by the given [code]angle[/code] (in radians). + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> + <method name="rotated_local" qualifiers="const"> + <return type="Transform2D" /> + <argument index="0" name="angle" type="float" /> + <description> + Returns a copy of the transform rotated by the given [code]angle[/code] (in radians). + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding rotation transform [code]R[/code] from the right, i.e., [code]X * R[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> <method name="scaled" qualifiers="const"> <return type="Transform2D" /> <argument index="0" name="scale" type="Vector2" /> <description> - Returns a copy of the transform scaled by the given [code]scale[/code] factor, using matrix multiplication. + Returns a copy of the transform scaled by the given [code]scale[/code] factor. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding scaling transform [code]S[/code] from the left, i.e., [code]S * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> + <method name="scaled_local" qualifiers="const"> + <return type="Transform2D" /> + <argument index="0" name="scale" type="Vector2" /> + <description> + Returns a copy of the transform scaled by the given [code]scale[/code] factor. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding scaling transform [code]S[/code] from the right, i.e., [code]X * S[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> <method name="set_rotation"> @@ -173,12 +199,24 @@ Sets the transform's skew (in radians). </description> </method> + <method name="translated" qualifiers="const"> + <return type="Transform2D" /> + <argument index="0" name="offset" type="Vector2" /> + <description> + Returns a copy of the transform translated by the given [code]offset[/code]. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding translation transform [code]T[/code] from the left, i.e., [code]T * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> <method name="translated_local" qualifiers="const"> <return type="Transform2D" /> <argument index="0" name="offset" type="Vector2" /> <description> - Returns a copy of the transform translated by the given [code]offset[/code], relative to the transform's basis vectors. - Unlike [method rotated] and [method scaled], this does not use matrix multiplication. + Returns a copy of the transform translated by the given [code]offset[/code]. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding translation transform [code]T[/code] from the right, i.e., [code]X * T[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> </methods> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index de1db718c2..9b673701ae 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -102,14 +102,43 @@ <argument index="0" name="axis" type="Vector3" /> <argument index="1" name="angle" type="float" /> <description> - Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians), using matrix multiplication. The [code]axis[/code] must be a normalized vector. + Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians). + The [code]axis[/code] must be a normalized vector. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> + <method name="rotated_local" qualifiers="const"> + <return type="Transform3D" /> + <argument index="0" name="axis" type="Vector3" /> + <argument index="1" name="angle" type="float" /> + <description> + Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians). + The [code]axis[/code] must be a normalized vector. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding rotation transform [code]R[/code] from the right, i.e., [code]X * R[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> <method name="scaled" qualifiers="const"> <return type="Transform3D" /> <argument index="0" name="scale" type="Vector3" /> <description> - Returns a copy of the transform with its basis and origin scaled by the given [code]scale[/code] factor, using matrix multiplication. + Returns a copy of the transform scaled by the given [code]scale[/code] factor. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding scaling transform [code]S[/code] from the left, i.e., [code]S * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> + <method name="scaled_local" qualifiers="const"> + <return type="Transform3D" /> + <argument index="0" name="scale" type="Vector3" /> + <description> + Returns a copy of the transform scaled by the given [code]scale[/code] factor. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding scaling transform [code]S[/code] from the right, i.e., [code]X * S[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> <method name="spherical_interpolate_with" qualifiers="const"> @@ -120,12 +149,24 @@ Returns a transform spherically interpolated between this transform and another by a given [code]weight[/code] (on the range of 0.0 to 1.0). </description> </method> + <method name="translated" qualifiers="const"> + <return type="Transform3D" /> + <argument index="0" name="offset" type="Vector3" /> + <description> + Returns a copy of the transform translated by the given [code]offset[/code]. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding translation transform [code]T[/code] from the left, i.e., [code]T * X[/code]. + This can be seen as transforming with respect to the global/parent frame. + </description> + </method> <method name="translated_local" qualifiers="const"> <return type="Transform3D" /> <argument index="0" name="offset" type="Vector3" /> <description> - Returns a copy of the transform translated by the given [code]offset[/code], relative to the transform's basis vectors. - Unlike [method rotated] and [method scaled], this does not use matrix multiplication. + Returns a copy of the transform translated by the given [code]offset[/code]. + This method is an optimized version of multiplying the given transform [code]X[/code] + with a corresponding translation transform [code]T[/code] from the right, i.e., [code]X * T[/code]. + This can be seen as transforming with respect to the local frame. </description> </method> </methods> diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 0d8a3310fd..a9a8ce68ac 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1666,7 +1666,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { if (present_mode_available) { window->presentMode = requested_present_mode; } else { - WARN_PRINT("Requested VSync mode is not available!"); + String present_mode_string; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + present_mode_string = "Mailbox"; + break; + case DisplayServer::VSYNC_ADAPTIVE: + present_mode_string = "Adaptive"; + break; + case DisplayServer::VSYNC_ENABLED: + present_mode_string = "Enabled"; + break; + case DisplayServer::VSYNC_DISABLED: + present_mode_string = "Disabled"; + break; + } + WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_string)); window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default. } @@ -2471,12 +2486,12 @@ String VulkanContext::get_device_pipeline_cache_uuid() const { } DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { - ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); return windows[p_window].vsync_mode; } void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { - ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); windows[p_window].vsync_mode = p_mode; _update_swap_chain(&windows[p_window]); } diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 5b4da0e32b..43961a7ceb 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -417,6 +417,45 @@ void DependencyRemoveDialog::_find_all_removed_dependencies(EditorFileSystemDire } } +void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<RemovedDependency> &p_removed) { + for (KeyValue<String, String> &files : all_remove_files) { + const String &path = files.key; + + // Look for dependencies in the translation remaps. + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); + + if (remaps.has(path)) { + RemovedDependency dep; + dep.file = TTR("Localization remap"); + dep.file_type = ""; + dep.dependency = path; + dep.dependency_folder = files.value; + p_removed.push_back(dep); + } + + Array remap_keys = remaps.keys(); + for (int j = 0; j < remap_keys.size(); j++) { + PackedStringArray remapped_files = remaps[remap_keys[j]]; + for (int k = 0; k < remapped_files.size(); k++) { + int splitter_pos = remapped_files[k].rfind(":"); + String res_path = remapped_files[k].substr(0, splitter_pos); + if (res_path == path) { + String locale_name = remapped_files[k].substr(splitter_pos + 1); + + RemovedDependency dep; + dep.file = vformat(TTR("Localization remap for path '%s' and locale '%s'."), remap_keys[j], locale_name); + dep.file_type = ""; + dep.dependency = path; + dep.dependency_folder = files.value; + p_removed.push_back(dep); + } + } + } + } + } +} + void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed) { owners->clear(); owners->create_item(); // root @@ -473,6 +512,7 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector< Vector<RemovedDependency> removed_deps; _find_all_removed_dependencies(EditorFileSystem::get_singleton()->get_filesystem(), removed_deps); + _find_localization_remaps_of_removed_files(removed_deps); removed_deps.sort(); if (removed_deps.is_empty()) { owners->hide(); diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index 96d82d58eb..6e39015ec3 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -119,6 +119,7 @@ class DependencyRemoveDialog : public ConfirmationDialog { void _find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder); void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed); + void _find_localization_remaps_of_removed_files(Vector<RemovedDependency> &p_removed); void _build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed); void ok_pressed() override; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index c21d69a982..13506718ec 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1677,7 +1677,7 @@ void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::RIGHT) { + if (movable && mb->get_button_index() == MouseButton::RIGHT) { popup_array_index_pressed = begin_array_index + p_index; rmb_popup->set_item_disabled(OPTION_MOVE_UP, popup_array_index_pressed == 0); rmb_popup->set_item_disabled(OPTION_MOVE_DOWN, popup_array_index_pressed == count - 1); @@ -1712,43 +1712,112 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { } } else if (mode == MODE_USE_COUNT_PROPERTY) { ERR_FAIL_COND(p_to_pos < -1 || p_to_pos > count); - List<PropertyInfo> object_property_list; - object->get_property_list(&object_property_list); - Array properties_as_array = _extract_properties_as_array(object_property_list); - properties_as_array.resize(count); + if (!swap_method.is_empty()) { + ERR_FAIL_COND(!object->has_method(swap_method)); - // For undoing things - undo_redo->add_undo_property(object, count_property, properties_as_array.size()); - for (int i = 0; i < (int)properties_as_array.size(); i++) { - Dictionary d = Dictionary(properties_as_array[i]); - Array keys = d.keys(); - for (int j = 0; j < keys.size(); j++) { - String key = keys[j]; - undo_redo->add_undo_property(object, vformat(key, i), d[key]); - } - } + // Swap method was provided, use it. + if (p_element_index < 0) { + // Add an element at position + undo_redo->add_do_property(object, count_property, count + 1); + if (p_to_pos >= 0) { + for (int i = count; i > p_to_pos; i--) { + undo_redo->add_do_method(object, swap_method, i, i - 1); + } + for (int i = p_to_pos; i < count; i++) { + undo_redo->add_undo_method(object, swap_method, i, i + 1); + } + } + undo_redo->add_undo_property(object, count_property, count); + + } else if (p_to_pos < 0) { + if (count > 0) { + // Remove element at position + undo_redo->add_undo_property(object, count_property, count); + + List<PropertyInfo> object_property_list; + object->get_property_list(&object_property_list); + + for (int i = p_element_index; i < count - 1; i++) { + undo_redo->add_do_method(object, swap_method, i, i + 1); + } + + for (int i = count; i > p_element_index; i--) { + undo_redo->add_undo_method(object, swap_method, i, i - 1); + } + + String erase_prefix = String(array_element_prefix) + itos(p_element_index); + + for (const PropertyInfo &E : object_property_list) { + if (E.name.begins_with(erase_prefix)) { + undo_redo->add_undo_property(object, E.name, object->get(E.name)); + } + } - if (p_element_index < 0) { - // Add an element. - properties_as_array.insert(p_to_pos < 0 ? properties_as_array.size() : p_to_pos, Dictionary()); - } else if (p_to_pos < 0) { - // Delete the element. - properties_as_array.remove_at(p_element_index); + undo_redo->add_do_property(object, count_property, count - 1); + } + } else { + if (p_to_pos > p_element_index) { + p_to_pos--; + } + + if (p_to_pos < p_element_index) { + for (int i = p_element_index; i > p_to_pos; i--) { + undo_redo->add_do_method(object, swap_method, i, i - 1); + } + for (int i = p_to_pos; i < p_element_index; i++) { + undo_redo->add_undo_method(object, swap_method, i, i + 1); + } + } else if (p_to_pos > p_element_index) { + for (int i = p_element_index; i < p_to_pos; i++) { + undo_redo->add_do_method(object, swap_method, i, i + 1); + } + + for (int i = p_to_pos; i > p_element_index; i--) { + undo_redo->add_undo_method(object, swap_method, i, i - 1); + } + } + } } else { - // Move the element. - properties_as_array.insert(p_to_pos, properties_as_array[p_element_index].duplicate()); - properties_as_array.remove_at(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); - } + // Use standard properties. + List<PropertyInfo> object_property_list; + object->get_property_list(&object_property_list); - // Change the array size then set the properties. - undo_redo->add_do_property(object, count_property, properties_as_array.size()); - for (int i = 0; i < (int)properties_as_array.size(); i++) { - Dictionary d = properties_as_array[i]; - Array keys = d.keys(); - for (int j = 0; j < keys.size(); j++) { - String key = keys[j]; - undo_redo->add_do_property(object, vformat(key, i), d[key]); + Array properties_as_array = _extract_properties_as_array(object_property_list); + properties_as_array.resize(count); + + // For undoing things + undo_redo->add_undo_property(object, count_property, properties_as_array.size()); + for (int i = 0; i < (int)properties_as_array.size(); i++) { + Dictionary d = Dictionary(properties_as_array[i]); + Array keys = d.keys(); + for (int j = 0; j < keys.size(); j++) { + String key = keys[j]; + undo_redo->add_undo_property(object, vformat(key, i), d[key]); + } + } + + if (p_element_index < 0) { + // Add an element. + properties_as_array.insert(p_to_pos < 0 ? properties_as_array.size() : p_to_pos, Dictionary()); + } else if (p_to_pos < 0) { + // Delete the element. + properties_as_array.remove_at(p_element_index); + } else { + // Move the element. + properties_as_array.insert(p_to_pos, properties_as_array[p_element_index].duplicate()); + properties_as_array.remove_at(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); + } + + // Change the array size then set the properties. + undo_redo->add_do_property(object, count_property, properties_as_array.size()); + for (int i = 0; i < (int)properties_as_array.size(); i++) { + Dictionary d = properties_as_array[i]; + Array keys = d.keys(); + for (int j = 0; j < keys.size(); j++) { + String key = keys[j]; + undo_redo->add_do_property(object, vformat(key, i), d[key]); + } } } } @@ -1988,6 +2057,20 @@ void EditorInspectorArray::_setup() { page = CLAMP(page, 0, max_page); } + Ref<Font> numbers_font; + int numbers_min_w = 0; + + if (numbered) { + numbers_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int digits_found = count; + String test; + while (digits_found) { + test += "8"; + digits_found /= 10; + } + numbers_min_w = numbers_font->get_string_size(test).width; + } + for (int i = 0; i < (int)array_elements.size(); i++) { ArrayElement &ae = array_elements[i]; @@ -2022,19 +2105,38 @@ void EditorInspectorArray::_setup() { ae.margin->add_child(ae.hbox); // Move button. - ae.move_texture_rect = memnew(TextureRect); - ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE); - if (is_inside_tree()) { - ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); + if (movable) { + ae.move_texture_rect = memnew(TextureRect); + ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE); + + if (is_inside_tree()) { + ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); + } + ae.hbox->add_child(ae.move_texture_rect); + } + + if (numbered) { + ae.number = memnew(Label); + ae.number->add_theme_font_override("font", numbers_font); + ae.number->set_custom_minimum_size(Size2(numbers_min_w, 0)); + ae.number->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + ae.number->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); + ae.number->set_text(itos(begin_array_index + i)); + ae.hbox->add_child(ae.number); } - ae.hbox->add_child(ae.move_texture_rect); // Right vbox. ae.vbox = memnew(VBoxContainer); ae.vbox->set_h_size_flags(SIZE_EXPAND_FILL); ae.vbox->set_v_size_flags(SIZE_EXPAND_FILL); ae.hbox->add_child(ae.vbox); + + ae.erase = memnew(Button); + ae.erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + ae.erase->set_v_size_flags(SIZE_SHRINK_CENTER); + ae.erase->connect("pressed", callable_mp(this, &EditorInspectorArray::_remove_item).bind(begin_array_index + i)); + ae.hbox->add_child(ae.erase); } // Hide/show the add button. @@ -2049,7 +2151,14 @@ void EditorInspectorArray::_setup() { } } +void EditorInspectorArray::_remove_item(int p_index) { + _move_element(p_index, -1); +} + Variant EditorInspectorArray::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (!movable) { + return Variant(); + } int index = p_from->get_meta("index"); Dictionary dict; dict["type"] = "property_array_element"; @@ -2071,6 +2180,9 @@ void EditorInspectorArray::drop_data_fw(const Point2 &p_point, const Variant &p_ } bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + if (!movable) { + return false; + } // First, update drawing. control_dropping->update(); @@ -2100,13 +2212,19 @@ void EditorInspectorArray::_notification(int p_what) { for (int i = 0; i < (int)array_elements.size(); i++) { ArrayElement &ae = array_elements[i]; - ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); + if (ae.move_texture_rect) { + ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); + } Size2 min_size = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->get_minimum_size(); ae.margin->add_theme_constant_override("margin_left", min_size.x / 2); ae.margin->add_theme_constant_override("margin_top", min_size.y / 2); ae.margin->add_theme_constant_override("margin_right", min_size.x / 2); ae.margin->add_theme_constant_override("margin_bottom", min_size.y / 2); + + if (ae.erase) { + ae.erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + } } add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -2142,23 +2260,31 @@ void EditorInspectorArray::set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } -void EditorInspectorArray::setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable) { +void EditorInspectorArray::setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable, bool p_numbered, int p_page_length, const String &p_add_item_text) { count_property = ""; mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION; array_element_prefix = p_array_element_prefix; page = p_page; + movable = p_movable; + page_length = p_page_length; + numbered = p_numbered; EditorInspectorSection::setup(String(p_array_element_prefix) + "_array", p_label, p_object, p_bg_color, p_foldable, 0); _setup(); } -void EditorInspectorArray::setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable) { +void EditorInspectorArray::setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable, bool p_numbered, int p_page_length, const String &p_add_item_text, const String &p_swap_method) { count_property = p_count_property; mode = MODE_USE_COUNT_PROPERTY; array_element_prefix = p_array_element_prefix; page = p_page; + movable = p_movable; + page_length = p_page_length; + numbered = p_numbered; + swap_method = p_swap_method; + add_button->set_text(p_add_item_text); EditorInspectorSection::setup(String(count_property) + "_array", p_label, p_object, p_bg_color, p_foldable, 0); _setup(); @@ -2887,9 +3013,35 @@ void EditorInspector::update_tree() { StringName array_element_prefix; Color c = sscolor; c.a /= level; + + Vector<String> class_name_components = String(p.class_name).split(","); + + array_element_prefix = class_name_components[1]; + int page_size = 5; + bool movable = true; + bool numbered = false; + bool foldable = use_folding; + String add_button_text; + String swap_method; + for (int i = (p.type == Variant::NIL ? 1 : 2); i < class_name_components.size(); i++) { + if (class_name_components[i].begins_with("page_size") && class_name_components[i].get_slice_count("=") == 2) { + page_size = class_name_components[i].get_slice("=", 1).to_int(); + } else if (class_name_components[i].begins_with("add_button_text") && class_name_components[i].get_slice_count("=") == 2) { + add_button_text = class_name_components[i].get_slice("=", 1).strip_edges(); + } else if (class_name_components[i] == "static") { + movable = false; + } else if (class_name_components[i] == "numbered") { + numbered = true; + } else if (class_name_components[i] == "unfoldable") { + foldable = false; + } else if (class_name_components[i].begins_with("swap_method") && class_name_components[i].get_slice_count("=") == 2) { + swap_method = class_name_components[i].get_slice("=", 1).strip_edges(); + } + } + if (p.type == Variant::NIL) { // Setup the array to use a method to create/move/delete elements. - array_element_prefix = p.class_name; + array_element_prefix = class_name_components[0]; editor_inspector_array = memnew(EditorInspectorArray); String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; @@ -2900,13 +3052,14 @@ void EditorInspector::update_tree() { editor_inspector_array->set_undo_redo(undo_redo); } else if (p.type == Variant::INT) { // Setup the array to use the count property and built-in functions to create/move/delete elements. - Vector<String> class_name_components = String(p.class_name).split(","); - if (class_name_components.size() == 2) { - array_element_prefix = class_name_components[1]; + + if (class_name_components.size() > 2) { editor_inspector_array = memnew(EditorInspectorArray); int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; - editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, use_folding); + + editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, foldable, movable, numbered, page_size, add_button_text, swap_method); editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix)); + editor_inspector_array->set_undo_redo(undo_redo); } } @@ -2915,6 +3068,7 @@ void EditorInspector::update_tree() { current_vbox->add_child(editor_inspector_array); editor_inspector_array_per_prefix[array_element_prefix] = editor_inspector_array; } + continue; } @@ -3401,14 +3555,23 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo undo_redo->add_undo_property(object, p_name, value); } - PropertyInfo prop_info; - if (ClassDB::get_property_info(object->get_class_name(), p_name, &prop_info)) { - for (const String &linked_prop : prop_info.linked_properties) { - valid = false; - value = object->get(linked_prop, &valid); - if (valid) { - undo_redo->add_undo_property(object, linked_prop, value); - } + List<StringName> linked_properties; + ClassDB::get_linked_properties_info(object->get_class_name(), p_name, &linked_properties); + + for (const StringName &linked_prop : linked_properties) { + valid = false; + Variant undo_value = object->get(linked_prop, &valid); + if (valid) { + undo_redo->add_undo_property(object, linked_prop, undo_value); + } + } + + PackedStringArray linked_properties_dynamic = object->call("_get_linked_undo_properties", p_name, p_value); + for (int i = 0; i < linked_properties_dynamic.size(); i++) { + valid = false; + Variant undo_value = object->get(linked_properties_dynamic[i], &valid); + if (valid) { + undo_redo->add_undo_property(object, linked_properties_dynamic[i], undo_value); } } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 54533de960..9b5e295854 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -321,6 +321,7 @@ class EditorInspectorArray : public EditorInspectorSection { } mode; StringName count_property; StringName array_element_prefix; + String swap_method; int count = 0; @@ -342,6 +343,9 @@ class EditorInspectorArray : public EditorInspectorSection { int begin_array_index = 0; int end_array_index = 0; + bool movable = true; + bool numbered = false; + enum MenuOptions { OPTION_MOVE_UP = 0, OPTION_MOVE_DOWN, @@ -359,7 +363,9 @@ class EditorInspectorArray : public EditorInspectorSection { MarginContainer *margin = nullptr; HBoxContainer *hbox = nullptr; TextureRect *move_texture_rect = nullptr; + Label *number = nullptr; VBoxContainer *vbox = nullptr; + Button *erase = nullptr; }; LocalVector<ArrayElement> array_elements; @@ -395,6 +401,8 @@ class EditorInspectorArray : public EditorInspectorSection { void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void _remove_item(int p_index); + protected: void _notification(int p_what); static void _bind_methods(); @@ -402,8 +410,8 @@ protected: public: void set_undo_redo(UndoRedo *p_undo_redo); - void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable); - void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable); + void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = ""); + void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = ""); VBoxContainer *get_vbox(int p_index); EditorInspectorArray(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ce885032a3..8caa38737c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6157,8 +6157,6 @@ EditorNode::EditorNode() { register_exporters(); - ClassDB::set_class_enabled("RootMotionView", true); - EDITOR_DEF("interface/editor/save_on_focus_loss", false); EDITOR_DEF("interface/editor/show_update_spinner", false); EDITOR_DEF("interface/editor/update_continuously", false); @@ -6877,6 +6875,7 @@ EditorNode::EditorNode() { filesystem_dock->connect("inherit", callable_mp(this, &EditorNode::_inherit_request)); filesystem_dock->connect("instance", callable_mp(this, &EditorNode::_instantiate_request)); filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_docks)); + get_project_settings()->connect_filesystem_dock_signals(filesystem_dock); // Scene: Top left. dock_slot[DOCK_SLOT_LEFT_UR]->add_child(SceneTreeDock::get_singleton()); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4846fd6478..80e77a1125 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -685,7 +685,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Visual editors EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/minimap_opacity", 0.85, "0.0,1.0,0.01") EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/lines_curvature", 0.5, "0.0,1.0,0.01") - EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "editors/visual_editors/visualshader/port_preview_size", 160, "100,400,0.01") + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "editors/visual_editors/visual_shader/port_preview_size", 160, "100,400,0.01") /* Run */ diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 02380c4e39..d188871f59 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -641,6 +641,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Add a highlight line at the top of the selected tab. style_tab_selected->set_border_width_all(0); + style_tab_selected->set_default_margin(SIDE_LEFT, widget_default_margin.x - border_width); style_tab_selected->set_border_width(SIDE_TOP, Math::round(2 * EDSCALE)); // Make the highlight line prominent, but not too prominent as to not be distracting. Color tab_highlight = dark_color_2.lerp(accent_color, 0.75); @@ -653,6 +654,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // We can't prevent them with both rounded corners and non-zero border width, though style_tab_selected->set_expand_margin_size(SIDE_BOTTOM, corner_width > 0 ? corner_width : border_width); + // When using a border width greater than 0, visually line up the left of the selected tab with the underlying panel. + style_tab_selected->set_expand_margin_size(SIDE_LEFT, -border_width); + style_tab_selected->set_default_margin(SIDE_LEFT, widget_default_margin.x + 2 * EDSCALE); style_tab_selected->set_default_margin(SIDE_RIGHT, widget_default_margin.x + 2 * EDSCALE); style_tab_selected->set_default_margin(SIDE_BOTTOM, widget_default_margin.y); @@ -830,6 +834,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("pressed", "CheckButton", style_menu); theme->set_stylebox("disabled", "CheckButton", style_menu); theme->set_stylebox("hover", "CheckButton", style_menu); + theme->set_stylebox("hover_pressed", "CheckButton", style_menu); theme->set_icon("on", "CheckButton", theme->get_icon(SNAME("GuiToggleOn"), SNAME("EditorIcons"))); theme->set_icon("on_disabled", "CheckButton", theme->get_icon(SNAME("GuiToggleOnDisabled"), SNAME("EditorIcons"))); @@ -867,6 +872,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("pressed", "CheckBox", sb_checkbox); theme->set_stylebox("disabled", "CheckBox", sb_checkbox); theme->set_stylebox("hover", "CheckBox", sb_checkbox); + theme->set_stylebox("hover_pressed", "CheckBox", sb_checkbox); theme->set_icon("checked", "CheckBox", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons"))); theme->set_icon("unchecked", "CheckBox", theme->get_icon(SNAME("GuiUnchecked"), SNAME("EditorIcons"))); theme->set_icon("radio_checked", "CheckBox", theme->get_icon(SNAME("GuiRadioChecked"), SNAME("EditorIcons"))); diff --git a/editor/icons/AudioStream.svg b/editor/icons/AudioStream.svg new file mode 100644 index 0000000000..5d92dc25a5 --- /dev/null +++ b/editor/icons/AudioStream.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="m12 2a-1 1 0 0 1 1 1-1 1 0 0 1 -1 1c-4.4301 0-8 3.5699-8 8a-1 1 0 0 1 -1 1-1 1 0 0 1 -1-1c0-5.511 4.489-10 10-10zm0 4a-1 1 0 0 1 1 1-1 1 0 0 1 -1 1c-2.221 0-4 1.779-4 4a-1 1 0 0 1 -1 1-1 1 0 0 1 -1-1c0-3.3018 2.6981-6 6-6zm0 4a-2 2 0 0 1 2 2-2 2 0 0 1 -2 2-2 2 0 0 1 -2-2-2 2 0 0 1 2-2z" fill="url(#a)"/></svg> diff --git a/editor/icons/AudioStreamGenerator.svg b/editor/icons/AudioStreamGenerator.svg new file mode 100644 index 0000000000..55b0fb9d92 --- /dev/null +++ b/editor/icons/AudioStreamGenerator.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="m14 9-3 5-3-12-3 7-3-2" fill="none" stroke="url(#a)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg> diff --git a/editor/icons/AudioStreamMicrophone.svg b/editor/icons/AudioStreamMicrophone.svg new file mode 100644 index 0000000000..51009e9d53 --- /dev/null +++ b/editor/icons/AudioStreamMicrophone.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="m7 1c-1.108 0-2 .892-2 2h2v1h-2v2h2v1h-2c0 1.108.892 2 2 2v4l-2 2h6l-2-2v-4c1.108 0 2-.892 2-2h-2v-1h2v-2h-2v-1h2c0-1.108-.892-2-2-2z" fill="url(#a)"/></svg> diff --git a/editor/icons/AudioStreamRandomizer.svg b/editor/icons/AudioStreamRandomizer.svg new file mode 100644 index 0000000000..1696dff795 --- /dev/null +++ b/editor/icons/AudioStreamRandomizer.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="m7.9999997 1c6.6837543 0 7.0000003.3165085 7.0000003 7.0057779 0 6.6877711-.286255 6.9755641-6.9367525 6.9938021-3.0191555.008313-4.4456225-.105997-5.1863245-.415726-1.570375-.65669-1.876923-1.727949-1.876923-6.5780761 0-6.6892694.316247-7.0057779 6.9999997-7.0057779zm3.5299143 1.7638478c-1.5662016 0-2.4379256 1.7724432-1.475213 2.9973439.738933.9401693 2.041543 1.025967 2.876923.1899002 1.183646-1.1846229.303279-3.1872441-1.40171-3.1872441zm-3.5760682 3.2710739c-1.5661974 0-2.4379268 1.7707341-1.4752138 2.9956331.7389365.9401892 2.0415435 1.0276772 2.8769233.191611 1.1836457-1.1846231.3032798-3.1872441-1.4017095-3.1872441zm-3.5538458 3.4729499c-.958537.031867-1.875214.7423284-1.875214 1.8493884 0 1.564955 2.248443 2.516522 3.249573 1.375494.7905175-.900982.8551191-1.664857.208547-2.487522-.416627-.5300879-1.007786-.7565128-1.582906-.7373604z" fill="url(#a)"/></svg> diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 0325f4bd5c..e8fb80eb57 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_translation_parser.h" +#include "editor/filesystem_dock.h" #include "editor/pot_generator.h" #include "scene/gui/control.h" @@ -379,6 +380,95 @@ void LocalizationEditor::_update_pot_file_extensions() { } } +void LocalizationEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) { + p_fs_dock->connect("files_moved", callable_mp(this, &LocalizationEditor::_filesystem_files_moved)); + p_fs_dock->connect("file_removed", callable_mp(this, &LocalizationEditor::_filesystem_file_removed)); +} + +void LocalizationEditor::_filesystem_files_moved(const String &p_old_file, const String &p_new_file) { + // Update remaps if the moved file is a part of them. + Dictionary remaps; + bool remaps_changed = false; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { + remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); + } + + // Check for the keys. + if (remaps.has(p_old_file)) { + PackedStringArray remapped_files = remaps[p_old_file]; + remaps.erase(p_old_file); + remaps[p_new_file] = remapped_files; + remaps_changed = true; + print_verbose(vformat("Changed remap key \"%s\" to \"%s\" due to a moved file.", p_old_file, p_new_file)); + } + + // Check for the Array elements of the values. + Array remap_keys = remaps.keys(); + for (int i = 0; i < remap_keys.size(); i++) { + PackedStringArray remapped_files = remaps[remap_keys[i]]; + bool remapped_files_updated = false; + + for (int j = 0; j < remapped_files.size(); j++) { + int splitter_pos = remapped_files[j].rfind(":"); + String res_path = remapped_files[j].substr(0, splitter_pos); + + if (res_path == p_old_file) { + String locale_name = remapped_files[j].substr(splitter_pos + 1); + // Replace the element at that index. + remapped_files.insert(j, p_new_file + ":" + locale_name); + remapped_files.remove_at(j + 1); + remaps_changed = true; + remapped_files_updated = true; + print_verbose(vformat("Changed remap value \"%s\" to \"%s\" of key \"%s\" due to a moved file.", res_path + ":" + locale_name, remapped_files[j], remap_keys[i])); + } + } + + if (remapped_files_updated) { + remaps[remap_keys[i]] = remapped_files; + } + } + + if (remaps_changed) { + ProjectSettings::get_singleton()->set_setting("internationalization/locale/translation_remaps", remaps); + update_translations(); + emit_signal("localization_changed"); + } +} + +void LocalizationEditor::_filesystem_file_removed(const String &p_file) { + // Check if the remaps are affected. + Dictionary remaps; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { + remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); + } + + bool remaps_changed = remaps.has(p_file); + + if (!remaps_changed) { + Array remap_keys = remaps.keys(); + for (int i = 0; i < remap_keys.size() && !remaps_changed; i++) { + PackedStringArray remapped_files = remaps[remap_keys[i]]; + for (int j = 0; j < remapped_files.size() && !remaps_changed; j++) { + int splitter_pos = remapped_files[j].rfind(":"); + String res_path = remapped_files[j].substr(0, splitter_pos); + remaps_changed = p_file == res_path; + if (remaps_changed) { + print_verbose(vformat("Remap value \"%s\" of key \"%s\" has been removed from the file system.", remapped_files[j], remap_keys[i])); + } + } + } + } else { + print_verbose(vformat("Remap key \"%s\" has been removed from the file system.", p_file)); + } + + if (remaps_changed) { + update_translations(); + emit_signal("localization_changed"); + } +} + void LocalizationEditor::update_translations() { if (updating_translations) { return; @@ -432,6 +522,13 @@ void LocalizationEditor::update_translations() { t->set_tooltip(0, keys[i]); t->set_metadata(0, keys[i]); t->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0, false, TTR("Remove")); + + // Display that it has been removed if this is the case. + if (!FileAccess::exists(keys[i])) { + t->set_text(0, t->get_text(0) + vformat(" (%s)", TTR("Removed"))); + t->set_tooltip(0, vformat(TTR("%s cannot be found."), t->get_tooltip(0))); + } + if (keys[i] == remap_selected) { t->select(0); translation_res_option_add_button->set_disabled(false); @@ -454,6 +551,12 @@ void LocalizationEditor::update_translations() { t2->set_editable(1, true); t2->set_metadata(1, path); t2->set_tooltip(1, locale); + + // Display that it has been removed if this is the case. + if (!FileAccess::exists(path)) { + t2->set_text(0, t2->get_text(0) + vformat(" (%s)", TTR("Removed"))); + t2->set_tooltip(0, vformat(TTR("%s cannot be found."), t2->get_tooltip(0))); + } } } } diff --git a/editor/localization_editor.h b/editor/localization_editor.h index 4b41a90cc2..10ccdfdc13 100644 --- a/editor/localization_editor.h +++ b/editor/localization_editor.h @@ -36,6 +36,7 @@ #include "scene/gui/tree.h" class EditorFileDialog; +class FileSystemDock; class LocalizationEditor : public VBoxContainer { GDCLASS(LocalizationEditor, VBoxContainer); @@ -81,6 +82,9 @@ class LocalizationEditor : public VBoxContainer { void _pot_generate(const String &p_file); void _update_pot_file_extensions(); + void _filesystem_files_moved(const String &p_old_file, const String &p_new_file); + void _filesystem_file_removed(const String &p_file); + protected: void _notification(int p_what); static void _bind_methods(); @@ -88,6 +92,7 @@ protected: public: void add_translation(const String &p_translation); void update_translations(); + void connect_filesystem_dock_signals(FileSystemDock *p_fs_dock); LocalizationEditor(); }; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 65d684c2a1..ad82f2e9b2 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -6249,7 +6249,7 @@ void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, Visua } Size2 VisualShaderNodePortPreview::get_minimum_size() const { - int port_preview_size = EditorSettings::get_singleton()->get("editors/visual_editors/visualshader/port_preview_size"); + int port_preview_size = EditorSettings::get_singleton()->get("editors/visual_editors/visual_shader/port_preview_size"); return Size2(port_preview_size, port_preview_size) * EDSCALE; } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index fba8c5c522..ca5eeaa787 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -39,6 +39,10 @@ ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr; +void ProjectSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) { + localization_editor->connect_filesystem_dock_signals(p_fs_dock); +} + void ProjectSettingsEditor::popup_project_settings() { // Restore valid window bounds or pop up at default size. Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "project_settings", Rect2()); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 849c50b9ba..c2d2c2d8f4 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -43,6 +43,8 @@ #include "editor/shader_globals_editor.h" #include "scene/gui/tab_container.h" +class FileSystemDock; + class ProjectSettingsEditor : public AcceptDialog { GDCLASS(ProjectSettingsEditor, AcceptDialog); @@ -120,6 +122,7 @@ public: TabContainer *get_tabs() { return tab_container; } void queue_save(); + void connect_filesystem_dock_signals(FileSystemDock *p_fs_dock); ProjectSettingsEditor(EditorData *p_data); }; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index b1f1decd72..437878818c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -828,6 +828,22 @@ namespace Godot } /// <summary> + /// Constructs a pure scale basis matrix with no rotation or shearing. + /// The scale values are set as the main diagonal of the matrix, + /// and all of the other parts of the matrix are zero. + /// </summary> + /// <param name="scale">The scale Vector3.</param> + /// <returns>A pure scale Basis matrix.</returns> + public static Basis FromScale(Vector3 scale) + { + return new Basis( + scale.x, 0, 0, + 0, scale.y, 0, + 0, 0, scale.z + ); + } + + /// <summary> /// Composes these two basis matrices by multiplying them /// together. This has the effect of transforming the second basis /// (the child) by the first basis (the parent). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index b8413f1e16..68d097eb4e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -297,7 +297,9 @@ namespace Godot } /// <summary> - /// Rotates the transform by <paramref name="angle"/> (in radians), using matrix multiplication. + /// Rotates the transform by <paramref name="angle"/> (in radians). + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. /// </summary> /// <param name="angle">The angle to rotate, in radians.</param> /// <returns>The rotated transformation matrix.</returns> @@ -307,7 +309,21 @@ namespace Godot } /// <summary> - /// Scales the transform by the given scaling factor, using matrix multiplication. + /// Rotates the transform by <paramref name="angle"/> (in radians). + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. + /// </summary> + /// <param name="angle">The angle to rotate, in radians.</param> + /// <returns>The rotated transformation matrix.</returns> + public Transform2D RotatedLocal(real_t angle) + { + return new Transform2D(angle, new Vector2()) * this; + } + + /// <summary> + /// Scales the transform by the given scaling factor. + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. /// </summary> /// <param name="scale">The scale to introduce.</param> /// <returns>The scaled transformation matrix.</returns> @@ -320,6 +336,21 @@ namespace Godot return copy; } + /// <summary> + /// Scales the transform by the given scaling factor. + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. + /// </summary> + /// <param name="scale">The scale to introduce.</param> + /// <returns>The scaled transformation matrix.</returns> + public Transform2D ScaledLocal(Vector2 scale) + { + Transform2D copy = this; + copy.x *= scale; + copy.y *= scale; + return copy; + } + private real_t Tdotx(Vector2 with) { return (this[0, 0] * with[0]) + (this[1, 0] * with[1]); @@ -331,11 +362,23 @@ namespace Godot } /// <summary> - /// Translates the transform by the given <paramref name="offset"/>, - /// relative to the transform's basis vectors. - /// - /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, - /// this does not use matrix multiplication. + /// Translates the transform by the given <paramref name="offset"/>. + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. + /// </summary> + /// <param name="offset">The offset to translate by.</param> + /// <returns>The translated matrix.</returns> + public Transform2D Translated(Vector2 offset) + { + Transform2D copy = this; + copy.origin += offset; + return copy; + } + + /// <summary> + /// Translates the transform by the given <paramref name="offset"/>. + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. /// </summary> /// <param name="offset">The offset to translate by.</param> /// <returns>The translated matrix.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 97b4a87713..9eaf4f3252 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -186,8 +186,10 @@ namespace Godot } /// <summary> - /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians), - /// using matrix multiplication. The axis must be a normalized vector. + /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians). + /// The axis must be a normalized vector. + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> /// <param name="angle">The angle to rotate, in radians.</param> @@ -198,7 +200,24 @@ namespace Godot } /// <summary> - /// Scales the transform by the given 3D scaling factor, using matrix multiplication. + /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians). + /// The axis must be a normalized vector. + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. + /// </summary> + /// <param name="axis">The axis to rotate around. Must be normalized.</param> + /// <param name="angle">The angle to rotate, in radians.</param> + /// <returns>The rotated transformation matrix.</returns> + public Transform3D RotatedLocal(Vector3 axis, real_t angle) + { + Basis tmpBasis = new Basis(axis, angle); + return new Transform3D(basis * tmpBasis, origin); + } + + /// <summary> + /// Scales the transform by the given 3D <paramref name="scale"/> factor. + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. /// </summary> /// <param name="scale">The scale to introduce.</param> /// <returns>The scaled transformation matrix.</returns> @@ -207,6 +226,19 @@ namespace Godot return new Transform3D(basis.Scaled(scale), origin * scale); } + /// <summary> + /// Scales the transform by the given 3D <paramref name="scale"/> factor. + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. + /// </summary> + /// <param name="scale">The scale to introduce.</param> + /// <returns>The scaled transformation matrix.</returns> + public Transform3D ScaledLocal(Vector3 scale) + { + Basis tmpBasis = Basis.FromScale(scale); + return new Transform3D(basis * tmpBasis, origin); + } + private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix @@ -231,11 +263,21 @@ namespace Godot } /// <summary> - /// Translates the transform by the given <paramref name="offset"/>, - /// relative to the transform's basis vectors. - /// - /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, - /// this does not use matrix multiplication. + /// Translates the transform by the given <paramref name="offset"/>. + /// The operation is done in the parent/global frame, equivalent to + /// multiplying the matrix from the left. + /// </summary> + /// <param name="offset">The offset to translate by.</param> + /// <returns>The translated matrix.</returns> + public Transform3D Translated(Vector3 offset) + { + return new Transform3D(basis, origin + offset); + } + + /// <summary> + /// Translates the transform by the given <paramref name="offset"/>. + /// The operation is done in the local frame, equivalent to + /// multiplying the matrix from the right. /// </summary> /// <param name="offset">The offset to translate by.</param> /// <returns>The translated matrix.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index ec2e724b10..c3d5fbfdef 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -620,22 +620,6 @@ namespace Godot ); } - /// <summary> - /// Returns a diagonal matrix with the vector as main diagonal. - /// - /// This is equivalent to a <see cref="Basis"/> with no rotation or shearing and - /// this vector's components set as the scale. - /// </summary> - /// <returns>A <see cref="Basis"/> with the vector as its main diagonal.</returns> - public Basis ToDiagonalMatrix() - { - return new Basis( - x, 0, 0, - 0, y, 0, - 0, 0, z - ); - } - // Constants private static readonly Vector3 _zero = new Vector3(0, 0, 0); private static readonly Vector3 _one = new Vector3(1, 1, 1); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 4155d0797f..26204a3b1a 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1470,7 +1470,7 @@ CPUParticles2D::CPUParticles2D() { set_emitting(true); set_amount(8); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); set_param_min(PARAM_ANGULAR_VELOCITY, 0); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index a869cf2525..075421a26d 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -660,7 +660,7 @@ GPUParticles2D::GPUParticles2D() { set_explosiveness_ratio(0); set_randomness_ratio(0); set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_draw_order(DRAW_ORDER_LIFETIME); set_speed_scale(1); set_fixed_fps(30); diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index da4a14394d..f654373ee5 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -507,7 +507,7 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.01,suffix:m"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001,suffix:m"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 4cb693f494..e26c301038 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -138,7 +138,7 @@ private: real_t randomness_ratio = 0.0; double lifetime_randomness = 0.0; double speed_scale = 1.0; - bool local_coords = true; + bool local_coords = false; int fixed_fps = 0; bool fractional_delta = true; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 01aff32954..2ee126e161 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -631,7 +631,7 @@ GPUParticles3D::GPUParticles3D() { set_randomness_ratio(0); set_trail_length(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_draw_passes(1); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index a67f850a86..9bcb061526 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -34,39 +34,14 @@ #include "servers/rendering_server.h" Size2 Button::get_minimum_size() const { - Size2 minsize = text_buf->get_size(); - if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { - minsize.width = 0; - } - - if (!expand_icon) { - Ref<Texture2D> _icon; - if (icon.is_null() && has_theme_icon(SNAME("icon"))) { - _icon = Control::get_theme_icon(SNAME("icon")); - } else { - _icon = icon; - } - - if (!_icon.is_null()) { - minsize.height = MAX(minsize.height, _icon->get_height()); - - if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { - minsize.width += _icon->get_width(); - if (!xl_text.is_empty()) { - minsize.width += get_theme_constant(SNAME("h_separation")); - } - } else { - minsize.width = MAX(minsize.width, _icon->get_width()); - } - } - } - if (!xl_text.is_empty()) { - Ref<Font> font = get_theme_font(SNAME("font")); - float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); - minsize.height = MAX(font_height, minsize.height); + Ref<Texture2D> _icon; + if (icon.is_null() && has_theme_icon(SNAME("icon"))) { + _icon = Control::get_theme_icon(SNAME("icon")); + } else { + _icon = icon; } - return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize; + return get_minimum_size_for_text_and_icon("", _icon); } void Button::_set_internal_margin(Side p_side, float p_value) { @@ -352,18 +327,62 @@ void Button::_notification(int p_what) { } } -void Button::_shape() { +Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const { + Ref<TextParagraph> paragraph; + if (p_text.is_empty()) { + paragraph = text_buf; + } else { + paragraph.instantiate(); + const_cast<Button *>(this)->_shape(paragraph, p_text); + } + + Size2 minsize = paragraph->get_size(); + if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { + minsize.width = 0; + } + + if (!expand_icon && !p_icon.is_null()) { + minsize.height = MAX(minsize.height, p_icon->get_height()); + + if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { + minsize.width += p_icon->get_width(); + if (!xl_text.is_empty() || !p_text.is_empty()) { + minsize.width += get_theme_constant(SNAME("hseparation")); + } + } else { + minsize.width = MAX(minsize.width, p_icon->get_width()); + } + } + + if (!xl_text.is_empty() || !p_text.is_empty()) { + Ref<Font> font = get_theme_font(SNAME("font")); + float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); + minsize.height = MAX(font_height, minsize.height); + } + + return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize; +} + +void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { + if (p_paragraph.is_null()) { + p_paragraph = text_buf; + } + + if (p_text.is_empty()) { + p_text = xl_text; + } + Ref<Font> font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); - text_buf->clear(); + p_paragraph->clear(); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { - text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { - text_buf->set_direction((TextServer::Direction)text_direction); + p_paragraph->set_direction((TextServer::Direction)text_direction); } - text_buf->add_string(xl_text, font, font_size, language); - text_buf->set_text_overrun_behavior(overrun_behavior); + p_paragraph->add_string(p_text, font, font_size, language); + p_paragraph->set_text_overrun_behavior(overrun_behavior); } void Button::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { diff --git a/scene/gui/button.h b/scene/gui/button.h index 9d8d457f7c..23b5c78166 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -54,7 +54,7 @@ private: HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT; float _internal_margin[4] = {}; - void _shape(); + void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); protected: void _set_internal_margin(Side p_side, float p_value); @@ -64,6 +64,8 @@ protected: public: virtual Size2 get_minimum_size() const override; + Size2 get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const; + void set_text(const String &p_text); String get_text() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 788feacdd9..8f63d76347 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -171,6 +171,9 @@ uniform float v = 1.0; void fragment() { float x = UV.x - 0.5; float y = UV.y - 0.5; + float h = atan(y, x) / (2.0 * M_PI); + float s = sqrt(x * x + y * y) * 2.0; + vec3 col = okhsl_to_srgb(vec3(h, s, v)); x += 0.001; y += 0.001; float b = float(sqrt(x * x + y * y) < 0.5); @@ -180,9 +183,6 @@ void fragment() { float b3 = float(sqrt(x * x + y * y) < 0.5); x += 0.002; float b4 = float(sqrt(x * x + y * y) < 0.5); - float s = sqrt(x * x + y * y); - float h = atan(y, x) / (2.0*M_PI); - vec3 col = okhsl_to_srgb(vec3(h, s, v)); COLOR = vec4(col, (b + b2 + b3 + b4) / 4.00); })"); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index a10ec1db06..c58513df17 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -35,7 +35,12 @@ static const int NONE_SELECTED = -1; Size2 OptionButton::get_minimum_size() const { - Size2 minsize = Button::get_minimum_size(); + Size2 minsize; + if (fit_to_longest_item) { + minsize = _cached_size; + } else { + minsize = Button::get_minimum_size(); + } if (has_theme_icon(SNAME("arrow"))) { const Size2 padding = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); @@ -107,6 +112,7 @@ void OptionButton::_notification(int p_what) { _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width()); } } + _refresh_size_cache(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -135,6 +141,10 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { _select(idx, false); } + if (property == "text" || property == "icon") { + _queue_refresh_cache(); + } + return valid; } return false; @@ -208,6 +218,7 @@ void OptionButton::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_l if (first_selectable) { select(get_item_count() - 1); } + _queue_refresh_cache(); } void OptionButton::add_item(const String &p_label, int p_id) { @@ -216,6 +227,7 @@ void OptionButton::add_item(const String &p_label, int p_id) { if (first_selectable) { select(get_item_count() - 1); } + _queue_refresh_cache(); } void OptionButton::set_item_text(int p_idx, const String &p_text) { @@ -224,6 +236,7 @@ void OptionButton::set_item_text(int p_idx, const String &p_text) { if (current == p_idx) { set_text(p_text); } + _queue_refresh_cache(); } void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { @@ -232,6 +245,7 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { if (current == p_idx) { set_icon(p_icon); } + _queue_refresh_cache(); } void OptionButton::set_item_id(int p_idx, int p_id) { @@ -301,6 +315,7 @@ void OptionButton::set_item_count(int p_count) { } } + _refresh_size_cache(); notify_property_list_changed(); } @@ -333,6 +348,19 @@ int OptionButton::get_item_count() const { return popup->get_item_count(); } +void OptionButton::set_fit_to_longest_item(bool p_fit) { + if (p_fit == fit_to_longest_item) { + return; + } + fit_to_longest_item = p_fit; + + _refresh_size_cache(); +} + +bool OptionButton::is_fit_to_longest_item() const { + return fit_to_longest_item; +} + void OptionButton::add_separator(const String &p_text) { popup->add_separator(p_text); } @@ -341,6 +369,7 @@ void OptionButton::clear() { popup->clear(); set_text(""); current = NONE_SELECTED; + _refresh_size_cache(); } void OptionButton::_select(int p_which, bool p_emit) { @@ -380,6 +409,29 @@ void OptionButton::_select_int(int p_which) { _select(p_which, false); } +void OptionButton::_refresh_size_cache() { + cache_refresh_pending = false; + + if (!fit_to_longest_item) { + return; + } + + _cached_size = Vector2(); + for (int i = 0; i < get_item_count(); i++) { + _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(get_item_text(i), get_item_icon(i))); + } + update_minimum_size(); +} + +void OptionButton::_queue_refresh_cache() { + if (cache_refresh_pending) { + return; + } + cache_refresh_pending = true; + + callable_mp(this, &OptionButton::_refresh_size_cache).call_deferredp(nullptr, 0); +} + void OptionButton::select(int p_idx) { _select(p_idx, false); } @@ -405,6 +457,7 @@ void OptionButton::remove_item(int p_idx) { if (current == p_idx) { _select(NONE_SELECTED); } + _queue_refresh_cache(); } PopupMenu *OptionButton::get_popup() const { @@ -453,10 +506,13 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count); ClassDB::bind_method(D_METHOD("has_selectable_items"), &OptionButton::has_selectable_items); ClassDB::bind_method(D_METHOD("get_selectable_item", "from_last"), &OptionButton::get_selectable_item, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_fit_to_longest_item", "fit"), &OptionButton::set_fit_to_longest_item); + ClassDB::bind_method(D_METHOD("is_fit_to_longest_item"), &OptionButton::is_fit_to_longest_item); // "selected" property must come after "item_count", otherwise GH-10213 occurs. ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_to_longest_item"), "set_fit_to_longest_item", "is_fit_to_longest_item"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index"))); ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index"))); } @@ -482,6 +538,7 @@ OptionButton::OptionButton(const String &p_text) : popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected)); popup->connect("id_focused", callable_mp(this, &OptionButton::_focused)); popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false)); + _refresh_size_cache(); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 5665296699..49b5eee910 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -39,11 +39,16 @@ class OptionButton : public Button { PopupMenu *popup = nullptr; int current = -1; + bool fit_to_longest_item = true; + Vector2 _cached_size; + bool cache_refresh_pending = false; void _focused(int p_which); void _selected(int p_which); void _select(int p_which, bool p_emit = false); void _select_int(int p_which); + void _refresh_size_cache(); + void _queue_refresh_cache(); virtual void pressed() override; @@ -85,6 +90,8 @@ public: void set_item_count(int p_count); int get_item_count() const; + void set_fit_to_longest_item(bool p_fit); + bool is_fit_to_longest_item() const; void add_separator(const String &p_text = ""); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 9efab27e3a..8fd547813d 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -37,7 +37,10 @@ Size2 ScrollContainer::get_minimum_size() const { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); Size2 min_size; - Size2 content_min_size; + + // Calculated in this function, as it needs to traverse all child controls once to calculate; + // and needs to be calculated before being used by update_scrollbars(). + largest_child_min_size = Size2(); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -50,25 +53,27 @@ Size2 ScrollContainer::get_minimum_size() const { if (c == h_scroll || c == v_scroll) { continue; } - Size2 minsize = c->get_combined_minimum_size(); - content_min_size.x = MAX(content_min_size.x, minsize.x); - content_min_size.y = MAX(content_min_size.y, minsize.y); + Size2 child_min_size = c->get_combined_minimum_size(); + + largest_child_min_size.x = MAX(largest_child_min_size.x, child_min_size.x); + largest_child_min_size.y = MAX(largest_child_min_size.y, child_min_size.y); } if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.x = MAX(min_size.x, content_min_size.x); + min_size.x = MAX(min_size.x, largest_child_min_size.x); } if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.y = MAX(min_size.y, content_min_size.y); + min_size.y = MAX(min_size.y, largest_child_min_size.y); } - bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && content_min_size.x > min_size.x); - bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && content_min_size.y > min_size.y); - if (h_scroll_show) { + bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > min_size.x); + bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > min_size.y); + + if (h_scroll_show && h_scroll->get_parent() == this) { min_size.y += h_scroll->get_minimum_size().y; } - if (v_scroll_show) { + if (v_scroll_show && v_scroll->get_parent() == this) { min_size.x += v_scroll->get_minimum_size().x; } @@ -261,8 +266,8 @@ void ScrollContainer::ensure_control_visible(Control *p_control) { set_v_scroll(get_v_scroll() + (diff.y - global_rect.position.y)); } -void ScrollContainer::_update_dimensions() { - child_max_size = Size2(0, 0); +void ScrollContainer::_reposition_children() { + update_scrollbars(); Size2 size = get_size(); Point2 ofs; @@ -291,25 +296,13 @@ void ScrollContainer::_update_dimensions() { continue; } Size2 minsize = c->get_combined_minimum_size(); - child_max_size.x = MAX(child_max_size.x, minsize.x); - child_max_size.y = MAX(child_max_size.y, minsize.y); Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize); - if (horizontal_scroll_mode == SCROLL_MODE_DISABLED || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { - r.position.x = 0; - if (c->get_h_size_flags() & SIZE_EXPAND) { - r.size.width = MAX(size.width, minsize.width); - } else { - r.size.width = minsize.width; - } + if (c->get_h_size_flags() & SIZE_EXPAND) { + r.size.width = MAX(size.width, minsize.width); } - if (vertical_scroll_mode == SCROLL_MODE_DISABLED || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) { - r.position.y = 0; - if (c->get_v_size_flags() & SIZE_EXPAND) { - r.size.height = MAX(size.height, minsize.height); - } else { - r.size.height = minsize.height; - } + if (c->get_v_size_flags() & SIZE_EXPAND) { + r.size.height = MAX(size.height, minsize.height); } r.position += ofs; if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { @@ -319,7 +312,6 @@ void ScrollContainer::_update_dimensions() { fit_child_in_rect(c, r); } - update_scrollbars(); update(); } @@ -337,18 +329,16 @@ void ScrollContainer::_notification(int p_what) { Viewport *viewport = get_viewport(); ERR_FAIL_COND(!viewport); viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed)); - _update_dimensions(); + _reposition_children(); } break; case NOTIFICATION_SORT_CHILDREN: { - _update_dimensions(); + _reposition_children(); } break; case NOTIFICATION_DRAW: { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); draw_style_box(sb, Rect2(Vector2(), get_size())); - - update_scrollbars(); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -426,36 +416,25 @@ void ScrollContainer::update_scrollbars() { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); size -= sb->get_minimum_size(); - Size2 hmin; - Size2 vmin; - if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) { - hmin = h_scroll->get_combined_minimum_size(); - } - if (vertical_scroll_mode != SCROLL_MODE_DISABLED) { - vmin = v_scroll->get_combined_minimum_size(); - } - - Size2 min = child_max_size; + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); - bool hide_scroll_h = horizontal_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (horizontal_scroll_mode == SCROLL_MODE_DISABLED || horizontal_scroll_mode == SCROLL_MODE_SHOW_NEVER || (horizontal_scroll_mode == SCROLL_MODE_AUTO && min.width <= size.width)); - bool hide_scroll_v = vertical_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (vertical_scroll_mode == SCROLL_MODE_DISABLED || vertical_scroll_mode == SCROLL_MODE_SHOW_NEVER || (vertical_scroll_mode == SCROLL_MODE_AUTO && min.height <= size.height)); + h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.width > size.width)); + v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.height > size.height)); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width)); - h_scroll->set_visible(!hide_scroll_h); + h_scroll->set_max(largest_child_min_size.width); + h_scroll->set_page((v_scroll->is_visible() && v_scroll->get_parent() == this) ? size.width - vmin.width : size.width); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - (hide_scroll_h ? 0 : hmin.height)); - v_scroll->set_visible(!hide_scroll_v); + v_scroll->set_max(largest_child_min_size.height); + v_scroll->set_page((h_scroll->is_visible() && h_scroll->get_parent() == this) ? size.height - hmin.height : size.height); // Avoid scrollbar overlapping. - h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width); - v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, (v_scroll->is_visible() && v_scroll->get_parent() == this) ? -vmin.width : 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, (h_scroll->is_visible() && h_scroll->get_parent() == this) ? -hmin.height : 0); } void ScrollContainer::_scroll_moved(float) { queue_sort(); - update(); }; void ScrollContainer::set_h_scroll(int p_pos) { diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 7c8690538d..bfa74cfd0f 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -50,7 +50,7 @@ private: HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; - Size2 child_max_size; + mutable Size2 largest_child_min_size; // The largest one among the min sizes of all available child controls. void update_scrollbars(); @@ -75,7 +75,7 @@ protected: Size2 get_minimum_size() const override; void _gui_focus_changed(Control *p_control); - void _update_dimensions(); + void _reposition_children(); void _notification(int p_what); void _scroll_moved(float); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6617bd1726..ea9788de27 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -653,21 +653,6 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE return err; } -template <typename... VarArgs> -Error Node::rpc(const StringName &p_method, VarArgs... p_args) { - return rpc_id(0, p_method, p_args...); -} - -template <typename... VarArgs> -Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); -} - Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); diff --git a/scene/main/node.h b/scene/main/node.h index 0645c68eb9..ccd1d561d2 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -512,4 +512,22 @@ VARIANT_ENUM_CAST(Node::DuplicateFlags); typedef HashSet<Node *, Node::Comparator> NodeSet; +// Template definitions must be in the header so they are always fully initialized before their usage. +// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file + +template <typename... VarArgs> +Error Node::rpc(const StringName &p_method, VarArgs... p_args) { + return rpc_id(0, p_method, p_args...); +} + +template <typename... VarArgs> +Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); +} + #endif // NODE_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0878a9f78f..d7fcd500b2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -523,9 +523,7 @@ void register_scene_types() { GDREGISTER_CLASS(GPUParticlesAttractorVectorField3D); GDREGISTER_CLASS(CPUParticles3D); GDREGISTER_CLASS(Position3D); - GDREGISTER_CLASS(RootMotionView); - ClassDB::set_class_enabled("RootMotionView", false); // disabled by default, enabled by editor OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index e0918b17c7..4b2e029f47 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -1650,7 +1650,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); - ADD_GROUP("ParticleFlags", "particle_flag_"); + ADD_GROUP("Particle Flags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index e9bc28873b..2bd108e897 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -494,11 +494,11 @@ int64_t DisplayServer::window_get_native_handle(HandleType p_handle_type, Window } void DisplayServer::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { - WARN_PRINT("Changing the VSync mode is not supported by this display server."); + WARN_PRINT("Changing the V-Sync mode is not supported by this display server."); } DisplayServer::VSyncMode DisplayServer::window_get_vsync_mode(WindowID p_window) const { - WARN_PRINT("Changing the VSync mode is not supported by this display server."); + WARN_PRINT("Changing the V-Sync mode is not supported by this display server."); return VSyncMode::VSYNC_ENABLED; } diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 41aae36785..26768e300c 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -147,6 +147,16 @@ PhysicsDirectBodyState2D::PhysicsDirectBodyState2D() {} /////////////////////////////////////////////////////// +Ref<PhysicsRayQueryParameters2D> PhysicsRayQueryParameters2D::create(Vector2 p_from, Vector2 p_to, uint32_t p_mask, const Vector<RID> &p_exclude) { + Ref<PhysicsRayQueryParameters2D> params; + params.instantiate(); + params->set_from(p_from); + params->set_to(p_to); + params->set_collision_mask(p_mask); + params->set_exclude(p_exclude); + return params; +} + void PhysicsRayQueryParameters2D::set_exclude(const Vector<RID> &p_exclude) { parameters.exclude.clear(); for (int i = 0; i < p_exclude.size(); i++) { @@ -165,6 +175,8 @@ Vector<RID> PhysicsRayQueryParameters2D::get_exclude() const { } void PhysicsRayQueryParameters2D::_bind_methods() { + ClassDB::bind_static_method("PhysicsRayQueryParameters2D", D_METHOD("create", "from", "to", "collision_mask", "exclude"), &PhysicsRayQueryParameters2D::create, DEFVAL(UINT32_MAX), DEFVAL(Vector<RID>())); + ClassDB::bind_method(D_METHOD("set_from", "from"), &PhysicsRayQueryParameters2D::set_from); ClassDB::bind_method(D_METHOD("get_from"), &PhysicsRayQueryParameters2D::get_from); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 7a821c64e6..6d95c591c2 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -605,6 +605,7 @@ protected: static void _bind_methods(); public: + static Ref<PhysicsRayQueryParameters2D> create(Vector2 p_from, Vector2 p_to, uint32_t p_mask, const Vector<RID> &p_exclude); const PhysicsDirectSpaceState2D::RayParameters &get_parameters() const { return parameters; } void set_from(const Vector2 &p_from) { parameters.from = p_from; } diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index a606d055f7..f25db22e66 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -184,6 +184,8 @@ Vector<RID> PhysicsRayQueryParameters3D::get_exclude() const { } void PhysicsRayQueryParameters3D::_bind_methods() { + ClassDB::bind_static_method("PhysicsRayQueryParameters3D", D_METHOD("create", "from", "to", "collision_mask", "exclude"), &PhysicsRayQueryParameters3D::create, DEFVAL(UINT32_MAX), DEFVAL(Vector<RID>())); + ClassDB::bind_method(D_METHOD("set_from", "from"), &PhysicsRayQueryParameters3D::set_from); ClassDB::bind_method(D_METHOD("get_from"), &PhysicsRayQueryParameters3D::get_from); @@ -220,6 +222,16 @@ void PhysicsRayQueryParameters3D::_bind_methods() { /////////////////////////////////////////////////////// +Ref<PhysicsRayQueryParameters3D> PhysicsRayQueryParameters3D::create(Vector3 p_from, Vector3 p_to, uint32_t p_mask, const Vector<RID> &p_exclude) { + Ref<PhysicsRayQueryParameters3D> params; + params.instantiate(); + params->set_from(p_from); + params->set_to(p_to); + params->set_collision_mask(p_mask); + params->set_exclude(p_exclude); + return params; +} + void PhysicsPointQueryParameters3D::set_exclude(const Vector<RID> &p_exclude) { parameters.exclude.clear(); for (int i = 0; i < p_exclude.size(); i++) { diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 86a41d96ef..837073409a 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -816,6 +816,7 @@ protected: static void _bind_methods(); public: + static Ref<PhysicsRayQueryParameters3D> create(Vector3 p_from, Vector3 p_to, uint32_t p_mask, const Vector<RID> &p_exclude); const PhysicsDirectSpaceState3D::RayParameters &get_parameters() const { return parameters; } void set_from(const Vector3 &p_from) { parameters.from = p_from; } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 827cce6047..c3af0868e0 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -2265,15 +2265,18 @@ void RenderForwardClustered::_update_render_base_uniform_set() { case RS::DECAL_FILTER_NEAREST: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; case RS::DECAL_FILTER_LINEAR: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::DECAL_FILTER_LINEAR_MIPMAPS: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; @@ -2292,15 +2295,18 @@ void RenderForwardClustered::_update_render_base_uniform_set() { case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; @@ -3275,12 +3281,18 @@ void RenderForwardClustered::_update_shader_quality_settings() { sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; sc.constant_id = SPEC_CONSTANT_DECAL_FILTER; - sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; + sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || + decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || + decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || + decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; spec_constants.push_back(sc); sc.constant_id = SPEC_CONSTANT_PROJECTOR_FILTER; - sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; + sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; spec_constants.push_back(sc); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 15f810fb3b..f74ad7617d 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1214,15 +1214,18 @@ void RenderForwardMobile::_update_render_base_uniform_set() { case RS::DECAL_FILTER_NEAREST: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; case RS::DECAL_FILTER_LINEAR: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::DECAL_FILTER_LINEAR_MIPMAPS: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; @@ -1241,15 +1244,18 @@ void RenderForwardMobile::_update_render_base_uniform_set() { case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } break; @@ -2558,12 +2564,18 @@ void RenderForwardMobile::_update_shader_quality_settings() { sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; sc.constant_id = SPEC_CONSTANT_DECAL_USE_MIPMAPS; - sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; + sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || + decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || + decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || + decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; spec_constants.push_back(sc); sc.constant_id = SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS; - sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; + sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || + light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; spec_constants.push_back(sc); diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index d85ab3af2e..246ef81cb2 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -182,17 +182,18 @@ void main() { if (found) { float margin_blend = 1.0; - vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.5 * 0.05); // make a uniform margin - if (any(bvec4(lessThan(pos, -margin), greaterThan(pos, params.screen_size + margin)))) { - // clip outside screen + margin + vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // make a uniform margin + if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, params.screen_size)))) { + // clip at the screen edges imageStore(ssr_image, ssC, vec4(0.0)); return; } { - //blend fading out towards external margin - vec2 margin_grad = mix(pos - params.screen_size, -pos, lessThan(pos, vec2(0.0))); - margin_blend = 1.0 - smoothstep(0.0, margin.x, max(margin_grad.x, margin_grad.y)); + //blend fading out towards inner margin + // 0.5 = midpoint of reflection + vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5)); + margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y); //margin_blend = 1.0; } diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 04ecfcfdce..8a69ee45f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -159,7 +159,7 @@ private: real_t randomness = 0.0; bool restart_request = false; AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); - bool use_local_coords = true; + bool use_local_coords = false; bool has_collision_cache = false; bool has_sdf_collision = false; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 7de175647b..019f10fe38 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -7158,9 +7158,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!n) { return ERR_PARSE_ERROR; } - if (n->get_datatype() != TYPE_INT) { - _set_error(RTR("Expected an integer expression.")); - return ERR_PARSE_ERROR; + { + const ShaderLanguage::DataType switch_type = n->get_datatype(); + if (switch_type != TYPE_INT && switch_type != TYPE_UINT) { + _set_error(RTR("Expected an integer expression.")); + return ERR_PARSE_ERROR; + } } tk = _get_token(); if (tk.type != TK_PARENTHESIS_CLOSE) { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index bb76281782..e02d82fbc3 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1885,9 +1885,10 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("light_projectors_set_filter", "filter"), &RenderingServer::light_projectors_set_filter); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST); - BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR); + BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS); + BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC); BIND_ENUM_CONSTANT(LIGHT_DIRECTIONAL); @@ -1989,9 +1990,10 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(DECAL_TEXTURE_MAX); BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST); - BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS); BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR); + BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS); BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR_MIPMAPS); + BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC); BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC); /* GI API (affects VoxelGI and SDFGI) */ @@ -2956,9 +2958,9 @@ void RenderingServer::init() { PROPERTY_HINT_RANGE, "-2,2,0.001")); GLOBAL_DEF("rendering/textures/decals/filter", DECAL_FILTER_LINEAR_MIPMAPS); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/decals/filter", PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Nearest+Mipmaps,Linear,Linear+Mipmaps,Linear+Mipmaps Anisotropic (Slow)")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/decals/filter", PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)")); GLOBAL_DEF("rendering/textures/light_projectors/filter", LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/light_projectors/filter", PropertyInfo(Variant::INT, "rendering/textures/light_projectors/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Nearest+Mipmaps,Linear,Linear+Mipmaps,Linear+Mipmaps Anisotropic (Slow)")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/light_projectors/filter", PropertyInfo(Variant::INT, "rendering/textures/light_projectors/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)")); GLOBAL_DEF_RST("rendering/occlusion_culling/occlusion_rays_per_thread", 512); GLOBAL_DEF_RST("rendering/occlusion_culling/bvh_build_quality", 2); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 1f2012ac8d..845a4a7a3e 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -495,9 +495,10 @@ public: enum LightProjectorFilter { LIGHT_PROJECTOR_FILTER_NEAREST, - LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS, LIGHT_PROJECTOR_FILTER_LINEAR, + LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS, LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS, + LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC, LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC, }; @@ -557,9 +558,10 @@ public: enum DecalFilter { DECAL_FILTER_NEAREST, - DECAL_FILTER_NEAREST_MIPMAPS, DECAL_FILTER_LINEAR, + DECAL_FILTER_NEAREST_MIPMAPS, DECAL_FILTER_LINEAR_MIPMAPS, + DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC, DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC, }; diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h new file mode 100644 index 0000000000..697bf63fc5 --- /dev/null +++ b/tests/core/math/test_transform_2d.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* test_transform_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_TRANSFORM_2D_H +#define TEST_TRANSFORM_2D_H + +#include "core/math/transform_2d.h" + +#include "tests/test_macros.h" + +namespace TestTransform2D { + +Transform2D create_dummy_transform() { + return Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)); +} + +Transform2D identity() { + return Transform2D(); +} + +TEST_CASE("[Transform2D] translation") { + Vector2 offset = Vector2(1, 2); + + // Both versions should give the same result applied to identity. + CHECK(identity().translated(offset) == identity().translated_local(offset)); + + // Check both versions against left and right multiplications. + Transform2D orig = create_dummy_transform(); + Transform2D T = identity().translated(offset); + CHECK(orig.translated(offset) == T * orig); + CHECK(orig.translated_local(offset) == orig * T); +} + +TEST_CASE("[Transform2D] scaling") { + Vector2 scaling = Vector2(1, 2); + + // Both versions should give the same result applied to identity. + CHECK(identity().scaled(scaling) == identity().scaled_local(scaling)); + + // Check both versions against left and right multiplications. + Transform2D orig = create_dummy_transform(); + Transform2D S = identity().scaled(scaling); + CHECK(orig.scaled(scaling) == S * orig); + CHECK(orig.scaled_local(scaling) == orig * S); +} + +TEST_CASE("[Transform2D] rotation") { + real_t phi = 1.0; + + // Both versions should give the same result applied to identity. + CHECK(identity().rotated(phi) == identity().rotated_local(phi)); + + // Check both versions against left and right multiplications. + Transform2D orig = create_dummy_transform(); + Transform2D R = identity().rotated(phi); + CHECK(orig.rotated(phi) == R * orig); + CHECK(orig.rotated_local(phi) == orig * R); +} +} // namespace TestTransform2D + +#endif // TEST_TRANSFORM_2D_H diff --git a/tests/core/math/test_transform_3d.h b/tests/core/math/test_transform_3d.h new file mode 100644 index 0000000000..da166b43f7 --- /dev/null +++ b/tests/core/math/test_transform_3d.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* test_transform_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_TRANSFORM_3D_H +#define TEST_TRANSFORM_3D_H + +#include "core/math/transform_3d.h" + +#include "tests/test_macros.h" + +namespace TestTransform3D { + +Transform3D create_dummy_transform() { + return Transform3D(Basis(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9)), Vector3(10, 11, 12)); +} + +Transform3D identity() { + return Transform3D(); +} + +TEST_CASE("[Transform3D] translation") { + Vector3 offset = Vector3(1, 2, 3); + + // Both versions should give the same result applied to identity. + CHECK(identity().translated(offset) == identity().translated_local(offset)); + + // Check both versions against left and right multiplications. + Transform3D orig = create_dummy_transform(); + Transform3D T = identity().translated(offset); + CHECK(orig.translated(offset) == T * orig); + CHECK(orig.translated_local(offset) == orig * T); +} + +TEST_CASE("[Transform3D] scaling") { + Vector3 scaling = Vector3(1, 2, 3); + + // Both versions should give the same result applied to identity. + CHECK(identity().scaled(scaling) == identity().scaled_local(scaling)); + + // Check both versions against left and right multiplications. + Transform3D orig = create_dummy_transform(); + Transform3D S = identity().scaled(scaling); + CHECK(orig.scaled(scaling) == S * orig); + CHECK(orig.scaled_local(scaling) == orig * S); +} + +TEST_CASE("[Transform3D] rotation") { + Vector3 axis = Vector3(1, 2, 3).normalized(); + real_t phi = 1.0; + + // Both versions should give the same result applied to identity. + CHECK(identity().rotated(axis, phi) == identity().rotated_local(axis, phi)); + + // Check both versions against left and right multiplications. + Transform3D orig = create_dummy_transform(); + Transform3D R = identity().rotated(axis, phi); + CHECK(orig.rotated(axis, phi) == R * orig); + CHECK(orig.rotated_local(axis, phi) == orig * R); +} +} // namespace TestTransform3D + +#endif // TEST_TRANSFORM_3D_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 16654181c6..d533ce4f63 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -49,6 +49,8 @@ #include "tests/core/math/test_random_number_generator.h" #include "tests/core/math/test_rect2.h" #include "tests/core/math/test_rect2i.h" +#include "tests/core/math/test_transform_2d.h" +#include "tests/core/math/test_transform_3d.h" #include "tests/core/math/test_vector2.h" #include "tests/core/math/test_vector2i.h" #include "tests/core/math/test_vector3.h" |