diff options
219 files changed, 2744 insertions, 1331 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index ce4f757a19..bfda7c72f8 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -82,10 +82,8 @@ jobs: sudo cp -f misc/ci/sources.list /etc/apt/sources.list sudo apt-get update # The actual dependencies - sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ - libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \ - libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip \ - llvm libspeechd-dev speech-dispatcher fontconfig libfontconfig-dev libxkbcommon-dev + sudo apt-get install build-essential pkg-config libgl1-mesa-dev libglu-dev \ + xvfb wget unzip llvm - name: Setup Godot build cache uses: ./.github/actions/godot-cache diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index db3191334a..00e74a7cce 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1286,6 +1286,10 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::STRING, "editor/script/templates_search_path", PROPERTY_HINT_DIR), "res://script_templates"); + // For correct doc generation. + GLOBAL_DEF("editor/naming/default_signal_callback_name", "_on_{node_name}_{signal_name}"); + GLOBAL_DEF("editor/naming/default_signal_callback_to_self_name", "_on_{signal_name}"); + _add_builtin_input_map(); // Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. diff --git a/core/core_constants.cpp b/core/core_constants.cpp index e28f7bfc4f..b1f56539e5 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -586,7 +586,8 @@ void register_global_constants() { BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_ENUM); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NIL_IS_VARIANT); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ARRAY); - BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE); + BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ALWAYS_DUPLICATE); + BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NEVER_DUPLICATE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_HIGH_END_GFX); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT); diff --git a/core/input/input.cpp b/core/input/input.cpp index 1ea9f00fee..3cf83fd64b 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -349,8 +349,8 @@ float Input::get_axis(const StringName &p_negative_action, const StringName &p_p Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone) const { Vector2 vector = Vector2( - get_action_raw_strength(p_positive_x) - get_action_raw_strength(p_negative_x), - get_action_raw_strength(p_positive_y) - get_action_raw_strength(p_negative_y)); + get_action_strength(p_positive_x) - get_action_strength(p_negative_x), + get_action_strength(p_positive_y) - get_action_strength(p_negative_y)); if (p_deadzone < 0.0f) { // If the deadzone isn't specified, get it from the average of the actions. diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index dbe9b55ee3..74c0812f43 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -478,7 +478,6 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) { Ref<InputEventKey> ie; ie.instantiate(); ie->set_keycode(p_keycode & KeyModifierMask::CODE_MASK); - ie->set_key_label(p_keycode & KeyModifierMask::CODE_MASK); ie->set_unicode(char32_t(p_keycode & KeyModifierMask::CODE_MASK)); if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) { diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 6d3575b9fa..e44bbc246b 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -262,7 +262,7 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { r->set(E.name, p.duplicate(p_subresources)); - } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { + } else if (p.get_type() == Variant::OBJECT && !(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { Ref<Resource> sr = p; if (sr.is_valid()) { r->set(E.name, sr->duplicate(p_subresources)); diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 4786110054..590483bf20 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -151,7 +151,7 @@ void Geometry3D::MeshData::optimize_vertices() { } } - for (MeshData::Edge edge : edges) { + for (MeshData::Edge &edge : edges) { int a = edge.vertex_a; int b = edge.vertex_b; diff --git a/core/object/object.cpp b/core/object/object.cpp index 2cb56dfe6c..a8b9e00c96 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -499,7 +499,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); if (!is_class("Script")) { // can still be set, but this is for user-friendliness - p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NEVER_DUPLICATE)); } if (script_instance && !p_reversed) { diff --git a/core/object/object.h b/core/object/object.h index f78c7c34fd..ec77da4ee1 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -109,15 +109,16 @@ enum PropertyUsageFlags { PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16, PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17, PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array. - PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 19, // If the object is duplicated also this property will be duplicated. - PROPERTY_USAGE_HIGH_END_GFX = 1 << 20, - PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 21, - PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 22, - PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 23, // Used in inspector to increment property when keyed in animation player. - PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 24, // when loading, the resource for this property can be set at the end of loading. - PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 25, // For Object properties, instantiate them when creating in editor. - PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 26, //for project or editor settings, show when basic settings are selected. - PROPERTY_USAGE_READ_ONLY = 1 << 27, // Mark a property as read-only in the inspector. + PROPERTY_USAGE_ALWAYS_DUPLICATE = 1 << 19, // When duplicating a resource, always duplicate, even with subresource duplication disabled. + PROPERTY_USAGE_NEVER_DUPLICATE = 1 << 20, // When duplicating a resource, never duplicate, even with subresource duplication enabled. + PROPERTY_USAGE_HIGH_END_GFX = 1 << 21, + PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 22, + PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 23, + PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 24, // Used in inspector to increment property when keyed in animation player. + PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // when loading, the resource for this property can be set at the end of loading. + PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor. + PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected. + PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector. PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE, diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 6315356510..84017e89a6 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -124,8 +124,6 @@ enum class Key { KP_7 = SPECIAL | 0x8D, KP_8 = SPECIAL | 0x8E, KP_9 = SPECIAL | 0x8F, - SUPER_L = SPECIAL | 0x40, - SUPER_R = SPECIAL | 0x41, MENU = SPECIAL | 0x42, HYPER = SPECIAL | 0x43, HELP = SPECIAL | 0x45, diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index ca42738b05..672b030806 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3492,6 +3492,46 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const } } +bool Variant::identity_compare(const Variant &p_variant) const { + if (type != p_variant.type) { + return false; + } + + switch (type) { + case OBJECT: { + return _get_obj().obj == p_variant._get_obj().obj; + } break; + + case DICTIONARY: { + const Dictionary &l = *(reinterpret_cast<const Dictionary *>(_data._mem)); + const Dictionary &r = *(reinterpret_cast<const Dictionary *>(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case ARRAY: { + const Array &l = *(reinterpret_cast<const Array *>(_data._mem)); + const Array &r = *(reinterpret_cast<const Array *>(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case PACKED_BYTE_ARRAY: + case PACKED_INT32_ARRAY: + case PACKED_INT64_ARRAY: + case PACKED_FLOAT32_ARRAY: + case PACKED_FLOAT64_ARRAY: + case PACKED_STRING_ARRAY: + case PACKED_VECTOR2_ARRAY: + case PACKED_VECTOR3_ARRAY: + case PACKED_COLOR_ARRAY: { + return _data.packed_array == p_variant._data.packed_array; + } break; + + default: { + return hash_compare(p_variant); + } + } +} + bool StringLikeVariantComparator::compare(const Variant &p_lhs, const Variant &p_rhs) { if (p_lhs.hash_compare(p_rhs)) { return true; diff --git a/core/variant/variant.h b/core/variant/variant.h index b9294de77d..b2f31a6d57 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -748,6 +748,7 @@ public: uint32_t recursive_hash(int recursion_count) const; bool hash_compare(const Variant &p_variant, int recursion_count = 0) const; + bool identity_compare(const Variant &p_variant) const; bool booleanize() const; String stringify(int recursion_count = 0) const; String to_json_string() const; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index fe7150bca9..042ebe368a 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -1007,9 +1007,14 @@ struct VariantUtilityFunctions { static inline uint64_t rid_allocate_id() { return RID_AllocBase::_gen_id(); } + static inline RID rid_from_int64(uint64_t p_base) { return RID::from_uint64(p_base); } + + static inline bool is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); + } }; #ifdef DEBUG_METHODS_ENABLED @@ -1601,6 +1606,8 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(rid_allocate_id, Vector<String>(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(rid_from_int64, sarray("base"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(is_same, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_GENERAL); } void Variant::_unregister_variant_utility_functions() { diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 2be15d5100..ecb6a83c8a 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -525,6 +525,31 @@ Returns [code]true[/code] if [param x] is a NaN ("Not a Number" or invalid) value. </description> </method> + <method name="is_same"> + <return type="bool" /> + <param index="0" name="a" type="Variant" /> + <param index="1" name="b" type="Variant" /> + <description> + Returns [code]true[/code], for value types, if [param a] and [param b] share the same value. Returns [code]true[/code], for reference types, if the references of [param a] and [param b] are the same. + [codeblock] + # Vector2 is a value type + var vec2_a = Vector2(0, 0) + var vec2_b = Vector2(0, 0) + var vec2_c = Vector2(1, 1) + is_same(vec2_a, vec2_a) # true + is_same(vec2_a, vec2_b) # true + is_same(vec2_a, vec2_c) # false + + # Array is a reference type + var arr_a = [] + var arr_b = [] + is_same(arr_a, arr_a) # true + is_same(arr_a, arr_b) # false + [/codeblock] + These are [Variant] value types: [code]null[/code], [bool], [int], [float], [String], [StringName], [Vector2], [Vector2i], [Vector3], [Vector3i], [Vector4], [Vector4i], [Rect2], [Rect2i], [Transform2D], [Transform3D], [Plane], [Quaternion], [AABB], [Basis], [Projection], [Color], [NodePath], [RID], [Callable] and [Signal]. + These are [Variant] reference types: [Object], [Dictionary], [Array], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], [PackedFloat64Array], [PackedStringArray], [PackedVector2Array], [PackedVector3Array] and [PackedColorArray]. + </description> + </method> <method name="is_zero_approx"> <return type="bool" /> <param index="0" name="x" type="float" /> @@ -2664,25 +2689,28 @@ <constant name="PROPERTY_USAGE_ARRAY" value="262144" enum="PropertyUsageFlags" is_bitfield="true"> The property is an array. </constant> - <constant name="PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE" value="524288" enum="PropertyUsageFlags" is_bitfield="true"> - If the property is a [Resource], a new copy of it is always created when calling [method Node.duplicate] or [method Resource.duplicate]. + <constant name="PROPERTY_USAGE_ALWAYS_DUPLICATE" value="524288" enum="PropertyUsageFlags" is_bitfield="true"> + When duplicating a resource with [method Resource.duplicate], and this flag is set on a property of that resource, the property should always be duplicated, regardless of the [code]subresources[/code] bool parameter. + </constant> + <constant name="PROPERTY_USAGE_NEVER_DUPLICATE" value="1048576" enum="PropertyUsageFlags" is_bitfield="true"> + When duplicating a resource with [method Resource.duplicate], and this flag is set on a property of that resource, the property should never be duplicated, regardless of the [code]subresources[/code] bool parameter. </constant> - <constant name="PROPERTY_USAGE_HIGH_END_GFX" value="1048576" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_HIGH_END_GFX" value="2097152" enum="PropertyUsageFlags" is_bitfield="true"> The property is only shown in the editor if modern renderers are supported (GLES3 is excluded). </constant> - <constant name="PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" value="2097152" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" value="4194304" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" value="4194304" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" value="8388608" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_KEYING_INCREMENTS" value="8388608" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_KEYING_INCREMENTS" value="16777216" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="16777216" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="33554432" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" value="33554432" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" value="67108864" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="67108864" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="134217728" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_READ_ONLY" value="134217728" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags" is_bitfield="true"> The property is read-only in the [EditorInspector]. </constant> <constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true"> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 304caeef43..c164fe4363 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -15,6 +15,17 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="_post_process_key_value" qualifiers="virtual const"> + <return type="Variant" /> + <param index="0" name="animation" type="Animation" /> + <param index="1" name="track" type="int" /> + <param index="2" name="value" type="Variant" /> + <param index="3" name="object" type="Object" /> + <param index="4" name="object_idx" type="int" /> + <description> + A virtual function for processing after key getting during playback. + </description> + </method> <method name="add_animation_library"> <return type="int" enum="Error" /> <param index="0" name="name" type="StringName" /> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 49bc4ee6af..3a3e8bb1fa 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -12,6 +12,17 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="_post_process_key_value" qualifiers="virtual const"> + <return type="Variant" /> + <param index="0" name="animation" type="Animation" /> + <param index="1" name="track" type="int" /> + <param index="2" name="value" type="Variant" /> + <param index="3" name="object" type="Object" /> + <param index="4" name="object_idx" type="int" /> + <description> + A virtual function for processing after key getting during playback. + </description> + </method> <method name="advance"> <return type="void" /> <param index="0" name="delta" type="float" /> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 7b86afcc4c..6dc66194b8 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -206,6 +206,7 @@ Overrides the [AABB] with one defined by user for use with frustum culling. Especially useful to avoid unexpected culling when using a shader to offset vertices. </member> <member name="shadow_mesh" type="ArrayMesh" setter="set_shadow_mesh" getter="get_shadow_mesh"> + An optional mesh which is used for rendering shadows and can be used for the depth prepass. Can be used to increase performance of shadow rendering by using a mesh that only contains vertex position data (without normals, UVs, colors, etc.). </member> </members> </class> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 7082eff97d..0d675112b8 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -383,7 +383,9 @@ <method name="get_global_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns the position and size of the control relative to the [CanvasLayer]. See [member global_position] and [member size]. + Returns the position and size of the control relative to the containing canvas. See [member global_position] and [member size]. + [b]Note:[/b] If the node itself or any parent [CanvasItem] between the node and the canvas have a non default rotation or skew, the resulting size is likely not meaningful. + [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2]. </description> </method> <method name="get_minimum_size" qualifiers="const"> @@ -414,7 +416,9 @@ <method name="get_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns the position and size of the control relative to the top-left corner of the parent Control. See [member position] and [member size]. + Returns the position and size of the control in the coordinate system of the containing node. See [member position], [member scale] and [member size]. + [b]Note:[/b] If [member rotation] is not the default rotation, the resulting size is not meaningful. + [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2]. </description> </method> <method name="get_screen_position" qualifiers="const"> @@ -895,6 +899,7 @@ <param index="0" name="position" type="Vector2" /> <description> Moves the mouse cursor to [param position], relative to [member position] of this [Control]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> @@ -991,7 +996,7 @@ By default, the node's pivot is its top-left corner. When you change its [member rotation] or [member scale], it will rotate or scale around this pivot. Set this property to [member size] / 2 to pivot around the Control's center. </member> <member name="position" type="Vector2" setter="_set_position" getter="get_position" default="Vector2(0, 0)"> - The node's position, relative to its parent. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. + The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. </member> <member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0"> The node's rotation around its pivot, in radians. See [member pivot_offset] to change the pivot's position. @@ -1008,7 +1013,7 @@ The [Node] which must be a parent of the focused [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused. </member> <member name="size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)"> - The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically. + The size of the node's bounding rectangle, in the node's coordinate system. [Container] nodes update this property automatically. </member> <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" enum="Control.SizeFlags" default="1"> Tells the parent [Container] nodes how they should resize and place the node on the X axis. Use one of the [enum SizeFlags] constants to change the flags. See the constants to learn what each does. diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 5f99ba82b8..03e5b5d1d8 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -42,7 +42,7 @@ You can access a dictionary's value by referencing its corresponding key. In the above example, [code]points_dict["White"][/code] will return [code]50[/code]. You can also write [code]points_dict.White[/code], which is equivalent. However, you'll have to use the bracket syntax if the key you're accessing the dictionary with isn't a fixed string (such as a number or variable). [codeblocks] [gdscript] - @export(String, "White", "Yellow", "Orange") var my_color + @export_enum("White", "Yellow", "Orange") var my_color: String var points_dict = {"White": 50, "Yellow": 75, "Orange": 100} func _ready(): # We can't use dot syntax here as `my_color` is a variable. diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index b77ec4c517..b0657cab90 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1090,6 +1090,7 @@ <param index="0" name="position" type="Vector2i" /> <description> Sets the mouse cursor position to the given [param position] relative to an origin at the upper left corner of the currently focused game Window Manager window. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> <method name="window_can_draw" qualifiers="const"> diff --git a/doc/classes/FileSystemDock.xml b/doc/classes/FileSystemDock.xml index 00f5c7ddff..f76bc2c279 100644 --- a/doc/classes/FileSystemDock.xml +++ b/doc/classes/FileSystemDock.xml @@ -51,5 +51,10 @@ <description> </description> </signal> + <signal name="resource_removed"> + <param index="0" name="resource" type="Resource" /> + <description> + </description> + </signal> </signals> </class> diff --git a/doc/classes/HeightMapShape3D.xml b/doc/classes/HeightMapShape3D.xml index 206981e547..f34870c500 100644 --- a/doc/classes/HeightMapShape3D.xml +++ b/doc/classes/HeightMapShape3D.xml @@ -14,10 +14,10 @@ Height map data, pool array must be of [member map_width] * [member map_depth] size. </member> <member name="map_depth" type="int" setter="set_map_depth" getter="get_map_depth" default="2"> - Depth of the height map data. Changing this will resize the [member map_data]. + Number of vertices in the depth of the height map. Changing this will resize the [member map_data]. </member> <member name="map_width" type="int" setter="set_map_width" getter="get_map_width" default="2"> - Width of the height map data. Changing this will resize the [member map_data]. + Number of vertices in the width of the height map. Changing this will resize the [member map_data]. </member> </members> </class> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 3b0cfb3825..70e629974d 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -375,6 +375,7 @@ <description> Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window. Mouse position is clipped to the limits of the screen resolution, or to the limits of the game window if [enum MouseMode] is set to [constant MOUSE_MODE_CONFINED] or [constant MOUSE_MODE_CONFINED_HIDDEN]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 3ce6ce41b4..020ce4f468 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -138,10 +138,10 @@ Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods. </constant> <constant name="RPC_MODE_ANY_PEER" value="1" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc(any)[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. + Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. </constant> <constant name="RPC_MODE_AUTHORITY" value="2" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc(authority)[/code] annotation. See [method Node.set_multiplayer_authority]. + Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc("authority")[/code] annotation. See [method Node.set_multiplayer_authority]. </constant> </constants> </class> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index b561748b30..6ae4dc4177 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -4,8 +4,8 @@ 2D Agent used in navigation for collision avoidance. </brief_description> <description> - 2D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent2D] is physics safe. - [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. + 2D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent2D] is physics safe. + [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. </description> <tutorials> <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link> @@ -14,13 +14,13 @@ <method name="distance_to_target" qualifiers="const"> <return type="float" /> <description> - Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. + Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate. </description> </method> <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector2Array" /> <description> - Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. + Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_path_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> <method name="get_current_navigation_path_index" qualifiers="const"> @@ -35,10 +35,10 @@ Returns the path query result for the path the agent is currently following. </description> </method> - <method name="get_final_location"> + <method name="get_final_position"> <return type="Vector2" /> <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. </description> </method> <method name="get_navigation_layer_value" qualifiers="const"> @@ -54,10 +54,10 @@ Returns the [RID] of the navigation map for this NavigationAgent node. This function returns always the map set on the NavigationAgent node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationAgent node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationAgent and also update the agent on the NavigationServer. </description> </method> - <method name="get_next_location"> + <method name="get_next_path_position"> <return type="Vector2" /> <description> - Returns the next location in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. </description> </method> <method name="get_rid" qualifiers="const"> @@ -69,19 +69,19 @@ <method name="is_navigation_finished"> <return type="bool" /> <description> - Returns true if the navigation path's final location has been reached. + Returns true if the navigation path's final position has been reached. </description> </method> <method name="is_target_reachable"> <return type="bool" /> <description> - Returns true if [member target_location] is reachable. + Returns true if [member target_position] is reachable. </description> </method> <method name="is_target_reached" qualifiers="const"> <return type="bool" /> <description> - Returns true if [member target_location] is reached. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location]. + Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position]. </description> </method> <method name="set_navigation_layer_value"> @@ -127,7 +127,7 @@ The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. </member> <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="100.0"> - The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. + The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters2D.PathMetadataFlags" default="7"> Additional information to return with the navigation path. @@ -139,8 +139,8 @@ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="10.0"> The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update. </member> - <member name="target_location" type="Vector2" setter="set_target_location" getter="get_target_location" default="Vector2(0, 0)"> - The user-defined target location. Setting this property will clear the current navigation path. + <member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2(0, 0)"> + The user-defined target position. Setting this property will clear the current navigation path. </member> <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="1.0"> The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive. @@ -152,7 +152,7 @@ <description> Notifies when a navigation link has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The start location of the link that was reached. + - [code]position[/code]: The start position of the link that was reached. - [code]type[/code]: Always [constant NavigationPathQueryResult2D.PATH_SEGMENT_TYPE_LINK]. - [code]rid[/code]: The [RID] of the link. - [code]owner[/code]: The object which manages the link (usually [NavigationLink2D]). @@ -160,7 +160,7 @@ </signal> <signal name="navigation_finished"> <description> - Notifies when the final location is reached. + Notifies when the final position is reached. </description> </signal> <signal name="path_changed"> @@ -170,7 +170,7 @@ </signal> <signal name="target_reached"> <description> - Notifies when the player-defined [member target_location] is reached. + Notifies when the player-defined [member target_position] is reached. </description> </signal> <signal name="velocity_computed"> @@ -184,7 +184,7 @@ <description> Notifies when a waypoint along the path has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The location of the waypoint that was reached. + - [code]position[/code]: The position of the waypoint that was reached. - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index a1b007ee56..a22cd6dd46 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -4,8 +4,8 @@ 3D Agent used in navigation for collision avoidance. </brief_description> <description> - 3D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent3D] is physics safe. - [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. + 3D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent3D] is physics safe. + [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. </description> <tutorials> <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link> @@ -14,13 +14,13 @@ <method name="distance_to_target" qualifiers="const"> <return type="float" /> <description> - Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. + Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate. </description> </method> <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector3Array" /> <description> - Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. + Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_path_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> <method name="get_current_navigation_path_index" qualifiers="const"> @@ -35,10 +35,10 @@ Returns the path query result for the path the agent is currently following. </description> </method> - <method name="get_final_location"> + <method name="get_final_position"> <return type="Vector3" /> <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. </description> </method> <method name="get_navigation_layer_value" qualifiers="const"> @@ -54,10 +54,10 @@ Returns the [RID] of the navigation map for this NavigationAgent node. This function returns always the map set on the NavigationAgent node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationAgent node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationAgent and also update the agent on the NavigationServer. </description> </method> - <method name="get_next_location"> + <method name="get_next_path_position"> <return type="Vector3" /> <description> - Returns the next location in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. </description> </method> <method name="get_rid" qualifiers="const"> @@ -69,19 +69,19 @@ <method name="is_navigation_finished"> <return type="bool" /> <description> - Returns true if the navigation path's final location has been reached. + Returns true if the navigation path's final position has been reached. </description> </method> <method name="is_target_reachable"> <return type="bool" /> <description> - Returns true if [member target_location] is reachable. + Returns true if [member target_position] is reachable. </description> </method> <method name="is_target_reached" qualifiers="const"> <return type="bool" /> <description> - Returns true if [member target_location] is reached. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location]. + Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position]. </description> </method> <method name="set_navigation_layer_value"> @@ -133,7 +133,7 @@ The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. </member> <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0"> - The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. + The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters3D.PathMetadataFlags" default="7"> Additional information to return with the navigation path. @@ -145,8 +145,8 @@ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0"> The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update. </member> - <member name="target_location" type="Vector3" setter="set_target_location" getter="get_target_location" default="Vector3(0, 0, 0)"> - The user-defined target location. Setting this property will clear the current navigation path. + <member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3(0, 0, 0)"> + The user-defined target position. Setting this property will clear the current navigation path. </member> <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="5.0"> The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive. @@ -158,7 +158,7 @@ <description> Notifies when a navigation link has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The start location of the link that was reached. + - [code]position[/code]: The start position of the link that was reached. - [code]type[/code]: Always [constant NavigationPathQueryResult3D.PATH_SEGMENT_TYPE_LINK]. - [code]rid[/code]: The [RID] of the link. - [code]owner[/code]: The object which manages the link (usually [NavigationLink3D]). @@ -166,7 +166,7 @@ </signal> <signal name="navigation_finished"> <description> - Notifies when the final location is reached. + Notifies when the final position is reached. </description> </signal> <signal name="path_changed"> @@ -176,7 +176,7 @@ </signal> <signal name="target_reached"> <description> - Notifies when the player-defined [member target_location] is reached. + Notifies when the player-defined [member target_position] is reached. </description> </signal> <signal name="velocity_computed"> @@ -190,7 +190,7 @@ <description> Notifies when a waypoint along the path has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The location of the waypoint that was reached. + - [code]position[/code]: The position of the waypoint that was reached. - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index 44d2110a7c..b3f4367675 100644 --- a/doc/classes/NavigationLink2D.xml +++ b/doc/classes/NavigationLink2D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NavigationLink2D" inherits="Node2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Creates a link between two locations that [NavigationServer2D] can route agents through. + Creates a link between two positions that [NavigationServer2D] can route agents through. </brief_description> <description> - Creates a link between two locations that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + Creates a link between two positions that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. </description> <tutorials> <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link> @@ -28,12 +28,12 @@ </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> - Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + Whether this link can be traveled in both directions or only from [member start_position] to [member end_position]. </member> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> Whether this link is currently active. If [code]false[/code], [method NavigationServer2D.map_get_path] will ignore this link. </member> - <member name="end_location" type="Vector2" setter="set_end_location" getter="get_end_location" default="Vector2(0, 0)"> + <member name="end_position" type="Vector2" setter="set_end_position" getter="get_end_position" default="Vector2(0, 0)"> Ending position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. @@ -44,7 +44,7 @@ <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer2D.map_get_path]. </member> - <member name="start_location" type="Vector2" setter="set_start_location" getter="get_start_location" default="Vector2(0, 0)"> + <member name="start_position" type="Vector2" setter="set_start_position" getter="get_start_position" default="Vector2(0, 0)"> Starting position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 4aa5801afb..4dff226042 100644 --- a/doc/classes/NavigationLink3D.xml +++ b/doc/classes/NavigationLink3D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NavigationLink3D" inherits="Node3D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Creates a link between two locations that [NavigationServer3D] can route agents through. + Creates a link between two positions that [NavigationServer3D] can route agents through. </brief_description> <description> - Creates a link between two locations that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + Creates a link between two positions that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. </description> <tutorials> <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link> @@ -28,12 +28,12 @@ </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> - Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + Whether this link can be traveled in both directions or only from [member start_position] to [member end_position]. </member> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> Whether this link is currently active. If [code]false[/code], [method NavigationServer3D.map_get_path] will ignore this link. </member> - <member name="end_location" type="Vector3" setter="set_end_location" getter="get_end_location" default="Vector3(0, 0, 0)"> + <member name="end_position" type="Vector3" setter="set_end_position" getter="get_end_position" default="Vector3(0, 0, 0)"> Ending position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. @@ -44,7 +44,7 @@ <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer3D.map_get_path]. </member> - <member name="start_location" type="Vector3" setter="set_start_location" getter="get_start_location" default="Vector3(0, 0, 0)"> + <member name="start_position" type="Vector3" setter="set_start_position" getter="get_start_position" default="Vector3(0, 0, 0)"> Starting position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 1ba949b294..95e3fded36 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -137,14 +137,14 @@ <method name="link_create"> <return type="RID" /> <description> - Create a new link between two locations on a map. + Create a new link between two positions on a map. </description> </method> - <method name="link_get_end_location" qualifiers="const"> + <method name="link_get_end_position" qualifiers="const"> <return type="Vector2" /> <param index="0" name="link" type="RID" /> <description> - Returns the ending location of this [code]link[/code]. + Returns the ending position of this [code]link[/code]. </description> </method> <method name="link_get_enter_cost" qualifiers="const"> @@ -175,11 +175,11 @@ Returns the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_get_start_location" qualifiers="const"> + <method name="link_get_start_position" qualifiers="const"> <return type="Vector2" /> <param index="0" name="link" type="RID" /> <description> - Returns the starting location of this [code]link[/code]. + Returns the starting position of this [code]link[/code]. </description> </method> <method name="link_get_travel_cost" qualifiers="const"> @@ -204,12 +204,12 @@ Sets whether this [code]link[/code] can be travelled in both directions. </description> </method> - <method name="link_set_end_location"> + <method name="link_set_end_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector2" /> + <param index="1" name="position" type="Vector2" /> <description> - Sets the exit location for the [code]link[/code]. + Sets the exit position for the [code]link[/code]. </description> </method> <method name="link_set_enter_cost"> @@ -244,12 +244,12 @@ Set the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_set_start_location"> + <method name="link_set_start_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector2" /> + <param index="1" name="position" type="Vector2" /> <description> - Sets the entry location for this [code]link[/code]. + Sets the entry position for this [code]link[/code]. </description> </method> <method name="link_set_travel_cost"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index e007c71342..fd20f780b3 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -144,14 +144,14 @@ <method name="link_create"> <return type="RID" /> <description> - Create a new link between two locations on a map. + Create a new link between two positions on a map. </description> </method> - <method name="link_get_end_location" qualifiers="const"> + <method name="link_get_end_position" qualifiers="const"> <return type="Vector3" /> <param index="0" name="link" type="RID" /> <description> - Returns the ending location of this [code]link[/code]. + Returns the ending position of this [code]link[/code]. </description> </method> <method name="link_get_enter_cost" qualifiers="const"> @@ -182,11 +182,11 @@ Returns the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_get_start_location" qualifiers="const"> + <method name="link_get_start_position" qualifiers="const"> <return type="Vector3" /> <param index="0" name="link" type="RID" /> <description> - Returns the starting location of this [code]link[/code]. + Returns the starting position of this [code]link[/code]. </description> </method> <method name="link_get_travel_cost" qualifiers="const"> @@ -211,12 +211,12 @@ Sets whether this [code]link[/code] can be travelled in both directions. </description> </method> - <method name="link_set_end_location"> + <method name="link_set_end_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector3" /> + <param index="1" name="position" type="Vector3" /> <description> - Sets the exit location for the [code]link[/code]. + Sets the exit position for the [code]link[/code]. </description> </method> <method name="link_set_enter_cost"> @@ -251,12 +251,12 @@ Set the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_set_start_location"> + <method name="link_set_start_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector3" /> + <param index="1" name="position" type="Vector3" /> <description> - Sets the entry location for this [code]link[/code]. + Sets the entry position for this [code]link[/code]. </description> </method> <method name="link_set_travel_cost"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 7b27f16d82..7c40c189c0 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -666,7 +666,7 @@ channel = 0, } [/codeblock] - See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc(any)[/code], [code]@rpc(authority)[/code]). By default, methods are not exposed to networking (and RPCs). + See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc("any")[/code], [code]@rpc("authority")[/code]). By default, methods are not exposed to networking (and RPCs). </description> </method> <method name="rpc_id" qualifiers="vararg"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 84df014fa4..430f1c60fd 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -698,10 +698,16 @@ <member name="editor/movie_writer/speaker_mode" type="int" setter="" getter="" default="0"> The speaker mode to use in the recorded audio when writing a movie. See [enum AudioServer.SpeakerMode] for possible values. </member> - <member name="editor/node_naming/name_casing" type="int" setter="" getter="" default="0"> + <member name="editor/naming/default_signal_callback_name" type="String" setter="" getter="" default=""_on_{node_name}_{signal_name}""> + The format of the default signal callback name (in the Signal Connection Dialog). The following substitutions are available: [code]{NodeName}[/code], [code]{nodeName}[/code], [code]{node_name}[/code], [code]{SignalName}[/code], [code]{signalName}[/code], and [code]{signal_name}[/code]. + </member> + <member name="editor/naming/default_signal_callback_to_self_name" type="String" setter="" getter="" default=""_on_{signal_name}""> + The format of the default signal callback name when a signal connects to the same node that emits it (in the Signal Connection Dialog). The following substitutions are available: [code]{NodeName}[/code], [code]{nodeName}[/code], [code]{node_name}[/code], [code]{SignalName}[/code], [code]{signalName}[/code], and [code]{signal_name}[/code]. + </member> + <member name="editor/naming/node_name_casing" type="int" setter="" getter="" default="0"> When creating node names automatically, set the type of casing in this project. This is mostly an editor setting. </member> - <member name="editor/node_naming/name_num_separator" type="int" setter="" getter="" default="0"> + <member name="editor/naming/node_name_num_separator" type="int" setter="" getter="" default="0"> What to use to separate node name from number. This is mostly an editor setting. </member> <member name="editor/run/main_run_args" type="String" setter="" getter="" default=""""> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index e533fc1e32..67f466ad4c 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -24,7 +24,8 @@ <param index="0" name="subresources" type="bool" default="false" /> <description> Duplicates this resource, returning a new resource with its [code]export[/code]ed or [constant PROPERTY_USAGE_STORAGE] properties copied from the original. - If [param subresources] is [code]false[/code], a shallow copy is returned. Nested resources within subresources are not duplicated and are shared from the original resource. This behavior can be overridden by the [constant PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE] flag. + If [param subresources] is [code]false[/code], a shallow copy is returned; nested resources within subresources are not duplicated and are shared from the original resource. If [param subresources] is [code]true[/code], a deep copy is returned; nested subresources will be duplicated and are not shared. + Subresource properties with the [constant PROPERTY_USAGE_ALWAYS_DUPLICATE] flag are always duplicated even with [param subresources] set to [code]false[/code], and properties with the [constant PROPERTY_USAGE_NEVER_DUPLICATE] flag are never duplicated even with [param subresources] set to [code]true[/code]. [b]Note:[/b] For custom resources, this method will fail if [method Object._init] has been defined with required parameters. </description> </method> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 5550bf0955..1ecc8a1d4e 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -8,7 +8,7 @@ [b]Note:[/b] Assignments to [member text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member text] will erase previous edits made from other manual sources such as [method append_text] and the [code]push_*[/code] / [method pop] methods. [b]Note:[/b] RichTextLabel doesn't support entangled BBCode tags. For example, instead of using [code][b]bold[i]bold italic[/b]italic[/i][/code], use [code][b]bold[i]bold italic[/i][/b][i]italic[/i][/code]. [b]Note:[/b] [code]push_*/pop[/code] functions won't affect BBCode. - [b]Note:[/b] Unlike [Label], RichTextLabel doesn't have a [i]property[/i] to horizontally align text to the center. Instead, enable [member bbcode_enabled] and surround the text in a [code][center][/code] tag as follows: [code][center]Example[/center][/code]. There is currently no built-in way to vertically align text either, but this can be emulated by relying on anchors/containers and the [member fit_content_height] property. + [b]Note:[/b] Unlike [Label], RichTextLabel doesn't have a [i]property[/i] to horizontally align text to the center. Instead, enable [member bbcode_enabled] and surround the text in a [code][center][/code] tag as follows: [code][center]Example[/center][/code]. There is currently no built-in way to vertically align text either, but this can be emulated by relying on anchors/containers and the [member fit_content] property. </description> <tutorials> <link title="BBCode in RichTextLabel">$DOCS_URL/tutorials/ui/bbcode_in_richtextlabel.html</link> @@ -251,6 +251,14 @@ Adds a [code][color][/code] tag to the tag stack. </description> </method> + <method name="push_customfx"> + <return type="void" /> + <param index="0" name="effect" type="RichTextEffect" /> + <param index="1" name="env" type="Dictionary" /> + <description> + Adds a custom effect tag to the tag stack. The effect does not need to be in [member custom_effects]. The environment is directly passed to the effect. + </description> + </method> <method name="push_dropcap"> <return type="void" /> <param index="0" name="string" type="String" /> @@ -474,9 +482,8 @@ <member name="deselect_on_focus_loss_enabled" type="bool" setter="set_deselect_on_focus_loss_enabled" getter="is_deselect_on_focus_loss_enabled" default="true"> If [code]true[/code], the selected text will be deselected when focus is lost. </member> - <member name="fit_content_height" type="bool" setter="set_fit_content_height" getter="is_fit_content_height_enabled" default="false"> - If [code]true[/code], the label's height will be automatically updated to fit its content. - [b]Note:[/b] This property is used as a workaround to fix issues with [RichTextLabel] in [Container]s, but it's unreliable in some cases and will be removed in future versions. + <member name="fit_content" type="bool" setter="set_fit_content" getter="is_fit_content_enabled" default="false"> + If [code]true[/code], the label's minimum size will be automatically updated to fit its content, matching the behavior of [Label]. </member> <member name="hint_underlined" type="bool" setter="set_hint_underline" getter="is_hint_underlined" default="true"> If [code]true[/code], the label underlines hint tags such as [code][hint=description]{text}[/hint][/code]. diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 9d73e9fb39..5b567dbc28 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -133,7 +133,7 @@ <description> Generates normals from vertices so you do not have to do it manually. If [param flip] is [code]true[/code], the resulting normals will be inverted. [method generate_normals] should be called [i]after[/i] generating geometry and [i]before[/i] committing the mesh using [method commit] or [method commit_to_arrays]. For correct display of normal-mapped surfaces, you will also have to generate tangents using [method generate_tangents]. [b]Note:[/b] [method generate_normals] only works if the primitive type to be set to [constant Mesh.PRIMITIVE_TRIANGLES]. - [b]Note:[/b] [method generate_normals] takes smooth groups into account. If you don't specify any smooth group for each vertex, [method generate_normals] will smooth normals for you. + [b]Note:[/b] [method generate_normals] takes smooth groups into account. To generate smooth normals, set the smooth group to a value greater than or equal to [code]0[/code] using [method set_smooth_group] or leave the smooth group at the default of [code]0[/code]. To generate flat normals, set the smooth group to [code]-1[/code] using [method set_smooth_group] prior to adding vertices. </description> </method> <method name="generate_tangents"> @@ -241,7 +241,7 @@ <return type="void" /> <param index="0" name="index" type="int" /> <description> - Specifies whether the current vertex (if using only vertex arrays) or current index (if also using index arrays) should use smooth normals for normal calculation. + Specifies the smooth group to use for the [i]next[/i] vertex. If this is never called, all vertices will have the default smooth group of [code]0[/code] and will be smoothed with adjacent vertices of the same group. To produce a mesh with flat normals, set the smooth group to [code]-1[/code]. </description> </method> <method name="set_tangent"> diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml index 20bfd0d8ae..5e7b79ae97 100644 --- a/doc/classes/SystemFont.xml +++ b/doc/classes/SystemFont.xml @@ -43,6 +43,12 @@ <member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="1"> Font hinting mode. </member> + <member name="msdf_pixel_range" type="int" setter="set_msdf_pixel_range" getter="get_msdf_pixel_range" default="16"> + The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]16[/code] allows outline sizes up to [code]8[/code] to look correct. + </member> + <member name="msdf_size" type="int" setter="set_msdf_size" getter="get_msdf_size" default="48"> + Source font size used to generate MSDF textures. Higher values allow for more precision, but are slower to render and require more memory. Only increase this value if you notice a visible lack of precision in glyph rendering. + </member> <member name="multichannel_signed_distance_field" type="bool" setter="set_multichannel_signed_distance_field" getter="is_multichannel_signed_distance_field" default="false"> If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. </member> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index ff5a665bfd..cf28dafcc9 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -401,7 +401,7 @@ </signal> <signal name="item_activated"> <description> - Emitted when an item's label is double-clicked. + Emitted when an item is double-clicked, or selected with a [code]ui_accept[/code] input event (e.g. using [kbd]Enter[/kbd] or [kbd]Space[/kbd] on the keyboard). </description> </signal> <signal name="item_collapsed"> @@ -415,14 +415,14 @@ Emitted when a custom button is pressed (i.e. in a [constant TreeItem.CELL_MODE_CUSTOM] mode cell). </description> </signal> - <signal name="item_double_clicked"> + <signal name="item_edited"> <description> - Emitted when an item's icon is double-clicked. + Emitted when an item is edited. </description> </signal> - <signal name="item_edited"> + <signal name="item_icon_double_clicked"> <description> - Emitted when an item is edited. + Emitted when an item's icon is double-clicked. For a signal that emits when any part of the item is double-clicked, see [signal item_activated]. </description> </signal> <signal name="item_mouse_selected"> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 236d34383f..ab2de14638 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -55,7 +55,7 @@ <method name="get_final_transform" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the total transform of the viewport. + Returns the transform from the viewport's coordinate system to the embedder's coordinate system. </description> </method> <method name="get_mouse_position" qualifiers="const"> @@ -210,6 +210,7 @@ <param index="0" name="position" type="Vector2" /> <description> Moves the mouse pointer to the specified position in this [Viewport] using the coordinate system of this [Viewport]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> diff --git a/doc/classes/VisualShaderNodeDerivativeFunc.xml b/doc/classes/VisualShaderNodeDerivativeFunc.xml index 9a1ad53394..4a31969171 100644 --- a/doc/classes/VisualShaderNodeDerivativeFunc.xml +++ b/doc/classes/VisualShaderNodeDerivativeFunc.xml @@ -15,6 +15,9 @@ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeDerivativeFunc.OpType" default="0"> A type of operands and returned value. See [enum OpType] for options. </member> + <member name="precision" type="int" setter="set_precision" getter="get_precision" enum="VisualShaderNodeDerivativeFunc.Precision" default="0"> + Sets the level of precision to use for the derivative function. See [enum Precision] for options. When using the GL_Compatibility renderer, this setting has no effect. + </member> </members> <constants> <constant name="OP_TYPE_SCALAR" value="0" enum="OpType"> @@ -44,5 +47,17 @@ <constant name="FUNC_MAX" value="3" enum="Function"> Represents the size of the [enum Function] enum. </constant> + <constant name="PRECISION_NONE" value="0" enum="Precision"> + No precision is specified, the GPU driver is allowed to use whatever level of precision it chooses. This is the default option and is equivalent to using [code]dFdx()[/code] or [code]dFdy()[/code] in text shaders. + </constant> + <constant name="PRECISION_COARSE" value="1" enum="Precision"> + The derivative will be calculated using the current fragment's neighbors (which may not include the current fragment). This tends to be faster than using [constant PRECISION_FINE], but may not be suitable when more precision is needed. This is equivalent to using [code]dFdxCoarse()[/code] or [code]dFdyCoarse()[/code] in text shaders. + </constant> + <constant name="PRECISION_FINE" value="2" enum="Precision"> + The derivative will be calculated using the current fragment and its immediate neighbors. This tends to be slower than using [constant PRECISION_COARSE], but may be necessary when more precision is needed. This is equivalent to using [code]dFdxFine()[/code] or [code]dFdyFine()[/code] in text shaders. + </constant> + <constant name="PRECISION_MAX" value="3" enum="Precision"> + Represents the size of the [enum Precision] enum. + </constant> </constants> </class> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 89b1c1889e..fbe00d22af 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -853,18 +853,19 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config - Color blend_color; + Color blend_color = base_color; + GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode; if (c->type == Item::Command::TYPE_RECT) { const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); if (rect->flags & CANVAS_RECT_LCD) { - p_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; + blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; blend_color = rect->modulate * base_color; } } - if (p_blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { + if (blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { _new_batch(r_batch_broken, r_index); - state.canvas_instance_batches[state.current_batch_index].blend_mode = p_blend_mode; + state.canvas_instance_batches[state.current_batch_index].blend_mode = blend_mode; state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color; } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 9547435607..949f2953d3 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -145,6 +145,7 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::Dep case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata); @@ -160,6 +161,7 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::Dep void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata)->data->dirty_dependencies = true; } void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) { diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 71caf3b8e3..10f42bf22b 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -192,7 +192,7 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("#define ViewIndex gl_ViewID_OVR\n"); builder.append("#define MAX_VIEWS 2\n"); builder.append("#else\n"); - builder.append("#define ViewIndex 0\n"); + builder.append("#define ViewIndex uint(0)\n"); builder.append("#define MAX_VIEWS 1\n"); builder.append("#endif\n"); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 52ff70f746..f977c8ceaf 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -565,9 +565,15 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #ifdef USE_MULTIVIEW uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray color_buffer; // texunit:-5 +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else uniform highp sampler2D depth_buffer; // texunit:-6 uniform highp sampler2D color_buffer; // texunit:-5 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif uniform highp mat4 world_transform; @@ -925,8 +931,12 @@ void main() { vec3 vertex = vertex_interp; #ifdef USE_MULTIVIEW vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz); + mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; #else vec3 view = -normalize(vertex_interp); + mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; #endif highp mat4 model_matrix = world_transform; vec3 albedo = vec3(1.0); diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 50e5c868d3..4fb780b665 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1546,6 +1546,8 @@ MaterialStorage::MaterialStorage() { actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_canvas.initialize(actions); } @@ -1635,8 +1637,8 @@ MaterialStorage::MaterialStorage() { actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz"; actions.renames["VIEW_INDEX"] = "ViewIndex"; - actions.renames["VIEW_MONO_LEFT"] = "0"; - actions.renames["VIEW_RIGHT"] = "1"; + actions.renames["VIEW_MONO_LEFT"] = "uint(0)"; + actions.renames["VIEW_RIGHT"] = "uint(1)"; //for light actions.renames["VIEW"] = "view"; @@ -1718,6 +1720,9 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.check_multiview_samplers = true; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_scene.initialize(actions); } @@ -1773,6 +1778,8 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_particles.initialize(actions); } @@ -1826,6 +1833,8 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_sky.initialize(actions); } } diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 639f5e6de5..8defa04ada 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1489,32 +1489,21 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } } -void AnimationBezierTrackEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { v_scroll += p_scroll_vec.y * v_zoom; v_scroll = CLAMP(v_scroll, -100000, 100000); timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); queue_redraw(); } -void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { +void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { const float v_zoom_orig = v_zoom; - if (p_alt) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { // Alternate zoom (doesn't affect timeline). - if (p_scroll_vec.y > 0) { - v_zoom = MIN(v_zoom * 1.2, 100000); - } else { - v_zoom = MAX(v_zoom / 1.2, 0.000001); - } + v_zoom = CLAMP(v_zoom * p_zoom_factor, 0.000001, 100000); } else { - if (p_scroll_vec.y > 0) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); - } else { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); - } + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / p_zoom_factor); } v_scroll = v_scroll + (p_origin.y - get_size().y / 2.0) * (v_zoom - v_zoom_orig); queue_redraw(); @@ -1681,7 +1670,7 @@ void AnimationBezierTrackEdit::_bind_methods() { AnimationBezierTrackEdit::AnimationBezierTrackEdit() { panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_scroll_callback), callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); play_position = memnew(Control); play_position->set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index e6d6424ef2..dbc231ccac 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -174,9 +174,8 @@ class AnimationBezierTrackEdit : public Control { SelectionSet selection; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right); void _draw_track(int p_track, const Color &p_color); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 857a9a664a..338a22c070 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1704,25 +1704,13 @@ Control::CursorShape AnimationTimelineEdit::get_cursor_shape(const Point2 &p_pos } } -void AnimationTimelineEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - // Timeline has no vertical scroll, so we change it to horizontal. - p_scroll_vec.x += p_scroll_vec.y; - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { set_value(get_value() - p_scroll_vec.x / get_zoom_scale()); } -void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTimelineEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - get_zoom()->set_value(new_zoom_value); + get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { @@ -1798,7 +1786,8 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_scroll_callback), callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_pan_axis(ViewPanner::PAN_AXIS_HORIZONTAL); set_layout_direction(Control::LAYOUT_DIRECTION_LTR); } @@ -5358,32 +5347,23 @@ void AnimationTrackEditor::_toggle_bezier_edit() { } } -void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_alt) { +void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) { goto_prev_step(true); } else { goto_next_step(true); } } else { - _pan_callback(-p_scroll_vec * 32); + timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); + scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); } } -void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) { - timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); - scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); -} - -void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTrackEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = timeline->get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - timeline->get_zoom()->set_value(new_zoom_value); + timeline->get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTrackEditor::_cancel_bezier_edit() { @@ -6398,7 +6378,7 @@ AnimationTrackEditor::AnimationTrackEditor() { timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_scroll_callback), callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); scroll = memnew(ScrollContainer); timeline_vbox->add_child(scroll); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index ef06445011..8506d9b80d 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -159,9 +159,8 @@ class AnimationTimelineEdit : public Range { bool use_fps = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool dragging_timeline = false; bool dragging_hsize = false; @@ -460,9 +459,8 @@ class AnimationTrackEditor : public VBoxContainer { PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr); Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _timeline_value_changed(double); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 9ff480f130..c1a5ae9ce1 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -30,6 +30,7 @@ #include "connections_dialog.h" +#include "core/config/project_settings.h" #include "editor/doc_tools.h" #include "editor/editor_help.h" #include "editor/editor_node.h" @@ -165,6 +166,7 @@ void ConnectDialog::_tree_node_selected() { if (!edit_mode) { set_dst_method(generate_method_callback_name(source, signal, current)); } + _update_method_tree(); _update_ok_enabled(); } @@ -182,6 +184,11 @@ void ConnectDialog::_unbind_count_changed(double p_count) { } } +void ConnectDialog::_method_selected() { + TreeItem *selected_item = method_tree->get_selected(); + dst_method->set_text(selected_item->get_text(0)); +} + /* * Adds a new parameter bind to connection. */ @@ -242,14 +249,153 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p String dst_method; if (p_source == p_target) { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_to_self_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_to_self_name")).format(subst); } else { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_name")).format(subst); } return dst_method; } +void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) { + for (const MethodInfo &mi : p_methods) { + TreeItem *method_item = method_tree->create_item(p_parent_item); + method_item->set_text(0, mi.name); + if (mi.return_val.type == Variant::NIL) { + method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons")); + } else { + method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons")); + } + } +} + +List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const { + bool check_signal = compatible_methods_only->is_pressed(); + List<MethodInfo> ret; + + for (const MethodInfo &mi : p_methods) { + if (!p_search_string.is_empty() && !mi.name.contains(p_search_string)) { + continue; + } + + if (check_signal) { + if (mi.arguments.size() != p_signal.arguments.size()) { + continue; + } + + bool type_mismatch = false; + const List<PropertyInfo>::Element *E = p_signal.arguments.front(); + for (const List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next(), E = E->next()) { + Variant::Type stype = E->get().type; + Variant::Type mtype = F->get().type; + + if (stype != Variant::NIL && mtype != Variant::NIL && stype != mtype) { + type_mismatch = true; + break; + } + } + + if (type_mismatch) { + continue; + } + } + ret.push_back(mi); + } + return ret; +} + +void ConnectDialog::_update_method_tree() { + method_tree->clear(); + + Color disabled_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")) * 0.7; + String search_string = method_search->get_text(); + Node *target = tree->get_selected(); + if (!target) { + return; + } + + MethodInfo signal_info; + if (compatible_methods_only->is_pressed()) { + List<MethodInfo> signals; + source->get_signal_list(&signals); + for (const MethodInfo &mi : signals) { + if (mi.name == signal) { + signal_info = mi; + break; + } + } + } + + TreeItem *root_item = method_tree->create_item(); + root_item->set_text(0, TTR("Methods")); + root_item->set_selectable(0, false); + + // If a script is attached, get methods from it. + ScriptInstance *si = target->get_script_instance(); + if (si) { + TreeItem *si_item = method_tree->create_item(root_item); + si_item->set_text(0, TTR("Attached Script")); + si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons"))); + si_item->set_selectable(0, false); + + List<MethodInfo> methods; + si->get_method_list(&methods); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + si_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, si_item); + } + } + + if (script_methods_only->is_pressed()) { + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); + return; + } + + // Get methods from each class in the heirarchy. + StringName current_class = target->get_class_name(); + do { + TreeItem *class_item = method_tree->create_item(root_item); + class_item->set_text(0, current_class); + Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons")); + if (has_theme_icon(current_class, SNAME("EditorIcons"))) { + icon = get_theme_icon(current_class, SNAME("EditorIcons")); + } + class_item->set_icon(0, icon); + class_item->set_selectable(0, false); + + List<MethodInfo> methods; + ClassDB::get_method_list(current_class, &methods, true); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + class_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, class_item); + } + current_class = ClassDB::get_parent_class_nocheck(current_class); + } while (current_class != StringName()); + + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); +} + +void ConnectDialog::_method_check_button_pressed(const CheckButton *p_button) { + if (p_button == script_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_script_methods_only", p_button->is_pressed()); + } else if (p_button == compatible_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_compatible_methods_only", p_button->is_pressed()); + } + _update_method_tree(); +} + +void ConnectDialog::_open_method_popup() { + method_popup->popup_centered(); + method_search->clear(); + method_search->grab_focus(); +} + /* * Enables or disables the connect button. The connect button is enabled if a * node is selected and valid in the selected mode. @@ -262,7 +408,7 @@ void ConnectDialog::_update_ok_enabled() { return; } - if (!advanced->is_pressed() && target->get_script().is_null()) { + if (dst_method->get_text().is_empty()) { get_ok_button()->set_disabled(true); return; } @@ -288,14 +434,13 @@ void ConnectDialog::_notification(int p_what) { style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0); from_signal->add_theme_style_override("normal", style); } + method_search->set_right_icon(get_theme_icon("Search", "EditorIcons")); + open_method_tree->set_icon(get_theme_icon("Edit", "EditorIcons")); } break; } } void ConnectDialog::_bind_methods() { - ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); - ClassDB::bind_method("_update_ok_enabled", &ConnectDialog::_update_ok_enabled); - ADD_SIGNAL(MethodInfo("connected")); } @@ -437,7 +582,6 @@ void ConnectDialog::_advanced_pressed() { error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())); } - _update_ok_enabled(); EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "use_advanced_connections", advanced->is_pressed()); popup_centered(); @@ -458,8 +602,8 @@ ConnectDialog::ConnectDialog() { vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); from_signal = memnew(LineEdit); - from_signal->set_editable(false); vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + from_signal->set_editable(false); tree = memnew(SceneTreeEditor(false)); tree->set_connecting_signal(true); @@ -476,6 +620,43 @@ ConnectDialog::ConnectDialog() { vbc_left->add_child(error_label); error_label->hide(); + method_popup = memnew(AcceptDialog); + method_popup->set_title(TTR("Select Method")); + method_popup->set_min_size(Vector2(400, 600) * EDSCALE); + add_child(method_popup); + + VBoxContainer *method_vbc = memnew(VBoxContainer); + method_popup->add_child(method_vbc); + + method_search = memnew(LineEdit); + method_vbc->add_child(method_search); + method_search->set_placeholder(TTR("Filter Methods")); + method_search->set_clear_button_enabled(true); + method_search->connect("text_changed", callable_mp(this, &ConnectDialog::_update_method_tree).unbind(1)); + + method_tree = memnew(Tree); + method_vbc->add_child(method_tree); + method_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + method_tree->set_hide_root(true); + method_tree->connect("item_selected", callable_mp(this, &ConnectDialog::_method_selected)); + method_tree->connect("item_activated", callable_mp((Window *)method_popup, &Window::hide)); + + empty_tree_label = memnew(Label(TTR("No method found matching given filters."))); + method_tree->add_child(empty_tree_label); + empty_tree_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER); + + script_methods_only = memnew(CheckButton(TTR("Script Methods Only"))); + method_vbc->add_child(script_methods_only); + script_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + script_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_script_methods_only", true)); + script_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(script_methods_only)); + + compatible_methods_only = memnew(CheckButton(TTR("Compatible Methods Only"))); + method_vbc->add_child(compatible_methods_only); + compatible_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + compatible_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_compatible_methods_only", true)); + compatible_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(compatible_methods_only)); + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -521,14 +702,22 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count); + HBoxContainer *hbc_method = memnew(HBoxContainer); + vbc_left->add_margin_child(TTR("Receiver Method:"), hbc_method); + dst_method = memnew(LineEdit); dst_method->set_h_size_flags(Control::SIZE_EXPAND_FILL); + dst_method->connect("text_changed", callable_mp(method_tree, &Tree::deselect_all).unbind(1)); dst_method->connect("text_submitted", callable_mp(this, &ConnectDialog::_text_submitted)); - vbc_left->add_margin_child(TTR("Receiver Method:"), dst_method); + hbc_method->add_child(dst_method); - advanced = memnew(CheckButton); + open_method_tree = memnew(Button); + hbc_method->add_child(open_method_tree); + open_method_tree->set_text("Pick"); + open_method_tree->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup)); + + advanced = memnew(CheckButton(TTR("Advanced"))); vbc_left->add_child(advanced); - advanced->set_text(TTR("Advanced")); advanced->set_h_size_flags(Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND); advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false)); advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); @@ -566,7 +755,7 @@ ConnectDialog::~ConnectDialog() { // Originally copied and adapted from EditorProperty, try to keep style in sync. Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); // p_text is expected to be something like this: // "gui_input::(event: InputEvent)::<Signal description>" @@ -1238,9 +1427,6 @@ ConnectionsDock::ConnectionsDock() { tree->connect("item_mouse_selected", callable_mp(this, &ConnectionsDock::_rmb_pressed)); add_theme_constant_override("separation", 3 * EDSCALE); - - EDITOR_DEF("interface/editors/default_signal_callback_name", "_on_{node_name}_{signal_name}"); - EDITOR_DEF("interface/editors/default_signal_callback_to_self_name", "_on_{signal_name}"); } ConnectionsDock::~ConnectionsDock() { diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 829a98caed..17a292434c 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -114,9 +114,17 @@ private: bool first_popup = true; NodePath dst_path; VBoxContainer *vbc_right = nullptr; - SceneTreeEditor *tree = nullptr; AcceptDialog *error = nullptr; + + Button *open_method_tree = nullptr; + AcceptDialog *method_popup = nullptr; + Tree *method_tree = nullptr; + Label *empty_tree_label = nullptr; + LineEdit *method_search = nullptr; + CheckButton *script_methods_only = nullptr; + CheckButton *compatible_methods_only = nullptr; + SpinBox *unbind_count = nullptr; EditorInspector *bind_editor = nullptr; OptionButton *type_list = nullptr; @@ -132,6 +140,14 @@ private: void _item_activated(); void _text_submitted(const String &p_text); void _tree_node_selected(); + + void _method_selected(); + void _create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item); + List<MethodInfo> _filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const; + void _update_method_tree(); + void _method_check_button_pressed(const CheckButton *p_button); + void _open_method_popup(); + void _unbind_count_changed(double p_count); void _add_bind(); void _remove_bind(); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index a925e2d1d3..c98ec7b2d5 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -536,12 +536,17 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector< } void DependencyRemoveDialog::ok_pressed() { - for (int i = 0; i < files_to_delete.size(); ++i) { - if (ResourceCache::has(files_to_delete[i])) { - Ref<Resource> res = ResourceCache::get_ref(files_to_delete[i]); + for (const KeyValue<String, String> &E : all_remove_files) { + String file = E.key; + + if (ResourceCache::has(file)) { + Ref<Resource> res = ResourceCache::get_ref(file); + emit_signal(SNAME("resource_removed"), res); res->set_path(""); } + } + for (int i = 0; i < files_to_delete.size(); ++i) { // If the file we are deleting for e.g. the main scene, default environment, // or audio bus layout, we must clear its definition in Project Settings. if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) { @@ -621,6 +626,7 @@ void DependencyRemoveDialog::ok_pressed() { } void DependencyRemoveDialog::_bind_methods() { + ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "obj"))); ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder"))); } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 4cf947b006..e11251596a 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -2370,7 +2370,7 @@ EditorHelpBit::EditorHelpBit() { rich_text = memnew(RichTextLabel); add_child(rich_text); rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked)); - rich_text->set_fit_content_height(true); + rich_text->set_fit_content(true); set_custom_minimum_size(Size2(0, 50 * EDSCALE)); } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 4753761f05..0166d4c719 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -886,7 +886,7 @@ void EditorProperty::_update_pin_flags() { static Control *make_help_bit(const String &p_text, bool p_property) { EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); PackedStringArray slices = p_text.split("::", false); if (slices.is_empty()) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index c7ea7dc7cf..0d5c820a3c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3089,7 +3089,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } String EditorNode::adjust_scene_name_casing(const String &root_name) { - switch (GLOBAL_GET("editor/scene/scene_naming").operator int()) { + switch (GLOBAL_GET("editor/naming/scene_name_casing").operator int()) { case SCENE_NAME_CASING_AUTO: // Use casing of the root node. break; @@ -3902,7 +3902,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b Ref<SceneState> state = sdata->get_state(); state->set_path(lpath); new_scene->set_scene_inherited_state(state); - new_scene->set_scene_file_path(lpath); + new_scene->set_scene_file_path(String()); } new_scene->set_scene_instance_state(Ref<SceneState>()); @@ -6113,6 +6113,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins Ref<SceneState> state = current_packed_scene->get_state(); state->set_path(current_packed_scene->get_path()); instantiated_node->set_scene_inherited_state(state); + instantiated_node->set_scene_file_path(String()); } editor_data.set_edited_scene_root(instantiated_node); current_edited_scene = instantiated_node; @@ -6429,7 +6430,7 @@ void EditorNode::_feature_profile_changed() { } void EditorNode::_bind_methods() { - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); ClassDB::bind_method("edit_current", &EditorNode::edit_current); ClassDB::bind_method("edit_node", &EditorNode::edit_node); @@ -7261,6 +7262,7 @@ EditorNode::EditorNode() { project_title->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); project_title->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); project_title->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_title->set_mouse_filter(Control::MOUSE_FILTER_PASS); left_spacer->add_child(project_title); } diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 4bcd91376a..d3cceee1a3 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -272,7 +272,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { OS::ProcessID pid = 0; Error err = OS::get_singleton()->create_instance(args, &pid); ERR_FAIL_COND_V(err, err); - pids.push_back(pid); + if (pid != 0) { + pids.push_back(pid); + } } status = STATUS_PLAY; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4879790b74..21e15bc996 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -474,10 +474,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Filesystem */ // External Programs - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/raster_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/vector_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/audio_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/3d_model_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/raster_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/vector_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/audio_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/3d_model_editor", "", "") // Directories EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/directories/autoscan_project_path", "", "") diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 745acca04b..5cae3ef3fd 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -198,7 +198,7 @@ static Ref<StyleBoxTexture> make_stylebox(Ref<Texture2D> p_texture, float p_left Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); style->set_texture(p_texture); style->set_texture_margin_individual(p_left * EDSCALE, p_top * EDSCALE, p_right * EDSCALE, p_bottom * EDSCALE); - style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); + style->set_content_margin_individual((p_left + p_margin_left) * EDSCALE, (p_top + p_margin_top) * EDSCALE, (p_right + p_margin_right) * EDSCALE, (p_bottom + p_margin_bottom) * EDSCALE); style->set_draw_center(p_draw_center); return style; } @@ -1492,11 +1492,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // HScrollBar Ref<Texture2D> empty_icon = memnew(ImageTexture); - theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "HScrollBar", empty_icon); theme->set_icon("increment_highlight", "HScrollBar", empty_icon); @@ -1506,11 +1506,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("decrement_pressed", "HScrollBar", empty_icon); // VScrollBar - theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "VScrollBar", empty_icon); theme->set_icon("increment_highlight", "VScrollBar", empty_icon); diff --git a/editor/editor_title_bar.cpp b/editor/editor_title_bar.cpp index 0271bbd64a..ae5cdfd72b 100644 --- a/editor/editor_title_bar.cpp +++ b/editor/editor_title_bar.cpp @@ -30,7 +30,7 @@ #include "editor/editor_title_bar.h" -void EditorTitleBar::input(const Ref<InputEvent> &p_event) { +void EditorTitleBar::gui_input(const Ref<InputEvent> &p_event) { if (!can_move) { return; } diff --git a/editor/editor_title_bar.h b/editor/editor_title_bar.h index 6cac163830..4055476b82 100644 --- a/editor/editor_title_bar.h +++ b/editor/editor_title_bar.h @@ -42,7 +42,7 @@ class EditorTitleBar : public HBoxContainer { bool can_move = false; protected: - virtual void input(const Ref<InputEvent> &p_event) override; + virtual void gui_input(const Ref<InputEvent> &p_event) override; static void _bind_methods(){}; public: diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index 5a334bdaa6..3998b33a53 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -41,12 +41,12 @@ void EditorZoomWidget::_update_zoom_label() { // lower the editor scale to increase the available real estate, // even if their display doesn't have a particularly low DPI. if (zoom >= 10) { - // Don't show a decimal when the zoom level is higher than 1000 %. - zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign(); + zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))); } else { - zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign(); + // 2 decimal places if the zoom is below 10%, 1 decimal place if it's below 1000%. + zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, (zoom >= 0.1) ? 0.1 : 0.01))); } - + zoom_text += " " + TS->percent_sign(); zoom_reset->set_text(zoom_text); } @@ -134,7 +134,7 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte float new_zoom_index = closest_zoom_index + p_increment_count; float new_zoom = Math::pow(2.f, new_zoom_index / 12.f); - // Restore Editor scale transformation + // Restore Editor scale transformation. new_zoom *= MAX(1, EDSCALE); set_zoom(new_zoom); @@ -179,8 +179,12 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset = memnew(Button); zoom_reset->set_flat(true); + zoom_reset->add_theme_style_override("normal", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("hover", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("focus", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); add_child(zoom_reset); - zoom_reset->add_theme_constant_override("outline_size", 1); + zoom_reset->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE)); zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0)); zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1)); zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset)); @@ -189,7 +193,7 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset->set_focus_mode(FOCUS_NONE); zoom_reset->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); // Prevent the button's size from changing when the text size changes - zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0)); + zoom_reset->set_custom_minimum_size(Size2(56 * EDSCALE, 0)); zoom_plus = memnew(Button); zoom_plus->set_flat(true); @@ -201,5 +205,5 @@ EditorZoomWidget::EditorZoomWidget() { _update_zoom_label(); - add_theme_constant_override("separation", Math::round(-8 * EDSCALE)); + add_theme_constant_override("separation", 0); } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index a078c58e72..378e06b4c9 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1531,6 +1531,10 @@ void FileSystemDock::_make_scene_confirm() { EditorNode::get_singleton()->save_scene_list({ scene_path }); } +void FileSystemDock::_resource_removed(const Ref<Resource> &p_resource) { + emit_signal(SNAME("resource_removed"), p_resource); +} + void FileSystemDock::_file_removed(String p_file) { emit_signal(SNAME("file_removed"), p_file); @@ -3095,6 +3099,7 @@ void FileSystemDock::_bind_methods() { ADD_SIGNAL(MethodInfo("inherit", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("instantiate", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"))); + ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder"))); ADD_SIGNAL(MethodInfo("files_moved", PropertyInfo(Variant::STRING, "old_file"), PropertyInfo(Variant::STRING, "new_file"))); @@ -3254,6 +3259,7 @@ FileSystemDock::FileSystemDock() { add_child(owners_editor); remove_dialog = memnew(DependencyRemoveDialog); + remove_dialog->connect("resource_removed", callable_mp(this, &FileSystemDock::_resource_removed)); remove_dialog->connect("file_removed", callable_mp(this, &FileSystemDock::_file_removed)); remove_dialog->connect("folder_removed", callable_mp(this, &FileSystemDock::_folder_removed)); add_child(remove_dialog); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 42a72b7ee8..ede6869eea 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -227,6 +227,7 @@ private: void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; void _update_project_settings_after_move(const HashMap<String, String> &p_renames) const; + void _resource_removed(const Ref<Resource> &p_resource); void _file_removed(String p_file); void _folder_removed(String p_folder); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index c0972d201e..9632670658 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1127,7 +1127,7 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action connecting = false; } -void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions) { +void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions) { Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label")); Color icon_color(1, 1, 1); Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); @@ -1138,7 +1138,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co accent.a *= 0.6; } - const Ref<Texture2D> icons[6] = { + const Ref<Texture2D> icons[] = { get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionSyncBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndBig"), SNAME("EditorIcons")), @@ -1146,6 +1146,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co get_theme_icon(SNAME("TransitionSyncAutoBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndAutoBig"), SNAME("EditorIcons")) }; + const int ICON_COUNT = sizeof(icons) / sizeof(*icons); if (p_selected) { state_machine_draw->draw_line(p_from, p_to, accent, 6); @@ -1153,12 +1154,18 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co if (p_travel) { linecolor = accent; - linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v()); } state_machine_draw->draw_line(p_from, p_to, linecolor, 2); - Ref<Texture2D> icon = icons[p_mode + (p_auto_advance ? 3 : 0)]; + if (p_fade_ratio > 0.0) { + Color fade_linecolor = accent; + fade_linecolor.set_hsv(1.0, fade_linecolor.get_s(), fade_linecolor.get_v()); + state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_linecolor, 2); + } + int icon_index = p_mode + (p_auto_advance ? ICON_COUNT / 2 : 0); + ERR_FAIL_COND(icon_index >= ICON_COUNT); + Ref<Texture2D> icon = icons[icon_index]; Transform2D xf; xf.columns[0] = (p_to - p_from).normalized(); @@ -1327,7 +1334,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, 0.0, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -1357,6 +1364,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; tl.travel = false; + tl.fade_ratio = 0.0; tl.hidden = false; if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists @@ -1378,6 +1386,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { if (blend_from == local_from && current == local_to) { tl.travel = true; + tl.fade_ratio = MIN(1.0, fading_pos / fading_time); } if (travel_path.size()) { @@ -1418,7 +1427,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { for (int i = 0; i < transition_lines.size(); i++) { TransitionLine tl = transition_lines[i]; if (!tl.hidden) { - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.auto_advance, !tl.multi_transitions.is_empty()); + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, !tl.multi_transitions.is_empty()); } } @@ -1508,25 +1517,24 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { state_machine_play_pos->queue_redraw(); } -void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String p_name, float p_ratio) { AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); if (!tree) { return; } Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); - if (!playback.is_valid() || !playback->is_playing()) { return; } - if (playback->get_current_node() == state_machine->start_node || playback->get_current_node() == state_machine->end_node) { + if (p_name == state_machine->start_node || p_name == state_machine->end_node || p_name.is_empty()) { return; } int idx = -1; for (int i = 0; i < node_rects.size(); i++) { - if (node_rects[i].node_name == playback->get_current_node()) { + if (node_rects[i].node_name == p_name) { idx = i; break; } @@ -1550,10 +1558,7 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, current_length); - - float pos = CLAMP(play_pos, 0, len); - float c = pos / len; + float c = p_ratio; Color fg = get_theme_color(SNAME("font_color"), SNAME("Label")); Color bg = fg; bg.a *= 0.3; @@ -1565,6 +1570,32 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { state_machine_play_pos->draw_line(from, to, fg, 2); } +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() { + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); + if (!tree) { + return; + } + + Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (!playback.is_valid() || !playback->is_playing()) { + return; + } + + { + float len = MAX(0.0001, current_length); + float pos = CLAMP(current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_current_node(), c); + } + + { + float len = MAX(0.0001, fade_from_length); + float pos = CLAMP(fade_from_current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_fading_from_node(), c); + } +} + void AnimationNodeStateMachineEditor::_update_graph() { if (updating) { return; @@ -1687,17 +1718,28 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { Vector<StringName> tp; bool is_playing = false; StringName current_node; - StringName blend_from_node; - play_pos = 0; + StringName fading_from_node; + + current_play_pos = 0; current_length = 0; + fade_from_current_play_pos = 0; + fade_from_length = 0; + + fading_time = 0; + fading_pos = 0; + if (playback.is_valid()) { tp = playback->get_travel_path(); is_playing = playback->is_playing(); current_node = playback->get_current_node(); - blend_from_node = playback->get_fading_from_node(); - play_pos = playback->get_current_play_pos(); + fading_from_node = playback->get_fading_from_node(); + current_play_pos = playback->get_current_play_pos(); current_length = playback->get_current_length(); + fade_from_current_play_pos = playback->get_fade_from_play_pos(); + fade_from_length = playback->get_fade_from_length(); + fading_time = playback->get_fading_time(); + fading_pos = playback->get_fading_pos(); } { @@ -1714,12 +1756,19 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //redraw if travel state changed - if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { + if (!same_travel_path || + last_active != is_playing || + last_current_node != current_node || + last_fading_from_node != fading_from_node || + last_fading_time != fading_time || + last_fading_pos != fading_pos) { state_machine_draw->queue_redraw(); last_travel_path = tp; last_current_node = current_node; last_active = is_playing; - last_blend_from_node = blend_from_node; + last_fading_from_node = fading_from_node; + last_fading_time = fading_time; + last_fading_pos = fading_pos; state_machine_play_pos->queue_redraw(); } @@ -1737,14 +1786,15 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { // when current_node is a state machine, use playback of current_node to set play_pos if (current_node_playback.is_valid()) { - play_pos = current_node_playback->get_current_play_pos(); + current_play_pos = current_node_playback->get_current_play_pos(); current_length = current_node_playback->get_current_length(); } } } - if (last_play_pos != play_pos) { - last_play_pos = play_pos; + if (last_play_pos != current_play_pos || fade_from_last_play_pos != fade_from_current_play_pos) { + last_play_pos = current_play_pos; + fade_from_last_play_pos = fade_from_current_play_pos; state_machine_play_pos->queue_redraw(); } } break; @@ -2048,7 +2098,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { state_machine_draw->add_child(state_machine_play_pos); state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent state_machine_play_pos->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw)); + state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw_all)); v_scroll = memnew(VScrollBar); state_machine_draw->add_child(v_scroll); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 46fe13ccc1..66338c820e 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -85,9 +85,12 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { static AnimationNodeStateMachineEditor *singleton; void _state_machine_gui_input(const Ref<InputEvent> &p_event); - void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions); + void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions); + void _state_machine_draw(); - void _state_machine_pos_draw(); + + void _state_machine_pos_draw_individual(String p_name, float p_ratio); + void _state_machine_pos_draw_all(); void _update_graph(); @@ -150,6 +153,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { float width = 0; bool selected; bool travel; + float fade_ratio; bool hidden; int transition_index; Vector<TransitionLine> multi_transitions; @@ -204,13 +208,23 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { void _delete_tree_draw(); bool last_active = false; - StringName last_blend_from_node; + StringName last_fading_from_node; StringName last_current_node; Vector<StringName> last_travel_path; + + float fade_from_last_play_pos = 0.0f; + float fade_from_current_play_pos = 0.0f; + float fade_from_length = 0.0f; + float last_play_pos = 0.0f; - float play_pos = 0.0f; + float current_play_pos = 0.0f; float current_length = 0.0f; + float last_fading_time = 0.0f; + float last_fading_pos = 0.0f; + float fading_time = 0.0f; + float fading_pos = 0.0f; + float error_time = 0.0f; String error_text; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 720deb0b92..ab46e8f04a 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -65,13 +65,14 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) { tree = p_tree; Vector<String> path; - if (tree && tree->has_meta("_tree_edit_path")) { - path = tree->get_meta("_tree_edit_path"); - } else { - current_root = ObjectID(); + if (tree) { + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + } else { + current_root = ObjectID(); + } + edit_path(path); } - - edit_path(path); } void AnimationTreeEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 5d0555a10e..e09636d297 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1259,57 +1259,25 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo } } - Ref<InputEventMagnifyGesture> magnify_gesture = p_event; - if (magnify_gesture.is_valid() && !p_already_accepted) { - // Zoom gesture - _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - return true; - } - - Ref<InputEventPanGesture> pan_gesture = p_event; - if (pan_gesture.is_valid() && !p_already_accepted) { - // If ctrl key pressed, then zoom instead of pan. - if (pan_gesture->is_ctrl_pressed()) { - const real_t factor = pan_gesture->get_delta().y; - - zoom_widget->set_zoom_by_increments(1); - if (factor != 1.f) { - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * factor + 1.f)); - } - _zoom_on_position(zoom_widget->get_zoom(), pan_gesture->get_position()); - - return true; - } - - // Pan gesture - const Vector2 delta = (pan_speed / zoom) * pan_gesture->get_delta(); - view_offset.x += delta.x; - view_offset.y += delta.y; - update_viewport(); - return true; - } - return false; } -void CanvasItemEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * pan_speed); -} - -void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) { +void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { view_offset.x -= p_scroll_vec.x / zoom; view_offset.y -= p_scroll_vec.y / zoom; update_viewport(); } -void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - int scroll_sign = (int)SIGN(p_scroll_vec.y); - // Inverted so that scrolling up (-1) zooms in, scrolling down (+1) zooms out. - zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt); - if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) { - // Handle high-precision (analog) scrolling. - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f)); +void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + // Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two. + int increment = p_zoom_factor > 1.0 ? 1 : -1; + zoom_widget->set_zoom_by_increments(increment, mb->is_alt_pressed()); + } else { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); } + _zoom_on_position(zoom_widget->get_zoom(), p_origin); } @@ -3868,7 +3836,7 @@ void CanvasItemEditor::_update_editor_settings() { context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); - pan_speed = int(EDITOR_GET("editors/panning/2d_editor_pan_speed")); + panner->set_scroll_speed(EDITOR_GET("editors/panning/2d_editor_pan_speed")); warped_panning = bool(EDITOR_GET("editors/panning/warped_mouse_panning")); } @@ -5059,7 +5027,7 @@ CanvasItemEditor::CanvasItemEditor() { zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_scroll_callback), callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); viewport = memnew(CanvasItemEditorViewport(this)); viewport_scrollable->add_child(viewport); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1e01eac82d..ebe87a56f7 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -363,10 +363,8 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - int pan_speed = 20; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool _is_node_locked(const Node *p_node) const; bool _is_node_movable(const Node *p_node, bool p_popup_warning = false); diff --git a/editor/plugins/navigation_link_2d_editor_plugin.cpp b/editor/plugins/navigation_link_2d_editor_plugin.cpp index 21a1d839f0..dff92ced27 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.cpp +++ b/editor/plugins/navigation_link_2d_editor_plugin.cpp @@ -65,20 +65,20 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - // Start location - if (xform.xform(node->get_start_location()).distance_to(mb->get_position()) < grab_threshold) { + // Start position + if (xform.xform(node->get_start_position()).distance_to(mb->get_position()) < grab_threshold) { start_grabbed = true; - original_start_location = node->get_start_location(); + original_start_position = node->get_start_position(); return true; } else { start_grabbed = false; } - // End location - if (xform.xform(node->get_end_location()).distance_to(mb->get_position()) < grab_threshold) { + // End position + if (xform.xform(node->get_end_position()).distance_to(mb->get_position()) < grab_threshold) { end_grabbed = true; - original_end_location = node->get_end_location(); + original_end_position = node->get_end_position(); return true; } else { @@ -87,10 +87,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } else { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (start_grabbed) { - undo_redo->create_action(TTR("Set start_location")); - undo_redo->add_do_method(node, "set_start_location", node->get_start_location()); + undo_redo->create_action(TTR("Set start_position")); + undo_redo->add_do_method(node, "set_start_position", node->get_start_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_start_location", original_start_location); + undo_redo->add_undo_method(node, "set_start_position", original_start_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -100,10 +100,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } if (end_grabbed) { - undo_redo->create_action(TTR("Set end_location")); - undo_redo->add_do_method(node, "set_end_location", node->get_end_location()); + undo_redo->create_action(TTR("Set end_position")); + undo_redo->add_do_method(node, "set_end_position", node->get_end_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_end_location", original_end_location); + undo_redo->add_undo_method(node, "set_end_position", original_end_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -120,14 +120,14 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e point = node->get_global_transform().affine_inverse().xform(point); if (start_grabbed) { - node->set_start_location(point); + node->set_start_position(point); canvas_item_editor->update_viewport(); return true; } if (end_grabbed) { - node->set_end_location(point); + node->set_end_position(point); canvas_item_editor->update_viewport(); return true; @@ -143,13 +143,13 @@ void NavigationLink2DEditor::forward_canvas_draw_over_viewport(Control *p_overla } Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - Vector2 global_start_location = gt.xform(node->get_start_location()); - Vector2 global_end_location = gt.xform(node->get_end_location()); + Vector2 global_start_position = gt.xform(node->get_start_position()); + Vector2 global_end_position = gt.xform(node->get_end_position()); // Only drawing the handles here, since the debug rendering will fill in the rest. const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); - p_overlay->draw_texture(handle, global_start_location - handle->get_size() / 2); - p_overlay->draw_texture(handle, global_end_location - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_start_position - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_end_position - handle->get_size() / 2); } void NavigationLink2DEditor::edit(NavigationLink2D *p_node) { diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h index 76444403d0..ea731ca2cd 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.h +++ b/editor/plugins/navigation_link_2d_editor_plugin.h @@ -43,10 +43,10 @@ class NavigationLink2DEditor : public Control { NavigationLink2D *node = nullptr; bool start_grabbed = false; - Vector2 original_start_location; + Vector2 original_start_position; bool end_grabbed = false; - Vector2 original_end_location; + Vector2 original_end_position; protected: void _notification(int p_what); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index bb71c27bff..e11dc1c81d 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -5057,8 +5057,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); Vector3::Axis up_axis = up_vector.max_axis_index(); - Vector3 start_location = link->get_start_location(); - Vector3 end_location = link->get_end_location(); + Vector3 start_position = link->get_start_position(); + Vector3 end_position = link->get_end_position(); Ref<Material> link_material = get_material("navigation_link_material", p_gizmo); Ref<Material> link_material_disabled = get_material("navigation_link_material_disabled", p_gizmo); @@ -5068,10 +5068,10 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw line between the points. Vector<Vector3> lines; - lines.append(start_location); - lines.append(end_location); + lines.append(start_position); + lines.append(end_position); - // Draw start location search radius + // Draw start position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5082,21 +5082,21 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(start_location + Vector3(0, a.x, a.y)); - lines.append(start_location + Vector3(0, b.x, b.y)); + lines.append(start_position + Vector3(0, a.x, a.y)); + lines.append(start_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(start_location + Vector3(a.x, 0, a.y)); - lines.append(start_location + Vector3(b.x, 0, b.y)); + lines.append(start_position + Vector3(a.x, 0, a.y)); + lines.append(start_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(start_location + Vector3(a.x, a.y, 0)); - lines.append(start_location + Vector3(b.x, b.y, 0)); + lines.append(start_position + Vector3(a.x, a.y, 0)); + lines.append(start_position + Vector3(b.x, b.y, 0)); break; } } - // Draw end location search radius + // Draw end position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5107,16 +5107,16 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(end_location + Vector3(0, a.x, a.y)); - lines.append(end_location + Vector3(0, b.x, b.y)); + lines.append(end_position + Vector3(0, a.x, a.y)); + lines.append(end_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(end_location + Vector3(a.x, 0, a.y)); - lines.append(end_location + Vector3(b.x, 0, b.y)); + lines.append(end_position + Vector3(a.x, 0, a.y)); + lines.append(end_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(end_location + Vector3(a.x, a.y, 0)); - lines.append(end_location + Vector3(b.x, b.y, 0)); + lines.append(end_position + Vector3(a.x, a.y, 0)); + lines.append(end_position + Vector3(b.x, b.y, 0)); break; } } @@ -5125,8 +5125,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); Vector<Vector3> handles; - handles.append(start_location); - handles.append(end_location); + handles.append(start_position); + handles.append(end_position); p_gizmo->add_handles(handles, handles_material); } @@ -5136,7 +5136,7 @@ String NavigationLink3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g Variant NavigationLink3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_node_3d()); - return p_id == 0 ? link->get_start_location() : link->get_end_location(); + return p_id == 0 ? link->get_start_position() : link->get_end_position(); } void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { @@ -5151,8 +5151,8 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i Vector3 ray_from = p_camera->project_ray_origin(p_point); Vector3 ray_dir = p_camera->project_ray_normal(p_point); - Vector3 location = p_id == 0 ? link->get_start_location() : link->get_end_location(); - Plane move_plane = Plane(cam_dir, gt.xform(location)); + Vector3 position = p_id == 0 ? link->get_start_position() : link->get_end_position(); + Plane move_plane = Plane(cam_dir, gt.xform(position)); Vector3 intersection; if (!move_plane.intersects_ray(ray_from, ray_dir, &intersection)) { @@ -5164,11 +5164,11 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i intersection.snap(Vector3(snap, snap, snap)); } - location = gi.xform(intersection); + position = gi.xform(intersection); if (p_id == 0) { - link->set_start_location(location); + link->set_start_position(position); } else if (p_id == 1) { - link->set_end_location(location); + link->set_end_position(position); } } @@ -5177,22 +5177,22 @@ void NavigationLink3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (p_cancel) { if (p_id == 0) { - link->set_start_location(p_restore); + link->set_start_position(p_restore); } else { - link->set_end_location(p_restore); + link->set_end_position(p_restore); } return; } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); if (p_id == 0) { - ur->create_action(TTR("Change Start Location")); - ur->add_do_method(link, "set_start_location", link->get_start_location()); - ur->add_undo_method(link, "set_start_location", p_restore); + ur->create_action(TTR("Change Start Position")); + ur->add_do_method(link, "set_start_position", link->get_start_position()); + ur->add_undo_method(link, "set_start_position", p_restore); } else { - ur->create_action(TTR("Change End Location")); - ur->add_do_method(link, "set_end_location", link->get_end_location()); - ur->add_undo_method(link, "set_end_location", p_restore); + ur->create_action(TTR("Change End Position")); + ur->add_do_method(link, "set_end_position", link->get_end_position()); + ur->add_undo_method(link, "set_end_position", p_restore); } ur->commit_action(); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index c30f0ec62d..fb35668310 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -939,21 +939,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } -void Polygon2DEditor::_uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _uv_pan_callback(-p_scroll_vec * 32); -} - -void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec) { +void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { uv_hscroll->set_value(uv_hscroll->get_value() - p_scroll_vec.x); uv_vscroll->set_value(uv_vscroll->get_value() - p_scroll_vec.y); } -void Polygon2DEditor::_uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } else { - uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } +void Polygon2DEditor::_uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + uv_zoom->set_value(uv_zoom->get_value() * p_zoom_factor); } void Polygon2DEditor::_uv_scroll_changed(real_t) { @@ -1478,7 +1470,7 @@ Polygon2DEditor::Polygon2DEditor() { bone_scroll->add_child(bone_scroll_vb); uv_panner.instantiate(); - uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_scroll_callback), callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); + uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); uv_edit_draw->connect("draw", callable_mp(this, &Polygon2DEditor::_uv_draw)); uv_edit_draw->connect("gui_input", callable_mp(this, &Polygon2DEditor::_uv_input)); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 7246c08bea..2c55a5f631 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -90,9 +90,8 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { TextureRect *uv_icon_zoom = nullptr; Ref<ViewPanner> uv_panner; - void _uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _uv_pan_callback(Vector2 p_scroll_vec); - void _uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); VBoxContainer *bone_scroll_main_vb = nullptr; ScrollContainer *bone_scroll = nullptr; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index b719a2ce30..6b4e7184d9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -252,12 +252,14 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); const int line = meta["line"].operator int64_t() - 1; + const String code = meta["code"].operator String(); + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; CodeEdit *text_editor = code_editor->get_text_editor(); String prev_line = line > 0 ? text_editor->get_line(line - 1) : ""; if (prev_line.contains("@warning_ignore")) { const int closing_bracket_idx = prev_line.find(")"); - const String text_to_insert = ", " + meta["code"].operator String(); + const String text_to_insert = ", " + code.quote(quote_style); prev_line = prev_line.insert(closing_bracket_idx, text_to_insert); text_editor->set_line(line - 1, prev_line); } else { @@ -268,7 +270,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else { annotation_indent = String(" ").repeat(text_editor->get_indent_size() * indent); } - text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + meta["code"].operator String() + ")"); + text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + code.quote(quote_style) + ")"); } _validate_script(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index d7559bc18e..76f2bb7bb5 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -620,22 +620,14 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } -void TextureRegionEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec) { +void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { p_scroll_vec /= draw_zoom; hscroll->set_value(hscroll->get_value() - p_scroll_vec.x); vscroll->set_value(vscroll->get_value() - p_scroll_vec.y); } -void TextureRegionEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - _zoom_on_position(draw_zoom * ((0.95 + (0.05 * Math::abs(p_scroll_vec.y))) / 0.95), p_origin); - } else { - _zoom_on_position(draw_zoom * (1 - (0.05 * Math::abs(p_scroll_vec.y))), p_origin); - } +void TextureRegionEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + _zoom_on_position(draw_zoom * p_zoom_factor, p_origin); } void TextureRegionEditor::_scroll_changed(float) { @@ -1169,7 +1161,7 @@ TextureRegionEditor::TextureRegionEditor() { hb_grid->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); edit_draw = memnew(Panel); vb->add_child(edit_draw); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 0325700d25..ba64a04084 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -103,9 +103,8 @@ class TextureRegionEditor : public AcceptDialog { bool request_center = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _set_snap_mode(int p_mode); void _set_snap_off_x(float p_val); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 0ac375407c..e430848875 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -47,18 +47,14 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) { } } -void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TileAtlasView::_pan_callback(Vector2 p_scroll_vec) { +void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { panning += p_scroll_vec; _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } -void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - zoom_widget->set_zoom_by_increments(-p_scroll_vec.y * 2); +void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } @@ -583,7 +579,7 @@ TileAtlasView::TileAtlasView() { add_child(button_center_view); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); panner->set_enable_rmb(true); center_container = memnew(CenterContainer); diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index f719bee704..4a7547f34b 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -65,9 +65,8 @@ private: virtual void gui_input(const Ref<InputEvent> &p_event) override; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 2394130ad6..d79bd8ced3 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -266,7 +266,7 @@ void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEven } Ref<TileSet> tile_set = tile_map->get_tileset(); - if (!tile_set.is_valid()) { + if (!tile_set.is_valid() || EditorNode::get_singleton()->is_resource_read_only(tile_set)) { return; } @@ -1277,13 +1277,15 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile); } - // Creating a pattern in the pattern list. - select_last_pattern = true; - int new_pattern_index = tile_set->get_patterns_count(); - undo_redo->create_action(TTR("Add TileSet pattern")); - undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); - undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); - undo_redo->commit_action(); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + // Creating a pattern in the pattern list. + select_last_pattern = true; + int new_pattern_index = tile_set->get_patterns_count(); + undo_redo->create_action(TTR("Add TileSet pattern")); + undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); + undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); + undo_redo->commit_action(); + } } else { // Get the top-left cell. Vector2i top_left; @@ -1989,6 +1991,15 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { _stop_dragging(); // Avoids staying in a wrong drag state. + // Disable sort button if the tileset is read-only + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (tile_set.is_valid()) { + source_sort_button->set_disabled(EditorNode::get_singleton()->is_resource_read_only(tile_set)); + } + } + if (tile_map_id != p_tile_map_id) { tile_map_id = p_tile_map_id; @@ -3986,7 +3997,7 @@ TileMapEditor::TileMapEditor() { tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed)); // --- TileMap toolbar --- - tile_map_toolbar = memnew(HBoxContainer); + tile_map_toolbar = memnew(HFlowContainer); tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); add_child(tile_map_toolbar); @@ -4001,8 +4012,11 @@ TileMapEditor::TileMapEditor() { } } - // Wide empty separation control. - tile_map_toolbar->add_spacer(); + // Wide empty separation control. (like BoxContainer::add_spacer()) + Control *c = memnew(Control); + c->set_mouse_filter(MOUSE_FILTER_PASS); + c->set_h_size_flags(SIZE_EXPAND_FILL); + tile_map_toolbar->add_child(c); // Layer selector. layers_selection_button = memnew(OptionButton); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index fb9c2f3689..1cab1d1500 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -38,6 +38,7 @@ #include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/flow_container.h" #include "scene/gui/item_list.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -323,7 +324,7 @@ private: Vector<TileMapEditorPlugin *> tile_map_editor_plugins; // Toolbar. - HBoxContainer *tile_map_toolbar = nullptr; + HFlowContainer *tile_map_toolbar = nullptr; OptionButton *layers_selection_button = nullptr; Button *toggle_highlight_selected_layer_button = nullptr; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 32421daa92..a12a647e99 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -585,6 +585,7 @@ void TileSetAtlasSourceEditor::_update_atlas_source_inspector() { // Update visibility. bool inspector_visible = tools_button_group->get_pressed_button() == tool_setup_atlas_source_button; atlas_source_inspector->set_visible(inspector_visible); + atlas_source_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_inspector() { @@ -599,6 +600,7 @@ void TileSetAtlasSourceEditor::_update_tile_inspector() { tile_inspector->hide(); tile_inspector_no_tile_selected_label->hide(); } + tile_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_data_editors() { @@ -970,19 +972,19 @@ void TileSetAtlasSourceEditor::_update_toolbar() { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->show(); - tool_advanced_menu_buttom->show(); + tool_advanced_menu_button->show(); } else if (tools_button_group->get_pressed_button() == tool_select_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } else if (tools_button_group->get_pressed_button() == tool_paint_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->show(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } } @@ -2188,7 +2190,12 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source); - if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id && new_read_only_state == read_only) { return; } @@ -2205,11 +2212,23 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource tile_set_atlas_source = p_tile_set_atlas_source; tile_set_atlas_source_id = p_source_id; - // Add the listener again. + // Read-only is off by default. + read_only = new_read_only_state; + if (tile_set.is_valid()) { tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } + if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) { + tool_paint_button->set_pressed(false); + tool_setup_atlas_source_button->set_pressed(true); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2344,7 +2363,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { tools_settings_erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); - tool_advanced_menu_buttom->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + tool_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); resize_handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons")); @@ -2352,6 +2371,18 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_changed_needs_update) { + // Read-only is off by default + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2516,12 +2547,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); - tool_advanced_menu_buttom = memnew(MenuButton); - tool_advanced_menu_buttom->set_flat(true); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); - tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); - tool_settings->add_child(tool_advanced_menu_buttom); + tool_advanced_menu_button = memnew(MenuButton); + tool_advanced_menu_button->set_flat(true); + tool_advanced_menu_button->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); + tool_advanced_menu_button->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); + tool_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); + tool_settings->add_child(tool_advanced_menu_button); _update_toolbar(); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index bcab1296ad..a4826bc56f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -113,6 +113,8 @@ public: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; @@ -209,7 +211,7 @@ private: HBoxContainer *tool_settings = nullptr; HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr; Button *tools_settings_erase_button = nullptr; - MenuButton *tool_advanced_menu_buttom = nullptr; + MenuButton *tool_advanced_menu_button = nullptr; // Selection. RBSet<TileSelection> selection; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 53c2d4de51..39d17c718e 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -89,6 +89,10 @@ void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { ERR_FAIL_COND_V(!tile_set.is_valid(), false); + if (read_only) { + return false; + } + if (p_from == sources_list) { Dictionary d = p_data; @@ -223,7 +227,7 @@ void TileSetEditor::_source_selected(int p_source_index) { ERR_FAIL_COND(!tile_set.is_valid()); // Update the selected source. - sources_delete_button->set_disabled(p_source_index < 0); + sources_delete_button->set_disabled(p_source_index < 0 || read_only); if (p_source_index >= 0) { int source_id = sources_list->get_item_metadata(p_source_index); @@ -356,8 +360,19 @@ void TileSetEditor::_notification(int p_what) { if (tile_set.is_valid()) { tile_set->set_edited(true); } + + read_only = false; + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + _update_sources_list(); _update_patterns_list(); + + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set_changed_needs_update = false; } } break; @@ -367,6 +382,10 @@ void TileSetEditor::_notification(int p_what) { void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(!tile_set.is_valid()); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + return; + } + if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) { Vector<int> selected = patterns_item_list->get_selected_items(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); @@ -667,7 +686,12 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p } void TileSetEditor::edit(Ref<TileSet> p_tile_set) { - if (p_tile_set == tile_set) { + bool new_read_only_state = false; + if (tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && new_read_only_state == read_only) { return; } @@ -679,8 +703,15 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { // Change the edited object. tile_set = p_tile_set; - // Add the listener again. + // Read-only status is false by default + read_only = new_read_only_state; + + // Add the listener again and check for read-only status. if (tile_set.is_valid()) { + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); if (first_edit) { first_edit = false; @@ -690,10 +721,6 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { } _update_patterns_list(); } - - tile_set_atlas_source_editor->hide(); - tile_set_scenes_collection_source_editor->hide(); - no_source_selected_label->show(); } TileSetEditor::TileSetEditor() { diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index e3dff11277..d36d3bde41 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -45,6 +45,8 @@ class TileSetEditor : public VBoxContainer { static TileSetEditor *singleton; private: + bool read_only = false; + Ref<TileSet> tile_set; bool tile_set_changed_needs_update = false; HSplitContainer *split_container = nullptr; diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 6251cd18f7..cc276597fa 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -284,7 +284,7 @@ void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { void TileSetScenesCollectionSourceEditor::_update_action_buttons() { Vector<int> selected_indices = scene_tiles_list->get_selected_items(); - scene_tile_delete_button->set_disabled(selected_indices.size() <= 0); + scene_tile_delete_button->set_disabled(selected_indices.size() <= 0 || read_only); } void TileSetScenesCollectionSourceEditor::_update_scenes_list() { @@ -342,6 +342,12 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_scenes_collection_source_changed_needs_update) { + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + // Update everything. _update_source_inspector(); _update_scenes_list(); @@ -365,7 +371,12 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source); - if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) { return; } @@ -379,6 +390,16 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; tile_set_source_id = p_source_id; + // Read-only status is false by default + read_only = new_read_only_state; + + if (tile_set.is_valid()) { + scenes_collection_source_inspector->set_read_only(read_only); + tile_inspector->set_read_only(read_only); + + scene_tile_add_button->set_disabled(read_only); + } + // Add the listener again. if (tile_set_scenes_collection_source) { tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h index 0901205a29..2a0e8595c4 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -91,6 +91,8 @@ private: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; int tile_set_source_id = -1; diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 19ee0ae98d..77dd0f7793 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -101,7 +101,7 @@ void TilesEditorPlugin::_thread() { encompassing_rect.expand_to(world_pos); // Texture. - Ref<TileSetAtlasSource> atlas_source = tile_set->get_source(tile_map->get_cell_source_id(0, cell)); + Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map->get_cell_source_id(0, cell)); if (atlas_source.is_valid()) { Vector2i coords = tile_map->get_cell_atlas_coords(0, cell); int alternative = tile_map->get_cell_alternative_tile(0, cell); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 4c5cde926a..af70e64b6a 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/filesystem_dock.h" #include "editor/inspector_dock.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/shader_editor_plugin.h" @@ -1271,18 +1272,55 @@ Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> return dict; } -void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { - Ref<Script> scr = Ref<Script>(p_resource.ptr()); - if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { +void VisualShaderEditor::_get_current_mode_limits(int &r_begin_type, int &r_end_type) const { + switch (visual_shader->get_mode()) { + case Shader::MODE_CANVAS_ITEM: + case Shader::MODE_SPATIAL: { + r_begin_type = 0; + r_end_type = 3; + } break; + case Shader::MODE_PARTICLES: { + r_begin_type = 3; + r_end_type = 5 + r_begin_type; + } break; + case Shader::MODE_SKY: { + r_begin_type = 8; + r_end_type = 1 + r_begin_type; + } break; + case Shader::MODE_FOG: { + r_begin_type = 9; + r_end_type = 1 + r_begin_type; + } break; + default: { + } break; + } +} + +void VisualShaderEditor::_script_created(const Ref<Script> &p_script) { + if (p_script.is_null() || p_script->get_instance_base_type() != "VisualShaderNodeCustom") { + return; + } + Ref<VisualShaderNodeCustom> ref; + ref.instantiate(); + ref->set_script(p_script); + + Dictionary dict = get_custom_node_data(ref); + add_custom_type(dict["name"], dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]); + + _update_options_menu(); +} + +void VisualShaderEditor::_update_custom_script(const Ref<Script> &p_script) { + if (p_script.is_null() || p_script->get_instance_base_type() != "VisualShaderNodeCustom") { return; } Ref<VisualShaderNodeCustom> ref; ref.instantiate(); - ref->set_script(scr); + ref->set_script(p_script); if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) { for (int i = 0; i < add_options.size(); i++) { - if (add_options[i].is_custom && add_options[i].script == scr) { + if (add_options[i].is_custom && add_options[i].script == p_script) { add_options.remove_at(i); _update_options_menu(); // TODO: Make indication for the existed custom nodes with that script on graph to be disabled. @@ -1296,8 +1334,8 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { bool found_type = false; bool need_rebuild = false; - for (int i = 0; i < add_options.size(); i++) { - if (add_options[i].is_custom && add_options[i].script == scr) { + for (int i = custom_node_option_idx; i < add_options.size(); i++) { + if (add_options[i].script == p_script) { found_type = true; add_options.write[i].name = dict["name"]; @@ -1306,31 +1344,11 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { add_options.write[i].category = dict["category"]; add_options.write[i].highend = dict["highend"]; - int max_type = 0; - int type_offset = 0; - switch (visual_shader->get_mode()) { - case Shader::MODE_CANVAS_ITEM: - case Shader::MODE_SPATIAL: { - max_type = 3; - } break; - case Shader::MODE_PARTICLES: { - max_type = 5; - type_offset = 3; - } break; - case Shader::MODE_SKY: { - max_type = 1; - type_offset = 8; - } break; - case Shader::MODE_FOG: { - max_type = 1; - type_offset = 9; - } break; - default: { - } break; - } - max_type = type_offset + max_type; + int begin_type = 0; + int end_type = 0; + _get_current_mode_limits(begin_type, end_type); - for (int t = type_offset; t < max_type; t++) { + for (int t = begin_type; t < end_type; t++) { VisualShader::Type type = (VisualShader::Type)t; Vector<int> nodes = visual_shader->get_node_list(type); @@ -1339,28 +1357,27 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { List<VisualShader::Connection> custom_node_input_connections; List<VisualShader::Connection> custom_node_output_connections; + for (const VisualShader::Connection &E : node_connections) { int from = E.from_node; - int from_idx = E.from_port; + int from_port = E.from_port; int to = E.to_node; - int to_idx = E.to_port; + int to_port = E.to_port; - if (graph_plugin->get_node_script(from) == scr) { - custom_node_output_connections.push_back({ from, from_idx, to, to_idx }); - } else if (graph_plugin->get_node_script(to) == scr) { - custom_node_input_connections.push_back({ from, from_idx, to, to_idx }); + if (graph_plugin->get_node_script(from) == p_script) { + custom_node_output_connections.push_back({ from, from_port, to, to_port }); + } else if (graph_plugin->get_node_script(to) == p_script) { + custom_node_input_connections.push_back({ from, from_port, to, to_port }); } } - for (int j = 0; j < nodes.size(); j++) { - int node_id = nodes[j]; - + for (int node_id : nodes) { Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, node_id); if (vsnode.is_null()) { continue; } Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr()); - if (custom_node.is_null() || custom_node->get_script() != scr) { + if (custom_node.is_null() || custom_node->get_script() != p_script) { continue; } need_rebuild = true; @@ -1429,6 +1446,89 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { } } +void VisualShaderEditor::_resource_saved(const Ref<Resource> &p_resource) { + _update_custom_script(Ref<Script>(p_resource.ptr())); +} + +void VisualShaderEditor::_resources_removed() { + bool has_any_instance = false; + + for (const Ref<Script> &scr : custom_scripts_to_delete) { + for (int i = custom_node_option_idx; i < add_options.size(); i++) { + if (add_options[i].script == scr) { + add_options.remove_at(i); + + // Removes all node instances using that script from the graph. + { + int begin_type = 0; + int end_type = 0; + _get_current_mode_limits(begin_type, end_type); + + for (int t = begin_type; t < end_type; t++) { + VisualShader::Type type = (VisualShader::Type)t; + + List<VisualShader::Connection> node_connections; + visual_shader->get_node_connections(type, &node_connections); + + for (const VisualShader::Connection &E : node_connections) { + int from = E.from_node; + int from_port = E.from_port; + int to = E.to_node; + int to_port = E.to_port; + + if (graph_plugin->get_node_script(from) == scr || graph_plugin->get_node_script(to) == scr) { + visual_shader->disconnect_nodes(type, from, from_port, to, to_port); + graph_plugin->disconnect_nodes(type, from, from_port, to, to_port); + } + } + + Vector<int> nodes = visual_shader->get_node_list(type); + for (int node_id : nodes) { + Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, node_id); + if (vsnode.is_null()) { + continue; + } + Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr()); + if (custom_node.is_null() || custom_node->get_script() != scr) { + continue; + } + visual_shader->remove_node(type, node_id); + graph_plugin->remove_node(type, node_id, false); + + has_any_instance = true; + } + } + } + + break; + } + } + } + if (has_any_instance) { + EditorUndoRedoManager::get_singleton()->clear_history(); // Need to clear undo history, otherwise it may lead to hard-detected errors and crashes (since the script was removed). + ResourceSaver::save(visual_shader, visual_shader->get_path()); + } + _update_options_menu(); + + custom_scripts_to_delete.clear(); + pending_custom_scripts_to_delete = false; +} + +void VisualShaderEditor::_resource_removed(const Ref<Resource> &p_resource) { + Ref<Script> scr = Ref<Script>(p_resource.ptr()); + if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { + return; + } + + custom_scripts_to_delete.push_back(scr); + + if (!pending_custom_scripts_to_delete) { + pending_custom_scripts_to_delete = true; + + call_deferred("_resources_removed"); + } +} + void VisualShaderEditor::_update_options_menu_deferred() { _update_options_menu(); @@ -4901,13 +5001,16 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_expand_output_port", &VisualShaderEditor::_expand_output_port); ClassDB::bind_method("_update_options_menu_deferred", &VisualShaderEditor::_update_options_menu_deferred); ClassDB::bind_method("_rebuild_shader_deferred", &VisualShaderEditor::_rebuild_shader_deferred); + ClassDB::bind_method("_resources_removed", &VisualShaderEditor::_resources_removed); ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); } VisualShaderEditor::VisualShaderEditor() { ShaderLanguage::get_keyword_list(&keyword_list); - EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::update_custom_type)); + EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::_resource_saved)); + FileSystemDock::get_singleton()->get_script_create_dialog()->connect("script_created", callable_mp(this, &VisualShaderEditor::_script_created)); + FileSystemDock::get_singleton()->connect("resource_removed", callable_mp(this, &VisualShaderEditor::_resource_removed)); graph = memnew(GraphEdit); graph->get_zoom_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index c4f6b4952c..519a390ccc 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -188,6 +188,9 @@ class VisualShaderEditor : public VBoxContainer { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; + bool pending_custom_scripts_to_delete = false; + List<Ref<Script>> custom_scripts_to_delete; + bool _block_update_options_menu = false; bool _block_rebuild_shader = false; @@ -503,6 +506,13 @@ class VisualShaderEditor : public VBoxContainer { void _visibility_changed(); + void _get_current_mode_limits(int &r_begin_type, int &r_end_type) const; + void _update_custom_script(const Ref<Script> &p_script); + void _script_created(const Ref<Script> &p_script); + void _resource_saved(const Ref<Resource> &p_resource); + void _resource_removed(const Ref<Resource> &p_resource); + void _resources_removed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -517,7 +527,6 @@ public: void add_custom_type(const String &p_name, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend); Dictionary get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node); - void update_custom_type(const Ref<Resource> &p_resource); virtual Size2 get_minimum_size() const override; void edit(VisualShader *p_visual_shader); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 727b2c9b4d..5800fcca91 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -1286,6 +1286,7 @@ static const char *gdscript_signals_renames[][2] = { { "about_to_show", "about_to_popup" }, // Popup { "button_release", "button_released" }, // XRController3D { "cancelled", "canceled" }, // AcceptDialog + { "item_double_clicked", "item_icon_double_clicked" }, // Tree { "network_peer_connected", "peer_connected" }, // MultiplayerAPI { "network_peer_disconnected", "peer_disconnected" }, // MultiplayerAPI { "network_peer_packet", "peer_packet" }, // MultiplayerAPI @@ -2548,14 +2549,14 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { valid = valid && test_conversion_with_regex("tool", "@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n tool", "\n tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\ntool", "\n\n@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(any_peer) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , get_function", "var size : Vector2 = Vector2() : get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , ", "var size : Vector2 = Vector2() : set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -4086,13 +4087,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("sync")) { - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("slave")) { line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true); @@ -4101,13 +4102,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true); } if (line.contains("puppet")) { - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); } if (line.contains("master")) { line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true); } if (line.contains("master")) { - line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(\"call_local\") func", true); } } } @@ -4155,25 +4156,25 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remote func", "@rpc(any_peer) func", line)); + found_renames.append(line_formatter(current_line, "remote func", "@rpc(\"any_peer\") func", line)); } } if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } if (line.contains("sync")) { old = line; - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "sync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "sync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } @@ -4195,9 +4196,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("puppet")) { old = line; - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(\"call_local\") func", line)); } } @@ -4211,9 +4212,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("master")) { old = line; - line = reg_container.keyword_gdscript_master.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_master.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(\"call_local\") func", line)); } } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 5d1a2a49c5..86e77fbbbe 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2191,7 +2191,7 @@ void SceneTreeDock::_do_create(Node *p_parent) { ERR_FAIL_COND(!child); String new_name = p_parent->validate_child_name(child); - if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { new_name = adjust_name_casing(new_name); } child->set_name(new_name); @@ -3613,7 +3613,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec scene_tree->connect("script_dropped", callable_mp(this, &SceneTreeDock::_script_dropped)); scene_tree->connect("nodes_dragged", callable_mp(this, &SceneTreeDock::_nodes_drag_begin)); - scene_tree->get_scene_tree()->connect("item_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node)); + scene_tree->get_scene_tree()->connect("item_icon_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node)); editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed)); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index e795cc919c..42801cdaf1 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -971,7 +971,7 @@ void SceneTreeEditor::_renamed() { String raw_new_name = which->get_text(0); if (raw_new_name.strip_edges().is_empty()) { // If name is empty, fallback to class name. - if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { raw_new_name = Node::adjust_name_casing(n->get_class()); } else { raw_new_name = n->get_class(); diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 3fe741a582..5bed1b9da3 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -485,7 +485,7 @@ Export a [NodePath] property with a filter for allowed node types. See also [constant PROPERTY_HINT_NODE_PATH_VALID_TYPES]. [codeblock] - @export_node_path(Button, TouchScreenButton) var some_button + @export_node_path("Button", "TouchScreenButton") var some_button [/codeblock] </description> </annotation> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 6b325d6451..4fc3929bbd 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2448,7 +2448,6 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { } String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { - Vector<uint8_t> sourcef; Error err; Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { @@ -2459,88 +2458,31 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b GDScriptParser parser; err = parser.parse(source, p_path, false); + if (err) { + return String(); + } - // TODO: Simplify this code by using the analyzer to get full inheritance. - if (err == OK) { - const GDScriptParser::ClassNode *c = parser.get_tree(); - if (r_icon_path) { - if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { - *r_icon_path = c->icon_path; - } else if (c->icon_path.is_relative_path()) { - *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); - } - } - if (r_base_type) { - const GDScriptParser::ClassNode *subclass = c; - String path = p_path; - GDScriptParser subparser; - while (subclass) { - if (subclass->extends_used) { - if (!subclass->extends_path.is_empty()) { - if (subclass->extends.size() == 0) { - get_global_class_name(subclass->extends_path, r_base_type); - subclass = nullptr; - break; - } else { - Vector<StringName> extend_classes = subclass->extends; - - Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); - if (subfile.is_null()) { - break; - } - String subsource = subfile->get_as_utf8_string(); - - if (subsource.is_empty()) { - break; - } - String subpath = subclass->extends_path; - if (subpath.is_relative_path()) { - subpath = path.get_base_dir().path_join(subpath).simplify_path(); - } - - if (OK != subparser.parse(subsource, subpath, false)) { - break; - } - path = subpath; - subclass = subparser.get_tree(); - - while (extend_classes.size() > 0) { - bool found = false; - for (int i = 0; i < subclass->members.size(); i++) { - if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { - continue; - } - - const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; - if (inner_class->identifier->name == extend_classes[0]) { - extend_classes.remove_at(0); - found = true; - subclass = inner_class; - break; - } - } - if (!found) { - subclass = nullptr; - break; - } - } - } - } else if (subclass->extends.size() == 1) { - *r_base_type = subclass->extends[0]; - subclass = nullptr; - } else { - break; - } - } else { - *r_base_type = "RefCounted"; - subclass = nullptr; - } - } + GDScriptAnalyzer analyzer(&parser); + err = analyzer.resolve_inheritance(); + if (err) { + return String(); + } + + const GDScriptParser::ClassNode *c = parser.get_tree(); + + if (r_base_type) { + *r_base_type = c->get_datatype().native_type; + } + + if (r_icon_path) { + if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { + *r_icon_path = c->icon_path.simplify_path(); + } else if (c->icon_path.is_relative_path()) { + *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); } - return c->identifier != nullptr ? String(c->identifier->name) : String(); } - return String(); + return c->identifier != nullptr ? String(c->identifier->name) : String(); } GDScriptLanguage::GDScriptLanguage() { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e04a962dcb..d7f6126207 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -802,6 +802,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) { + resolve_annotation(E); E->apply(parser, member.variable); } } break; @@ -812,6 +813,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) { + resolve_annotation(E); E->apply(parser, member.constant); } } break; @@ -835,6 +837,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) { + resolve_annotation(E); E->apply(parser, member.signal); } } break; @@ -882,6 +885,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) { + resolve_annotation(E); E->apply(parser, member.m_enum); } } break; @@ -1064,6 +1068,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { + resolve_annotation(E); E->apply(parser, member.function); } @@ -1290,7 +1295,55 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root } void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_annotation) { - // TODO: Add second validation function for annotations, so they can use checked types. + ERR_FAIL_COND_MSG(!parser->valid_annotations.has(p_annotation->name), vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); + + const MethodInfo &annotation_info = parser->valid_annotations[p_annotation->name].info; + + const List<PropertyInfo>::Element *E = annotation_info.arguments.front(); + for (int i = 0; i < p_annotation->arguments.size(); i++) { + GDScriptParser::ExpressionNode *argument = p_annotation->arguments[i]; + const PropertyInfo &argument_info = E->get(); + + if (E->next() != nullptr) { + E = E->next(); + } + + reduce_expression(argument); + + if (!argument->is_constant) { + push_error(vformat(R"(Argument %d of annotation "%s" isn't a constant expression.)", i + 1, p_annotation->name), argument); + return; + } + + Variant value = argument->reduced_value; + + if (value.get_type() != argument_info.type) { +#ifdef DEBUG_ENABLED + if (argument_info.type == Variant::INT && value.get_type() == Variant::FLOAT) { + parser->push_warning(argument, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + + if (!Variant::can_convert_strict(value.get_type(), argument_info.type)) { + push_error(vformat(R"(Invalid argument for annotation "%s": argument %d should be "%s" but is "%s".)", p_annotation->name, i + 1, Variant::get_type_name(argument_info.type), argument->get_datatype().to_string()), argument); + return; + } + + Variant converted_to; + const Variant *converted_from = &value; + Callable::CallError call_error; + Variant::construct(argument_info.type, converted_to, &converted_from, 1, call_error); + + if (call_error.error != Callable::CallError::CALL_OK) { + push_error(vformat(R"(Cannot convert argument %d of annotation "%s" from "%s" to "%s".)", i + 1, p_annotation->name, Variant::get_type_name(value.get_type()), Variant::get_type_name(argument_info.type)), argument); + return; + } + + value = converted_to; + } + + p_annotation->resolved_arguments.push_back(value); + } } void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) { @@ -1486,8 +1539,10 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { for (int i = 0; i < p_suite->statements.size(); i++) { GDScriptParser::Node *stmt = p_suite->statements[i]; - for (GDScriptParser::AnnotationNode *&annotation : stmt->annotations) { - annotation->apply(parser, stmt); + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : stmt->annotations) { + resolve_annotation(E); + E->apply(parser, stmt); } #ifdef DEBUG_ENABLED @@ -1544,10 +1599,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype(); if (p_assignable->infer_datatype) { - if (!initializer_type.is_set() || initializer_type.has_no_type()) { + if (!initializer_type.is_set() || initializer_type.has_no_type() || !initializer_type.is_hard_type()) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); - } else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) { - push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } @@ -2014,7 +2067,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_expression)); break; case GDScriptParser::Node::TERNARY_OPERATOR: - reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression)); + reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression), p_is_root); break; case GDScriptParser::Node::UNARY_OPERATOR: reduce_unary_op(static_cast<GDScriptParser::UnaryOpNode *>(p_expression)); @@ -3534,6 +3587,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } #endif result_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_subscript); } } if (!valid) { @@ -3735,10 +3789,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri p_subscript->set_datatype(result_type); } -void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op) { +void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root) { reduce_expression(p_ternary_op->condition); - reduce_expression(p_ternary_op->true_expr); - reduce_expression(p_ternary_op->false_expr); + reduce_expression(p_ternary_op->true_expr, p_is_root); + reduce_expression(p_ternary_op->false_expr, p_is_root); GDScriptParser::DataType result; @@ -4552,6 +4606,11 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { + resolve_annotation(E); + E->apply(parser, parser->head); + } return resolve_class_inheritance(parser->head, true); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index b22d47982f..5397be33f0 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -99,7 +99,7 @@ class GDScriptAnalyzer { void reduce_preload(GDScriptParser::PreloadNode *p_preload); void reduce_self(GDScriptParser::SelfNode *p_self); void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript); - void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op); + void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 3fc0924b4c..f88ac581ca 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -782,6 +782,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a } } else if (p_annotation->name == SNAME("@export_node_path")) { ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + node.insert_text = node.display.quote(p_quote_style); r_result.insert(node.display, node); List<StringName> node_types; ClassDB::get_inheriters_from_class("Node", &node_types); @@ -790,11 +791,13 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a continue; } ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + option.insert_text = option.display.quote(p_quote_style); r_result.insert(option.display, option); } } else if (p_annotation->name == SNAME("@warning_ignore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + warning.insert_text = warning.display.quote(p_quote_style); r_result.insert(warning.display, warning); } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6dc63c502c..1a744d59ba 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -529,7 +529,7 @@ void GDScriptParser::parse_program() { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::SCRIPT)) { - annotation->apply(this, head); + head->annotations.push_back(annotation); } else { annotation_stack.push_back(annotation); // This annotation must appear after script-level annotations @@ -771,7 +771,6 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)() return; } - // Apply annotations. for (AnnotationNode *&annotation : annotations) { member->annotations.push_back(annotation); } @@ -848,7 +847,7 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { if (previous.type != GDScriptTokenizer::Token::NEWLINE) { push_error(R"(Expected newline after a standalone annotation.)"); } - annotation->apply(this, head); + head->annotations.push_back(annotation); } else { annotation_stack.push_back(annotation); } @@ -1434,7 +1433,11 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali annotation->info = &valid_annotations[annotation->name]; if (!annotation->applies_to(p_valid_targets)) { - push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name)); + if (annotation->applies_to(AnnotationInfo::SCRIPT)) { + push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name)); + } else { + push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name)); + } valid = false; } @@ -1466,7 +1469,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. if (valid) { - valid = validate_annotation_arguments(annotation); + valid = validate_annotation_argument_count(annotation); } return valid ? annotation : nullptr; @@ -1697,6 +1700,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::CALL: case Node::ASSIGNMENT: case Node::AWAIT: + case Node::TERNARY_OPERATOR: // Fine. break; case Node::LAMBDA: @@ -1712,7 +1716,6 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { } } - // Apply annotations to statement. while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) { AnnotationNode *last_annotation = annotation_stack.back()->get(); if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) { @@ -3593,7 +3596,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const { return (info->target_kind & p_target_kinds) > 0; } -bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) { +bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) { ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); const MethodInfo &info = valid_annotations[p_annotation->name].info; @@ -3608,62 +3611,6 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) return false; } - const List<PropertyInfo>::Element *E = info.arguments.front(); - for (int i = 0; i < p_annotation->arguments.size(); i++) { - ExpressionNode *argument = p_annotation->arguments[i]; - const PropertyInfo ¶meter = E->get(); - - if (E->next() != nullptr) { - E = E->next(); - } - - switch (parameter.type) { - case Variant::STRING: - case Variant::STRING_NAME: - case Variant::NODE_PATH: - // Allow "quote-less strings", as long as they are recognized as identifiers. - if (argument->type == Node::IDENTIFIER) { - IdentifierNode *string = static_cast<IdentifierNode *>(argument); - Callable::CallError error; - Vector<Variant> args = varray(string->name); - const Variant *name = args.ptr(); - Variant r; - Variant::construct(parameter.type, r, &(name), 1, error); - p_annotation->resolved_arguments.push_back(r); - if (error.error != Callable::CallError::CALL_OK) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1); - return false; - } - break; - } - [[fallthrough]]; - default: { - if (argument->type != Node::LITERAL) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - return false; - } - - Variant value = static_cast<LiteralNode *>(argument)->value; - if (!Variant::can_convert_strict(value.get_type(), parameter.type)) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - return false; - } - Callable::CallError error; - const Variant *args = &value; - Variant r; - Variant::construct(parameter.type, r, &(args), 1, error); - p_annotation->resolved_arguments.push_back(r); - if (error.error != Callable::CallError::CALL_OK) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1); - return false; - } - break; - } - } - } - return true; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 5da709e8cd..74e12d0b5e 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1401,7 +1401,7 @@ private: // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); - bool validate_annotation_arguments(AnnotationNode *p_annotation); + bool validate_annotation_argument_count(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd new file mode 100644 index 0000000000..75524c32ae --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd @@ -0,0 +1,6 @@ +var num := 1 + +@export_range(num, 10) var a + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out new file mode 100644 index 0000000000..b4f0e79237 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Argument 1 of annotation "@export_range" isn't a constant expression. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd new file mode 100644 index 0000000000..6014ee831c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd @@ -0,0 +1,3 @@ +func test(): + var untyped = 1 + var inferred := untyped diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out new file mode 100644 index 0000000000..b6dc6d0b01 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" variable because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd new file mode 100644 index 0000000000..040aa2e82a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd @@ -0,0 +1,5 @@ +var untyped = 1 +var inferred := untyped + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out new file mode 100644 index 0000000000..b6dc6d0b01 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" variable because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd new file mode 100644 index 0000000000..80c676488e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd @@ -0,0 +1,5 @@ +func check(untyped = 1, inferred := untyped): + pass + +func test(): + check() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out new file mode 100644 index 0000000000..8c9f0c13ae --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" parameter because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd new file mode 100644 index 0000000000..272dce8bbe --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd @@ -0,0 +1,10 @@ +const BEFORE = 1 + +@export_range(-10, 10) var a = 0 +@export_range(1 + 2, absi(-10) + 1) var b = 5 +@export_range(BEFORE + 1, BEFORE + AFTER + 1) var c = 5 + +const AFTER = 10 + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd index 2d2c2bef19..595563541f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd @@ -1,9 +1,4 @@ func test(): - var one_0 = 0 - one_0 = 1 - var one_1 := one_0 - print(one_1) - var two: Variant = 0 two += 2 print(two) diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out index 7536c38490..0ddfa4b75f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out +++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out @@ -1,5 +1,4 @@ GDTEST_OK -1 2 3 4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd index 877a4ea221..4c02fd4b0d 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd @@ -1,12 +1,12 @@ -@warning_ignore(unused_private_class_variable) +@warning_ignore("unused_private_class_variable") var _unused = 2 -@warning_ignore(unused_variable) +@warning_ignore("unused_variable") func test(): print("test") var unused = 3 - @warning_ignore(redundant_await) + @warning_ignore("redundant_await") print(await regular_func()) print("done") diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out index a598ff8424..5fcf1cbcad 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Annotation "@icon" is not allowed in this level. +Annotation "@icon" must be at the top of the script, before "extends" and "class_name". diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd index cc78309ae4..a34cc26e67 100644 --- a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd @@ -1,6 +1,6 @@ # https://github.com/godotengine/godot/issues/50285 -@warning_ignore(unused_local_constant) +@warning_ignore("unused_local_constant") func test(): const CONST_INNER_DICTIONARY = { "key": true } const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = { diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd index 1e072728fc..acf9ff2e21 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd @@ -5,7 +5,7 @@ @export var color: Color @export_color_no_alpha var color_no_alpha: Color -@export_node_path(Sprite2D, Sprite3D, Control, Node) var nodepath := ^"hello" +@export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var nodepath := ^"hello" func test(): diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd index 46b9fbc951..1490a164c9 100644 --- a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd +++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd @@ -2,6 +2,6 @@ func wait() -> void: pass func test(): - @warning_ignore(redundant_await) + @warning_ignore("redundant_await") await wait() print("end") diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd index 1d4b400d81..48af734317 100644 --- a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd +++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd @@ -7,11 +7,11 @@ func test(): func builtin_method(): var pba := PackedByteArray() - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") pba.resize(1) # Built-in validated. func builtin_method_static(): var _pba := PackedByteArray() - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") Vector2.from_angle(PI) # Static built-in validated. diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd index f2368643de..e686cffc48 100644 --- a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd +++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd @@ -11,10 +11,10 @@ class InnerClass: func _init() -> void: prints("Inner") ''' - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") gdscr.reload() var inst = gdscr.new() - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") inst.test() diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd index cc34e71b01..2f55059334 100644 --- a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd +++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd @@ -20,26 +20,26 @@ func test_utility(v, f): assert(not f) # Test unary operator reading from `nil`. func test_builtin_call(v, f): - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") v.angle() # Built-in method call. assert(not f) # Test unary operator reading from `nil`. func test_builtin_call_validated(v: Vector2, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.abs() # Built-in method call validated. assert(not f) # Test unary operator reading from `nil`. func test_object_call(v, f): - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") v.get_reference_count() # Native type method call. assert(not f) # Test unary operator reading from `nil`. func test_object_call_method_bind(v: Resource, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.duplicate() # Native type method call with MethodBind. assert(not f) # Test unary operator reading from `nil`. func test_object_call_ptrcall(v: RefCounted, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.get_reference_count() # Native type method call with ptrcall. assert(not f) # Test unary operator reading from `nil`. diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd index af3f3cb941..efa8270526 100644 --- a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd +++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd @@ -1,7 +1,7 @@ # https://github.com/godotengine/godot/issues/71172 func test(): - @warning_ignore(narrowing_conversion) + @warning_ignore("narrowing_conversion") var foo: int = 0.0 print(typeof(foo) == TYPE_INT) var dict : Dictionary = {"a":0.0} diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp index 6a04e40c73..4e56120ec6 100644 --- a/modules/minimp3/resource_importer_mp3.cpp +++ b/modules/minimp3/resource_importer_mp3.cpp @@ -71,7 +71,7 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const { } void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0)); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs index 5fb29b86da..22a21a1754 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs @@ -111,15 +111,16 @@ namespace Godot.SourceGenerators ClassIsEnum = 65536, NilIsVariant = 131072, Array = 262144, - DoNotShareOnDuplicate = 524288, - HighEndGfx = 1048576, - NodePathFromSceneRoot = 2097152, - ResourceNotPersistent = 4194304, - KeyingIncrements = 8388608, - DeferredSetResource = 16777216, - EditorInstantiateObject = 33554432, - EditorBasicSetting = 67108864, - ReadOnly = 134217728, + AlwaysDuplicate = 524288, + NeverDuplicate = 1048576, + HighEndGfx = 2097152, + NodePathFromSceneRoot = 4194304, + ResourceNotPersistent = 8388608, + KeyingIncrements = 16777216, + DeferredSetResource = 33554432, + EditorInstantiateObject = 67108864, + EditorBasicSetting = 134217728, + ReadOnly = 268435456, Default = 6, NoEditor = 2 } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 03cbfda1bd..5c4abe30e4 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -229,6 +229,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf List<String> tag_stack; bool code_tag = false; + bool line_del = false; int pos = 0; while (pos < bbcode.length()) { @@ -239,20 +240,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } if (brk_pos > pos) { - String text = bbcode.substr(pos, brk_pos - pos); - if (code_tag || tag_stack.size() > 0) { - xml_output.append(text.xml_escape()); - } else { - Vector<String> lines = text.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (i != 0) { - xml_output.append("<para>"); - } + if (!line_del) { + String text = bbcode.substr(pos, brk_pos - pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) { + xml_output.append("<para>"); + } - xml_output.append(lines[i].xml_escape()); + xml_output.append(lines[i].xml_escape()); - if (i != lines.size() - 1) { - xml_output.append("</para>\n"); + if (i != lines.size() - 1) { + xml_output.append("</para>\n"); + } } } } @@ -265,20 +268,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf int brk_end = bbcode.find("]", brk_pos + 1); if (brk_end == -1) { - String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); - if (code_tag || tag_stack.size() > 0) { - xml_output.append(text.xml_escape()); - } else { - Vector<String> lines = text.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (i != 0) { - xml_output.append("<para>"); - } + if (!line_del) { + String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) { + xml_output.append("<para>"); + } - xml_output.append(lines[i].xml_escape()); + xml_output.append(lines[i].xml_escape()); - if (i != lines.size() - 1) { - xml_output.append("</para>\n"); + if (i != lines.size() - 1) { + xml_output.append("</para>\n"); + } } } } @@ -292,7 +297,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); if (!tag_ok) { - xml_output.append("["); + if (!line_del) { + xml_output.append("["); + } pos = brk_pos + 1; continue; } @@ -307,11 +314,20 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append("</c>"); } else if (tag == "/codeblock") { xml_output.append("</code>"); + } else if (tag == "/b") { + xml_output.append("</b>"); + } else if (tag == "/i") { + xml_output.append("</i>"); + } else if (tag == "/csharp") { + xml_output.append("</code>"); + line_del = true; + } else if (tag == "/codeblocks") { + line_del = false; } } else if (code_tag) { xml_output.append("["); pos = brk_pos + 1; - } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ")) { + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) { const int tag_end = tag.find(" "); const String link_tag = tag.substr(0, tag_end); const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" "); @@ -356,6 +372,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } else if (link_tag == "theme_item") { // We do not declare theme_items in any way in C#, so there is nothing to reference _append_xml_undeclared(xml_output, link_target); + } else if (link_tag == "param") { + _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false)); } pos = brk_end + 1; @@ -377,8 +395,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf #endif "\"/>"); } else if (tag == "Variant") { - // We use System.Object for Variant, so there is no Variant type in C# - xml_output.append("<c>Variant</c>"); + xml_output.append("<see cref=\"Godot.Variant\"/>"); } else if (tag == "String") { xml_output.append("<see cref=\"string\"/>"); } else if (tag == "Nil") { @@ -428,11 +445,13 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf pos = brk_end + 1; } else if (tag == "b") { - // bold is not supported in xml comments + xml_output.append("<b>"); + pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "i") { - // italics is not supported in xml comments + xml_output.append("<i>"); + pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "code") { @@ -447,6 +466,17 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf code_tag = true; pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "codeblocks") { + line_del = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "csharp") { + xml_output.append("<code>"); + + line_del = false; + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); } else if (tag == "kbd") { // keyboard combinations are not supported in xml comments pos = brk_end + 1; @@ -459,7 +489,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now. pos = brk_end + 1; } else if (tag == "u") { - // underline is not supported in xml comments + // underline is not supported in Rider xml comments pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "s") { @@ -510,7 +540,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf pos = brk_end + 1; tag_stack.push_front("font"); } else { - xml_output.append("["); // ignore + if (!line_del) { + xml_output.append("["); // ignore + } pos = brk_pos + 1; } } diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 2b5db6462c..5baf6db2e8 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -535,32 +535,32 @@ uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) con return link->get_navigation_layers(); } -COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location) { +COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position) { NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND(link == nullptr); - link->set_start_location(p_location); + link->set_start_position(p_position); } -Vector3 GodotNavigationServer::link_get_start_location(RID p_link) const { +Vector3 GodotNavigationServer::link_get_start_position(RID p_link) const { const NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND_V(link == nullptr, Vector3()); - return link->get_start_location(); + return link->get_start_position(); } -COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location) { +COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position) { NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND(link == nullptr); - link->set_end_location(p_location); + link->set_end_position(p_position); } -Vector3 GodotNavigationServer::link_get_end_location(RID p_link) const { +Vector3 GodotNavigationServer::link_get_end_position(RID p_link) const { const NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND_V(link == nullptr, Vector3()); - return link->get_end_location(); + return link->get_end_position(); } COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) { diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index a87a88d3bc..efefa60de8 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -158,10 +158,10 @@ public: virtual bool link_is_bidirectional(RID p_link) const override; COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers); virtual uint32_t link_get_navigation_layers(RID p_link) const override; - COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location); - virtual Vector3 link_get_start_location(RID p_link) const override; - COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location); - virtual Vector3 link_get_end_location(RID p_link) const override; + COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position); + virtual Vector3 link_get_start_position(RID p_link) const override; + COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position); + virtual Vector3 link_get_end_position(RID p_link) const override; COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost); virtual real_t link_get_enter_cost(RID p_link) const override; COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost); diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp index 05d2b21487..ad87cc0b05 100644 --- a/modules/navigation/nav_link.cpp +++ b/modules/navigation/nav_link.cpp @@ -42,13 +42,13 @@ void NavLink::set_bidirectional(bool p_bidirectional) { link_dirty = true; } -void NavLink::set_start_location(const Vector3 p_location) { - start_location = p_location; +void NavLink::set_start_position(const Vector3 p_position) { + start_position = p_position; link_dirty = true; } -void NavLink::set_end_location(const Vector3 p_location) { - end_location = p_location; +void NavLink::set_end_position(const Vector3 p_position) { + end_position = p_position; link_dirty = true; } diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h index 47c1211db8..0b8ad4db69 100644 --- a/modules/navigation/nav_link.h +++ b/modules/navigation/nav_link.h @@ -37,8 +37,8 @@ class NavLink : public NavBase { NavMap *map = nullptr; bool bidirectional = true; - Vector3 start_location; - Vector3 end_location; + Vector3 start_position; + Vector3 end_position; bool link_dirty = true; @@ -57,14 +57,14 @@ public: return bidirectional; } - void set_start_location(Vector3 p_location); - Vector3 get_start_location() const { - return start_location; + void set_start_position(Vector3 p_position); + Vector3 get_start_position() const { + return start_position; } - void set_end_location(Vector3 p_location); - Vector3 get_end_location() const { - return end_location; + void set_end_position(Vector3 p_position); + Vector3 get_end_position() const { + return end_position; } bool check_dirty(); diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index fe255c1ce8..d763b1d3bc 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -780,8 +780,8 @@ void NavMap::sync() { // Search for polygons within range of a nav link. for (const NavLink *link : links) { - const Vector3 start = link->get_start_location(); - const Vector3 end = link->get_end_location(); + const Vector3 start = link->get_start_position(); + const Vector3 end = link->get_end_position(); gd::Polygon *closest_start_polygon = nullptr; real_t closest_start_distance = link_connection_radius; diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 50437469aa..06a1a1f403 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -125,7 +125,7 @@ struct NavigationPoly { Vector3 back_navigation_edge_pathway_start; Vector3 back_navigation_edge_pathway_end; - /// The entry location of this poly. + /// The entry position of this poly. Vector3 entry; /// The distance to the destination. float traveled_distance = 0.0; diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 64d254f221..b712d63030 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -73,7 +73,7 @@ String ResourceImporterOggVorbis::get_preset_name(int p_idx) const { } void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0)); diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index ba1750386f..0c18acbcb1 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -90,7 +90,7 @@ You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices. </description> <tutorials> - <link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link> + <link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link> </tutorials> <methods> <method name="get_input_source_target_ray_mode" qualifiers="const"> diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 8485c62218..97b2eea4d7 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -347,9 +347,8 @@ Transform3D WebXRInterfaceJS::get_camera_transform() { ERR_FAIL_NULL_V(xr_server, camera_transform); if (initialized) { - float world_scale = xr_server->get_world_scale(); + double world_scale = xr_server->get_world_scale(); - // just scale our origin point of our transform Transform3D _head_transform = head_transform; _head_transform.origin *= world_scale; @@ -372,13 +371,8 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran Transform3D transform_for_view = _js_matrix_to_transform(js_matrix); - float world_scale = xr_server->get_world_scale(); - // Scale only the center point of our eye transform, so we don't scale the - // distance between the eyes. - Transform3D _head_transform = head_transform; - transform_for_view.origin -= _head_transform.origin; - _head_transform.origin *= world_scale; - transform_for_view.origin += _head_transform.origin; + double world_scale = xr_server->get_world_scale(); + transform_for_view.origin *= world_scale; return p_cam_transform * xr_server->get_reference_frame() * transform_for_view; }; diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 277195a054..17903b3965 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -80,7 +80,20 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod ev.instantiate(); Key physical_keycode = godot_code_from_android_code(p_physical_keycode); - Key keycode = fix_keycode(unicode, physical_keycode); + Key keycode = physical_keycode; + if (unicode == '\b') { // 0x08 + keycode = Key::BACKSPACE; + } else if (unicode == '\t') { // 0x09 + keycode = Key::TAB; + } else if (unicode == '\n') { // 0x0A + keycode = Key::ENTER; + } else if (unicode == 0x1B) { + keycode = Key::ESCAPE; + } else if (unicode == 0x1F) { + keycode = Key::KEY_DELETE; + } else { + keycode = fix_keycode(unicode, physical_keycode); + } switch (physical_keycode) { case Key::SHIFT: { diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt index f848089aa8..71385315ae 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt @@ -31,12 +31,11 @@ package org.godotengine.editor import android.Manifest +import android.app.ActivityManager +import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.os.Debug -import android.os.Environment +import android.os.* import android.util.Log import android.widget.Toast import androidx.window.layout.WindowMetricsCalculator @@ -64,11 +63,18 @@ open class GodotEditor : FullScreenGodotApp() { private const val COMMAND_LINE_PARAMS = "command_line_params" + private const val EDITOR_ID = 777 private const val EDITOR_ARG = "--editor" private const val EDITOR_ARG_SHORT = "-e" + private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor" + private const val GAME_ID = 667 + private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame" + + private const val PROJECT_MANAGER_ID = 555 private const val PROJECT_MANAGER_ARG = "--project-manager" private const val PROJECT_MANAGER_ARG_SHORT = "-p" + private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager" } private val commandLineParams = ArrayList<String>() @@ -102,9 +108,10 @@ open class GodotEditor : FullScreenGodotApp() { override fun getCommandLine() = commandLineParams - override fun onNewGodotInstanceRequested(args: Array<String>) { + override fun onNewGodotInstanceRequested(args: Array<String>): Int { // Parse the arguments to figure out which activity to start. var targetClass: Class<*> = GodotGame::class.java + var instanceId = GAME_ID // Whether we should launch the new godot instance in an adjacent window // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT @@ -115,12 +122,14 @@ open class GodotEditor : FullScreenGodotApp() { if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) { targetClass = GodotEditor::class.java launchAdjacent = false + instanceId = EDITOR_ID break } if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) { targetClass = GodotProjectManager::class.java launchAdjacent = false + instanceId = PROJECT_MANAGER_ID break } } @@ -139,6 +148,37 @@ open class GodotEditor : FullScreenGodotApp() { Log.d(TAG, "Starting $targetClass") startActivity(newInstance) } + return instanceId + } + + override fun onGodotForceQuit(godotInstanceId: Int): Boolean { + val processNameSuffix = when (godotInstanceId) { + GAME_ID -> { + GAME_PROCESS_NAME_SUFFIX + } + EDITOR_ID -> { + EDITOR_PROCESS_NAME_SUFFIX + } + PROJECT_MANAGER_ID -> { + PROJECT_MANAGER_PROCESS_NAME_SUFFIX + } + else -> "" + } + if (processNameSuffix.isBlank()) { + return false + } + + val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val runningProcesses = activityManager.runningAppProcesses + for (runningProcess in runningProcesses) { + if (runningProcess.processName.endsWith(processNameSuffix)) { + Log.v(TAG, "Killing Godot process ${runningProcess.processName}") + Process.killProcess(runningProcess.pid) + return true + } + } + + return false } // Get the screen's density scale diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index 863e67f1e9..65032d6a68 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -74,28 +74,36 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God public void onDestroy() { Log.v(TAG, "Destroying Godot app..."); super.onDestroy(); - onGodotForceQuit(godotFragment); + terminateGodotInstance(godotFragment); } @Override public final void onGodotForceQuit(Godot instance) { + runOnUiThread(() -> { + terminateGodotInstance(instance); + }); + } + + private void terminateGodotInstance(Godot instance) { if (instance == godotFragment) { Log.v(TAG, "Force quitting Godot instance"); - ProcessPhoenix.forceQuit(this); + ProcessPhoenix.forceQuit(FullScreenGodotApp.this); } } @Override public final void onGodotRestartRequested(Godot instance) { - if (instance == godotFragment) { - // It's very hard to properly de-initialize Godot on Android to restart the game - // from scratch. Therefore, we need to kill the whole app process and relaunch it. - // - // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including - // releasing and reloading native libs or resetting their state somehow and clearing statics). - Log.v(TAG, "Restarting Godot instance..."); - ProcessPhoenix.triggerRebirth(this); - } + runOnUiThread(() -> { + if (instance == godotFragment) { + // It's very hard to properly de-initialize Godot on Android to restart the game + // from scratch. Therefore, we need to kill the whole app process and relaunch it. + // + // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including + // releasing and reloading native libs or resetting their state somehow and clearing statics). + Log.v(TAG, "Restarting Godot instance..."); + ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this); + } + }); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 905db13c85..50263bc392 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -348,11 +348,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } public void restart() { - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onGodotRestartRequested(this); - } - }); + if (godotHost != null) { + godotHost.onGodotRestartRequested(this); + } } public void alert(final String message, final String title) { @@ -889,11 +887,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC private void forceQuit() { // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each // native Godot components that is started in Godot#onVideoInit. - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onGodotForceQuit(this); - } - }); + forceQuit(0); + } + + @Keep + private boolean forceQuit(int instanceId) { + if (godotHost == null) { + return false; + } + if (instanceId == 0) { + godotHost.onGodotForceQuit(this); + return true; + } else { + return godotHost.onGodotForceQuit(instanceId); + } } private boolean obbIsCorrupted(String f, String main_pack_md5) { @@ -1052,11 +1059,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } @Keep - private void createNewGodotInstance(String[] args) { - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onNewGodotInstanceRequested(args); - } - }); + private int createNewGodotInstance(String[] args) { + if (godotHost != null) { + return godotHost.onNewGodotInstanceRequested(args); + } + return 0; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 256d04e3a5..7700b9b628 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -55,21 +55,35 @@ public interface GodotHost { default void onGodotMainLoopStarted() {} /** - * Invoked on the UI thread as the last step of the Godot instance clean up phase. + * Invoked on the render thread to terminate the given Godot instance. */ default void onGodotForceQuit(Godot instance) {} /** - * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host + * Invoked on the render thread to terminate the Godot instance with the given id. + * @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested} + * + * @return true if successful, false otherwise. + */ + default boolean onGodotForceQuit(int godotInstanceId) { + return false; + } + + /** + * Invoked on the render thread when the Godot instance wants to be restarted. It's up to the host * to perform the appropriate action(s). */ default void onGodotRestartRequested(Godot instance) {} /** - * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to + * Invoked on the render thread when a new Godot instance is requested. It's up to the host to * perform the appropriate action(s). * * @param args Arguments used to initialize the new instance. + * + * @return the id of the new instance. See {@code onGodotForceQuit} */ - default void onNewGodotInstanceRequested(String[] args) {} + default int onNewGodotInstanceRequested(String[] args) { + return 0; + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt index 9649b0aecc..b9b7ebac6e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt @@ -64,7 +64,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler. override fun hasDirId(dirId: Int) = dirs.indexOfKey(dirId) >= 0 override fun dirOpen(path: String): Int { - val assetsPath = getAssetsPath(path) ?: return INVALID_DIR_ID + val assetsPath = getAssetsPath(path) try { val files = assetManager.list(assetsPath) ?: return INVALID_DIR_ID // Empty directories don't get added to the 'assets' directory, so @@ -99,7 +99,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler. } override fun fileExists(path: String): Boolean { - val assetsPath = getAssetsPath(path) ?: return false + val assetsPath = getAssetsPath(path) try { val files = assetManager.list(assetsPath) ?: return false // Empty directories don't get added to the 'assets' directory, so diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 03548d11f6..9d9d087896 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -60,7 +60,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ // get some Godot method pointers... _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z"); _restart = p_env->GetMethodID(godot_class, "restart", "()V"); - _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V"); + _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z"); _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V"); _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I"); @@ -77,7 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;"); _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); - _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V"); + _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I"); _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); // get some Activity method pointers... @@ -179,14 +179,15 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) { } } -void GodotJavaWrapper::force_quit(JNIEnv *p_env) { +bool GodotJavaWrapper::force_quit(JNIEnv *p_env, int p_instance_id) { if (_finish) { if (p_env == nullptr) { p_env = get_jni_env(); } - ERR_FAIL_NULL(p_env); - p_env->CallVoidMethod(godot_instance, _finish); + ERR_FAIL_NULL_V(p_env, false); + return p_env->CallBooleanMethod(godot_instance, _finish, p_instance_id); } + return false; } void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { @@ -345,14 +346,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) { } } -void GodotJavaWrapper::create_new_godot_instance(List<String> args) { +int GodotJavaWrapper::create_new_godot_instance(List<String> args) { if (_create_new_godot_instance) { JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL(env); + ERR_FAIL_NULL_V(env, 0); jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); for (int i = 0; i < args.size(); i++) { env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data())); } - env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs); + return env->CallIntMethod(godot_instance, _create_new_godot_instance, jargs); + } else { + return 0; } } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 5dad2a3eb9..1bd79584d8 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -85,7 +85,7 @@ public: void on_godot_setup_completed(JNIEnv *p_env = nullptr); void on_godot_main_loop_started(JNIEnv *p_env = nullptr); void restart(JNIEnv *p_env = nullptr); - void force_quit(JNIEnv *p_env = nullptr); + bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0); void set_keep_screen_on(bool p_enabled); void alert(const String &p_message, const String &p_title); int get_gles_version_code(); @@ -103,7 +103,7 @@ public: bool is_activity_resumed(); void vibrate(int p_duration_ms); String get_input_fallback_mapping(); - void create_new_godot_instance(List<String> args); + int create_new_godot_instance(List<String> args); }; #endif // JAVA_GODOT_WRAPPER_H diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 29b91983bf..725fea8d54 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -739,9 +739,19 @@ Error OS_Android::create_process(const String &p_path, const List<String> &p_arg } Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) { - godot_java->create_new_godot_instance(p_arguments); + int instance_id = godot_java->create_new_godot_instance(p_arguments); + if (r_child_id) { + *r_child_id = instance_id; + } return OK; } +Error OS_Android::kill(const ProcessID &p_pid) { + if (godot_java->force_quit(nullptr, p_pid)) { + return OK; + } + return OS_Unix::kill(p_pid); +} + OS_Android::~OS_Android() { } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 9b43797580..53910b1498 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -158,6 +158,7 @@ public: virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override; virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; + virtual Error kill(const ProcessID &p_pid) override; virtual bool _check_internal_feature_support(const String &p_feature) override; OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub index fcd739cdc9..3c5dc78c60 100644 --- a/platform/linuxbsd/SCsub +++ b/platform/linuxbsd/SCsub @@ -11,6 +11,7 @@ common_linuxbsd = [ "joypad_linux.cpp", "freedesktop_portal_desktop.cpp", "freedesktop_screensaver.cpp", + "xkbcommon-so_wrap.c", ] if env["x11"]: diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub index d869ce9ecc..8b2e2aabe4 100644 --- a/platform/linuxbsd/x11/SCsub +++ b/platform/linuxbsd/x11/SCsub @@ -9,7 +9,6 @@ source_files = [ "dynwrappers/xcursor-so_wrap.c", "dynwrappers/xinerama-so_wrap.c", "dynwrappers/xinput2-so_wrap.c", - "dynwrappers/xkbcommon-so_wrap.c", "dynwrappers/xrandr-so_wrap.c", "dynwrappers/xrender-so_wrap.c", "dynwrappers/xext-so_wrap.c", diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 9971fe8c79..428cf3a145 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1610,7 +1610,7 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent // a subwindow and its parent are both destroyed. if (!wd_window.no_focus && !wd_window.is_popup && wd_window.focused) { if ((xwa.map_state == IsViewable) && !wd_parent.no_focus && !wd_window.is_popup) { - XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime); + XSetInputFocus(x11_display, wd_parent.x11_window, RevertToParent, CurrentTime); } } } else { @@ -2496,6 +2496,9 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win return; } if (!wd.focused) { + wd.ime_active = false; + im_text = String(); + im_selection = Vector2i(); return; } @@ -2524,7 +2527,6 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win im_text = String(); im_selection = Vector2i(); } - OS_Unix::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); } void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { @@ -2931,6 +2933,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, xkeyevent_no_mod.state &= ~ShiftMask; xkeyevent_no_mod.state &= ~ControlMask; XLookupString(xkeyevent, str, 255, &keysym_unicode, nullptr); + XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr); String keysym; if (xkb_keysym_to_utf32 && xkb_keysym_to_upper) { @@ -3316,42 +3319,43 @@ void DisplayServerX11::_xim_preedit_draw_callback(::XIM xim, ::XPointer client_d WindowData &wd = ds->windows[window_id]; XIMText *xim_text = call_data->text; - if (xim_text != nullptr) { - String changed_text; - if (xim_text->encoding_is_wchar) { - changed_text = String(xim_text->string.wide_char); - } else { - changed_text.parse_utf8(xim_text->string.multi_byte); - } + if (wd.ime_active) { + if (xim_text != nullptr) { + String changed_text; + if (xim_text->encoding_is_wchar) { + changed_text = String(xim_text->string.wide_char); + } else { + changed_text.parse_utf8(xim_text->string.multi_byte); + } - if (call_data->chg_length < 0) { - ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text; - } else { - ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text + ds->im_text.substr(call_data->chg_length); - } + if (call_data->chg_length < 0) { + ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text; + } else { + ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text + ds->im_text.substr(call_data->chg_length); + } - // Find the start and end of the selection. - int start = 0, count = 0; - for (int i = 0; i < xim_text->length; i++) { - if (xim_text->feedback[i] & XIMReverse) { - if (count == 0) { - start = i; - count = 1; - } else { - count++; + // Find the start and end of the selection. + int start = 0, count = 0; + for (int i = 0; i < xim_text->length; i++) { + if (xim_text->feedback[i] & XIMReverse) { + if (count == 0) { + start = i; + count = 1; + } else { + count++; + } } } - } - if (count > 0) { - ds->im_selection = Point2i(start + call_data->chg_first, count); + if (count > 0) { + ds->im_selection = Point2i(start + call_data->chg_first, count); + } else { + ds->im_selection = Point2i(call_data->caret, 0); + } } else { - ds->im_selection = Point2i(call_data->caret, 0); + ds->im_text = String(); + ds->im_selection = Point2i(); } - } else { - ds->im_text = String(); - ds->im_selection = Point2i(); - } - if (wd.ime_active) { + OS_Unix::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); } } @@ -4856,7 +4860,11 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V { wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); - wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); + + XSetWindowAttributes window_attributes_ime = {}; + window_attributes_ime.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask; + + wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWEventMask, &window_attributes_ime); // Enable receiving notification when the window is initialized (MapNotify) // so the focus can be set at the right time. diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 290e3d6a5e..bfb97ae44c 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -75,10 +75,11 @@ #include "dynwrappers/xext-so_wrap.h" #include "dynwrappers/xinerama-so_wrap.h" #include "dynwrappers/xinput2-so_wrap.h" -#include "dynwrappers/xkbcommon-so_wrap.h" #include "dynwrappers/xrandr-so_wrap.h" #include "dynwrappers/xrender-so_wrap.h" +#include "../xkbcommon-so_wrap.h" + typedef struct _xrr_monitor_info { Atom name; Bool primary = false; diff --git a/platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.c b/platform/linuxbsd/xkbcommon-so_wrap.c index 601d4c5052..601d4c5052 100644 --- a/platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.c +++ b/platform/linuxbsd/xkbcommon-so_wrap.c diff --git a/platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.h b/platform/linuxbsd/xkbcommon-so_wrap.h index f7e6f4c4cf..f7e6f4c4cf 100644 --- a/platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.h +++ b/platform/linuxbsd/xkbcommon-so_wrap.h diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 521bf85b27..89a7114583 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1671,9 +1671,11 @@ void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p if (p_active) { wd.ime_active = true; ImmAssociateContext(wd.hWnd, wd.im_himc); + CreateCaret(wd.hWnd, NULL, 1, 1); window_set_ime_position(wd.im_position, p_window); } else { ImmAssociateContext(wd.hWnd, (HIMC)0); + DestroyCaret(); wd.ime_active = false; } } @@ -3440,9 +3442,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA [[fallthrough]]; } case WM_CHAR: { - if (windows[window_id].ime_in_progress) { - break; - } ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); // Make sure we don't include modifiers for the modifier key itself. @@ -3469,15 +3468,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_IME_COMPOSITION: { CANDIDATEFORM cf; cf.dwIndex = 0; - cf.dwStyle = CFS_EXCLUDE; + + cf.dwStyle = CFS_CANDIDATEPOS; cf.ptCurrentPos.x = windows[window_id].im_position.x; cf.ptCurrentPos.y = windows[window_id].im_position.y; + ImmSetCandidateWindow(windows[window_id].im_himc, &cf); + + cf.dwStyle = CFS_EXCLUDE; cf.rcArea.left = windows[window_id].im_position.x; cf.rcArea.right = windows[window_id].im_position.x; cf.rcArea.top = windows[window_id].im_position.y; cf.rcArea.bottom = windows[window_id].im_position.y; ImmSetCandidateWindow(windows[window_id].im_himc, &cf); + if (windows[window_id].ime_active) { + SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y); OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); } } break; @@ -3663,7 +3668,7 @@ void DisplayServerWindows::_process_key_events() { memset(keyboard_state, 0, 256); wchar_t chars[256] = {}; UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX); - if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 0, GetKeyboardLayout(0)) > 0) { + if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) { String keysym = String::utf16((char16_t *)chars, 255); if (!keysym.is_empty()) { key_label = fix_key_label(keysym[0], keycode); @@ -3715,7 +3720,7 @@ void DisplayServerWindows::_process_key_events() { memset(keyboard_state, 0, 256); wchar_t chars[256] = {}; UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX); - if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 0, GetKeyboardLayout(0)) > 0) { + if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) { String keysym = String::utf16((char16_t *)chars, 255); if (!keysym.is_empty()) { key_label = fix_key_label(keysym[0], keycode); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 6b842e6e6b..907fb7e6a9 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -76,10 +76,10 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent2D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent2D::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location); - ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location); + ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent2D::set_target_position); + ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position); - ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location); + ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent2D::get_next_path_position); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity); ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result); @@ -88,12 +88,12 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished); - ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent2D::get_final_location); + ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent2D::get_final_position); ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done); ADD_GROUP("Pathfinding", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_location", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_location", "get_target_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,1000,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); @@ -328,17 +328,17 @@ real_t NavigationAgent2D::get_path_max_distance() { return path_max_distance; } -void NavigationAgent2D::set_target_location(Vector2 p_location) { - target_location = p_location; +void NavigationAgent2D::set_target_position(Vector2 p_position) { + target_position = p_position; target_position_submitted = true; _request_repath(); } -Vector2 NavigationAgent2D::get_target_location() const { - return target_location; +Vector2 NavigationAgent2D::get_target_position() const { + return target_position; } -Vector2 NavigationAgent2D::get_next_location() { +Vector2 NavigationAgent2D::get_next_path_position() { update_navigation(); const Vector<Vector2> &navigation_path = navigation_result->get_path(); @@ -352,7 +352,7 @@ Vector2 NavigationAgent2D::get_next_location() { real_t NavigationAgent2D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); - return agent_parent->get_global_position().distance_to(target_location); + return agent_parent->get_global_position().distance_to(target_position); } bool NavigationAgent2D::is_target_reached() const { @@ -360,7 +360,7 @@ bool NavigationAgent2D::is_target_reached() const { } bool NavigationAgent2D::is_target_reachable() { - return target_desired_distance >= get_final_location().distance_to(target_location); + return target_desired_distance >= get_final_position().distance_to(target_position); } bool NavigationAgent2D::is_navigation_finished() { @@ -368,7 +368,7 @@ bool NavigationAgent2D::is_navigation_finished() { return navigation_finished; } -Vector2 NavigationAgent2D::get_final_location() { +Vector2 NavigationAgent2D::get_final_position() { update_navigation(); const Vector<Vector2> &navigation_path = navigation_result->get_path(); @@ -450,7 +450,7 @@ void NavigationAgent2D::update_navigation() { if (reload_path) { navigation_query->set_start_position(origin); - navigation_query->set_target_position(target_location); + navigation_query->set_target_position(target_position); navigation_query->set_navigation_layers(navigation_layers); navigation_query->set_metadata_flags(path_metadata_flags); @@ -472,7 +472,7 @@ void NavigationAgent2D::update_navigation() { // Check if we can advance the navigation path if (navigation_finished == false) { - // Advances to the next far away location. + // Advances to the next far away position. const Vector<Vector2> &navigation_path = navigation_result->get_path(); const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); @@ -482,7 +482,7 @@ void NavigationAgent2D::update_navigation() { Dictionary details; const Vector2 waypoint = navigation_path[navigation_path_index]; - details[SNAME("location")] = waypoint; + details[SNAME("position")] = waypoint; int waypoint_type = -1; if (path_metadata_flags.has_flag(NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 190a2fcbda..9787bb1bdb 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -60,7 +60,7 @@ class NavigationAgent2D : public Node { real_t path_max_distance = 100.0; - Vector2 target_location; + Vector2 target_position; bool target_position_submitted = false; Ref<NavigationPathQueryParameters2D> navigation_query; Ref<NavigationPathQueryResult2D> navigation_result; @@ -143,10 +143,10 @@ public: void set_path_max_distance(real_t p_pmd); real_t get_path_max_distance(); - void set_target_location(Vector2 p_location); - Vector2 get_target_location() const; + void set_target_position(Vector2 p_position); + Vector2 get_target_position() const; - Vector2 get_next_location(); + Vector2 get_next_path_position(); Ref<NavigationPathQueryResult2D> get_current_navigation_result() const { return navigation_result; @@ -162,7 +162,7 @@ public: bool is_target_reached() const; bool is_target_reachable(); bool is_navigation_finished(); - Vector2 get_final_location(); + Vector2 get_final_position(); void set_velocity(Vector2 p_velocity); void _avoidance_done(Vector3 p_new_velocity); diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 9ef0ba617e..65bf178cc5 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -48,11 +48,11 @@ void NavigationLink2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink2D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink2D::get_navigation_layer_value); - ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink2D::set_start_location); - ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink2D::get_start_location); + ClassDB::bind_method(D_METHOD("set_start_position", "position"), &NavigationLink2D::set_start_position); + ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationLink2D::get_start_position); - ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink2D::set_end_location); - ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink2D::get_end_location); + ClassDB::bind_method(D_METHOD("set_end_position", "position"), &NavigationLink2D::set_end_position); + ClassDB::bind_method(D_METHOD("get_end_position"), &NavigationLink2D::get_end_position); ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink2D::set_enter_cost); ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink2D::get_enter_cost); @@ -63,12 +63,38 @@ void NavigationLink2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_location"), "set_start_location", "get_start_location"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_position"), "set_end_position", "get_end_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); } +#ifndef DISABLE_DEPRECATED +bool NavigationLink2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "start_location") { + set_start_position(p_value); + return true; + } + if (p_name == "end_location") { + set_end_position(p_value); + return true; + } + return false; +} + +bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "start_location") { + r_ret = get_start_position(); + return true; + } + if (p_name == "end_location") { + r_ret = get_end_position(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void NavigationLink2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -77,15 +103,15 @@ void NavigationLink2D::_notification(int p_what) { // Update global positions for the link. Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } } break; case NOTIFICATION_TRANSFORM_CHANGED: { // Update global positions for the link. Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } break; case NOTIFICATION_EXIT_TREE: { NavigationServer2D::get_singleton()->link_set_map(link, RID()); @@ -102,9 +128,9 @@ void NavigationLink2D::_notification(int p_what) { real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - draw_line(get_start_location(), get_end_location(), color); - draw_arc(get_start_location(), radius, 0, Math_TAU, 10, color); - draw_arc(get_end_location(), radius, 0, Math_TAU, 10, color); + draw_line(get_start_position(), get_end_position(), color); + draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); } #endif // DEBUG_ENABLED } break; @@ -119,14 +145,14 @@ Rect2 NavigationLink2D::_edit_get_rect() const { real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - Rect2 rect(get_start_location(), Size2()); - rect.expand_to(get_end_location()); + Rect2 rect(get_start_position(), Size2()); + rect.expand_to(get_end_position()); rect.grow_by(radius); return rect; } bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - Point2 segment[2] = { get_start_location(), get_end_location() }; + Point2 segment[2] = { get_start_position(), get_end_position() }; Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment); return p_point.distance_to(closest_point) < p_tolerance; @@ -199,19 +225,19 @@ bool NavigationLink2D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } -void NavigationLink2D::set_start_location(Vector2 p_location) { - if (start_location.is_equal_approx(p_location)) { +void NavigationLink2D::set_start_position(Vector2 p_position) { + if (start_position.is_equal_approx(p_position)) { return; } - start_location = p_location; + start_position = p_position; if (!is_inside_tree()) { return; } Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); update_configuration_warnings(); @@ -222,19 +248,19 @@ void NavigationLink2D::set_start_location(Vector2 p_location) { #endif // DEBUG_ENABLED } -void NavigationLink2D::set_end_location(Vector2 p_location) { - if (end_location.is_equal_approx(p_location)) { +void NavigationLink2D::set_end_position(Vector2 p_position) { + if (end_position.is_equal_approx(p_position)) { return; } - end_location = p_location; + end_position = p_position; if (!is_inside_tree()) { return; } Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); update_configuration_warnings(); @@ -270,8 +296,8 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) { PackedStringArray NavigationLink2D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (start_location.is_equal_approx(end_location)) { - warnings.push_back(RTR("NavigationLink2D start location should be different than the end location to be useful.")); + if (start_position.is_equal_approx(end_position)) { + warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful.")); } return warnings; diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index e14ee5adb9..5bf2a72358 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -40,8 +40,8 @@ class NavigationLink2D : public Node2D { RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector2 end_location; - Vector2 start_location; + Vector2 end_position; + Vector2 start_position; real_t enter_cost = 0.0; real_t travel_cost = 1.0; @@ -49,6 +49,11 @@ protected: static void _bind_methods(); void _notification(int p_what); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: #ifdef TOOLS_ENABLED virtual Rect2 _edit_get_rect() const override; @@ -67,11 +72,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; - void set_start_location(Vector2 p_location); - Vector2 get_start_location() const { return start_location; } + void set_start_position(Vector2 p_position); + Vector2 get_start_position() const { return start_position; } - void set_end_location(Vector2 p_location); - Vector2 get_end_location() const { return end_location; } + void set_end_position(Vector2 p_position); + Vector2 get_end_position() const { return end_position; } void set_enter_cost(real_t p_enter_cost); real_t get_enter_cost() const { return enter_cost; } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 02388a7681..83cfffc333 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -562,7 +562,7 @@ void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const { PropertyInfo(Variant::OBJECT, PNAME("modification_stack"), PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_ALWAYS_DUPLICATE)); } void Skeleton2D::_make_bone_setup_dirty() { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index e91948c6e1..47eb1eaa40 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -554,7 +554,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.001,suffix:m"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001,or_greater,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"); @@ -602,7 +602,7 @@ void Camera3D::set_fov(real_t p_fov) { } void Camera3D::set_size(real_t p_size) { - ERR_FAIL_COND(p_size < 0.001 || p_size > 16384); + ERR_FAIL_COND(p_size <= CMP_EPSILON); size = p_size; _update_camera_mode(); } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 0034bf78b9..fe7ddcb06e 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -80,10 +80,10 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location); - ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location); + ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent3D::set_target_position); + ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent3D::get_target_position); - ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location); + ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent3D::get_next_path_position); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent3D::set_velocity); ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent3D::get_current_navigation_result); @@ -92,12 +92,12 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent3D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent3D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent3D::is_navigation_finished); - ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent3D::get_final_location); + ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent3D::get_final_position); ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done); ADD_GROUP("Pathfinding", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_location", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_location", "get_target_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_target_desired_distance", "get_target_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset"); @@ -344,17 +344,17 @@ real_t NavigationAgent3D::get_path_max_distance() { return path_max_distance; } -void NavigationAgent3D::set_target_location(Vector3 p_location) { - target_location = p_location; +void NavigationAgent3D::set_target_position(Vector3 p_position) { + target_position = p_position; target_position_submitted = true; _request_repath(); } -Vector3 NavigationAgent3D::get_target_location() const { - return target_location; +Vector3 NavigationAgent3D::get_target_position() const { + return target_position; } -Vector3 NavigationAgent3D::get_next_location() { +Vector3 NavigationAgent3D::get_next_path_position() { update_navigation(); const Vector<Vector3> &navigation_path = navigation_result->get_path(); @@ -368,7 +368,7 @@ Vector3 NavigationAgent3D::get_next_location() { real_t NavigationAgent3D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); - return agent_parent->get_global_transform().origin.distance_to(target_location); + return agent_parent->get_global_transform().origin.distance_to(target_position); } bool NavigationAgent3D::is_target_reached() const { @@ -376,7 +376,7 @@ bool NavigationAgent3D::is_target_reached() const { } bool NavigationAgent3D::is_target_reachable() { - return target_desired_distance >= get_final_location().distance_to(target_location); + return target_desired_distance >= get_final_position().distance_to(target_position); } bool NavigationAgent3D::is_navigation_finished() { @@ -384,7 +384,7 @@ bool NavigationAgent3D::is_navigation_finished() { return navigation_finished; } -Vector3 NavigationAgent3D::get_final_location() { +Vector3 NavigationAgent3D::get_final_position() { update_navigation(); const Vector<Vector3> &navigation_path = navigation_result->get_path(); @@ -467,7 +467,7 @@ void NavigationAgent3D::update_navigation() { if (reload_path) { navigation_query->set_start_position(origin); - navigation_query->set_target_position(target_location); + navigation_query->set_target_position(target_position); navigation_query->set_navigation_layers(navigation_layers); navigation_query->set_metadata_flags(path_metadata_flags); @@ -489,7 +489,7 @@ void NavigationAgent3D::update_navigation() { // Check if we can advance the navigation path if (navigation_finished == false) { - // Advances to the next far away location. + // Advances to the next far away position. const Vector<Vector3> &navigation_path = navigation_result->get_path(); const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); @@ -499,7 +499,7 @@ void NavigationAgent3D::update_navigation() { Dictionary details; const Vector3 waypoint = navigation_path[navigation_path_index]; - details[SNAME("location")] = waypoint; + details[SNAME("position")] = waypoint; int waypoint_type = -1; if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 91be068392..12f83ce6a8 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -62,7 +62,7 @@ class NavigationAgent3D : public Node { real_t path_max_distance = 3.0; - Vector3 target_location; + Vector3 target_position; bool target_position_submitted = false; Ref<NavigationPathQueryParameters3D> navigation_query; Ref<NavigationPathQueryResult3D> navigation_result; @@ -155,10 +155,10 @@ public: void set_path_max_distance(real_t p_pmd); real_t get_path_max_distance(); - void set_target_location(Vector3 p_location); - Vector3 get_target_location() const; + void set_target_position(Vector3 p_position); + Vector3 get_target_position() const; - Vector3 get_next_location(); + Vector3 get_next_path_position(); Ref<NavigationPathQueryResult3D> get_current_navigation_result() const { return navigation_result; @@ -174,7 +174,7 @@ public: bool is_target_reached() const; bool is_target_reachable(); bool is_navigation_finished(); - Vector3 get_final_location(); + Vector3 get_final_position(); void set_velocity(Vector3 p_velocity); void _avoidance_done(Vector3 p_new_velocity); diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index e058ef62d0..f47fcfaf51 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -70,10 +70,10 @@ void NavigationLink3D::_update_debug_mesh() { Vector<Vector3> lines; // Draw line between the points. - lines.push_back(start_location); - lines.push_back(end_location); + lines.push_back(start_position); + lines.push_back(end_position); - // Draw start location search radius + // Draw start position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -84,21 +84,21 @@ void NavigationLink3D::_update_debug_mesh() { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(start_location + Vector3(0, a.x, a.y)); - lines.append(start_location + Vector3(0, b.x, b.y)); + lines.append(start_position + Vector3(0, a.x, a.y)); + lines.append(start_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(start_location + Vector3(a.x, 0, a.y)); - lines.append(start_location + Vector3(b.x, 0, b.y)); + lines.append(start_position + Vector3(a.x, 0, a.y)); + lines.append(start_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(start_location + Vector3(a.x, a.y, 0)); - lines.append(start_location + Vector3(b.x, b.y, 0)); + lines.append(start_position + Vector3(a.x, a.y, 0)); + lines.append(start_position + Vector3(b.x, b.y, 0)); break; } } - // Draw end location search radius + // Draw end position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -109,16 +109,16 @@ void NavigationLink3D::_update_debug_mesh() { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(end_location + Vector3(0, a.x, a.y)); - lines.append(end_location + Vector3(0, b.x, b.y)); + lines.append(end_position + Vector3(0, a.x, a.y)); + lines.append(end_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(end_location + Vector3(a.x, 0, a.y)); - lines.append(end_location + Vector3(b.x, 0, b.y)); + lines.append(end_position + Vector3(a.x, 0, a.y)); + lines.append(end_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(end_location + Vector3(a.x, a.y, 0)); - lines.append(end_location + Vector3(b.x, b.y, 0)); + lines.append(end_position + Vector3(a.x, a.y, 0)); + lines.append(end_position + Vector3(b.x, b.y, 0)); break; } } @@ -157,11 +157,11 @@ void NavigationLink3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink3D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink3D::get_navigation_layer_value); - ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink3D::set_start_location); - ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink3D::get_start_location); + ClassDB::bind_method(D_METHOD("set_start_position", "position"), &NavigationLink3D::set_start_position); + ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationLink3D::get_start_position); - ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink3D::set_end_location); - ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink3D::get_end_location); + ClassDB::bind_method(D_METHOD("set_end_position", "position"), &NavigationLink3D::set_end_position); + ClassDB::bind_method(D_METHOD("get_end_position"), &NavigationLink3D::get_end_position); ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink3D::set_enter_cost); ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink3D::get_enter_cost); @@ -172,12 +172,38 @@ void NavigationLink3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_location"), "set_start_location", "get_start_location"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_position"), "set_start_position", "get_start_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_position"), "set_end_position", "get_end_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); } +#ifndef DISABLE_DEPRECATED +bool NavigationLink3D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "start_location") { + set_start_position(p_value); + return true; + } + if (p_name == "end_location") { + set_end_position(p_value); + return true; + } + return false; +} + +bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "start_location") { + r_ret = get_start_position(); + return true; + } + if (p_name == "end_location") { + r_ret = get_end_position(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void NavigationLink3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -186,8 +212,8 @@ void NavigationLink3D::_notification(int p_what) { // Update global positions for the link. Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } #ifdef DEBUG_ENABLED @@ -197,8 +223,8 @@ void NavigationLink3D::_notification(int p_what) { case NOTIFICATION_TRANSFORM_CHANGED: { // Update global positions for the link. Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); #ifdef DEBUG_ENABLED if (is_inside_tree() && debug_instance.is_valid()) { @@ -316,19 +342,19 @@ bool NavigationLink3D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } -void NavigationLink3D::set_start_location(Vector3 p_location) { - if (start_location.is_equal_approx(p_location)) { +void NavigationLink3D::set_start_position(Vector3 p_position) { + if (start_position.is_equal_approx(p_position)) { return; } - start_location = p_location; + start_position = p_position; if (!is_inside_tree()) { return; } Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); #ifdef DEBUG_ENABLED _update_debug_mesh(); @@ -338,19 +364,19 @@ void NavigationLink3D::set_start_location(Vector3 p_location) { update_configuration_warnings(); } -void NavigationLink3D::set_end_location(Vector3 p_location) { - if (end_location.is_equal_approx(p_location)) { +void NavigationLink3D::set_end_position(Vector3 p_position) { + if (end_position.is_equal_approx(p_position)) { return; } - end_location = p_location; + end_position = p_position; if (!is_inside_tree()) { return; } Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); #ifdef DEBUG_ENABLED _update_debug_mesh(); @@ -385,8 +411,8 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { PackedStringArray NavigationLink3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (start_location.is_equal_approx(end_location)) { - warnings.push_back(RTR("NavigationLink3D start location should be different than the end location to be useful.")); + if (start_position.is_equal_approx(end_position)) { + warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful.")); } return warnings; diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 175c5cdd5d..5c9ec36189 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -40,8 +40,8 @@ class NavigationLink3D : public Node3D { RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector3 end_location; - Vector3 start_location; + Vector3 end_position; + Vector3 start_position; real_t enter_cost = 0.0; real_t travel_cost = 1.0; @@ -56,6 +56,11 @@ protected: static void _bind_methods(); void _notification(int p_what); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: NavigationLink3D(); ~NavigationLink3D(); @@ -72,11 +77,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; - void set_start_location(Vector3 p_location); - Vector3 get_start_location() const { return start_location; } + void set_start_position(Vector3 p_position); + Vector3 get_start_position() const { return start_position; } - void set_end_location(Vector3 p_location); - Vector3 get_end_location() const { return end_location; } + void set_end_position(Vector3 p_position); + Vector3 get_end_position() const { return end_position; } void set_enter_cost(real_t p_enter_cost); real_t get_enter_cost() const { return enter_cost; } diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 4325152a7b..41dc27352f 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -502,7 +502,7 @@ void VoxelGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); BIND_ENUM_CONSTANT(SUBDIV_128); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 02f1e9f9a6..7fb831b3b2 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -228,6 +228,22 @@ float AnimationNodeStateMachinePlayback::get_current_length() const { return len_current; } +float AnimationNodeStateMachinePlayback::get_fade_from_play_pos() const { + return pos_fade_from; +} + +float AnimationNodeStateMachinePlayback::get_fade_from_length() const { + return len_fade_from; +} + +float AnimationNodeStateMachinePlayback::get_fading_time() const { + return fading_time; +} + +float AnimationNodeStateMachinePlayback::get_fading_pos() const { + return fading_pos; +} + bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel) { ERR_FAIL_COND_V(!playing, false); ERR_FAIL_COND_V(!p_state_machine->states.has(p_travel), false); @@ -466,7 +482,17 @@ double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_ if (fading_from != StringName()) { double fade_blend_inv = 1.0 - fade_blend; - p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + float fading_from_rem = 0.0; + fading_from_rem = p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + //guess playback position + if (fading_from_rem > len_fade_from) { // weird but ok + len_fade_from = fading_from_rem; + } + + { //advance and loop check + float next_pos = len_fade_from - fading_from_rem; + pos_fade_from = next_pos; //looped + } if (fade_blend >= 1.0) { fading_from = StringName(); } @@ -633,6 +659,8 @@ double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_ } current = next; + pos_fade_from = pos_current; + len_fade_from = len_current; if (reset_request) { len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. @@ -716,7 +744,7 @@ AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { /////////////////////////////////////////////////////// void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); List<StringName> advance_conditions; for (int i = 0; i < transitions.size(); i++) { StringName ac = transitions[i].transition->get_advance_condition_name(); diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 1b4e010a06..cf4d850aa6 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -118,6 +118,9 @@ class AnimationNodeStateMachinePlayback : public Resource { StringName next; }; + double len_fade_from = 0.0; + double pos_fade_from = 0.0; + double len_current = 0.0; double pos_current = 0.0; bool end_loop = false; @@ -164,6 +167,12 @@ public: float get_current_play_pos() const; float get_current_length() const; + float get_fade_from_play_pos() const; + float get_fade_from_length() const; + + float get_fading_time() const; + float get_fading_pos() const; + AnimationNodeStateMachinePlayback(); }; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 047997ca09..77d82990d1 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -451,6 +451,15 @@ static void _call_object(Object *p_object, const StringName &p_method, const Vec } } +Variant AnimationPlayer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + Variant res; + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) { + return res; + } + + return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx); +} + Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { switch (p_anim->track_get_type(p_track)) { #ifndef _3D_DISABLED @@ -473,7 +482,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); Animation *a = p_anim->animation.operator->(); +#ifdef TOOLS_ENABLED bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); +#endif // TOOLS_ENABLED bool backward = signbit(p_delta); for (int i = 0; i < a->get_track_count(); i++) { @@ -512,7 +523,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - loc = _post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx); + loc = post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -540,7 +551,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - rot = _post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx); + rot = post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -568,7 +579,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - scale = _post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx); + scale = post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -596,7 +607,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - blend = _post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx); + blend = post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -649,7 +660,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (p_time < first_key_time) { double c = Math::ease(p_time / first_key_time, transition); Variant first_value = a->track_get_key_value(i, first_key); - first_value = _post_process_key_value(a, i, first_value, nc->node); + first_value = post_process_key_value(a, i, first_value, nc->node); Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); @@ -670,7 +681,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (value == Variant()) { continue; } - value = _post_process_key_value(a, i, value, nc->node); + value = post_process_key_value(a, i, value, nc->node); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); @@ -701,7 +712,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double for (int &F : indices) { Variant value = a->track_get_key_value(i, F); - value = _post_process_key_value(a, i, value, nc->node); + value = post_process_key_value(a, i, value, nc->node); switch (pa->special) { case SP_NONE: { bool valid; @@ -745,11 +756,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_METHOD: { - if (!nc->node || is_stopping) { +#ifdef TOOLS_ENABLED + if (!can_call) { continue; } - if (!p_is_current) { - break; +#endif // TOOLS_ENABLED + if (!p_is_current || !nc->node || is_stopping) { + continue; } List<int> indices; @@ -772,16 +785,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double for (int &E : indices) { StringName method = a->method_track_get_name(i, E); Vector<Variant> params = a->method_track_get_params(i, E); - #ifdef DEBUG_ENABLED if (!nc->node->has_method(method)) { ERR_PRINT("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'."); } #endif - - if (can_call) { - _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED); - } + _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED); } } break; @@ -796,7 +805,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double TrackNodeCache::BezierAnim *ba = &E->value; real_t bezier = a->bezier_track_interpolate(i, p_time); - bezier = _post_process_key_value(a, i, bezier, nc->node); + bezier = post_process_key_value(a, i, bezier, nc->node); if (ba->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX); cache_update_bezier[cache_update_bezier_size++] = ba; @@ -813,7 +822,11 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } if (p_seeked) { - //find whatever should be playing +#ifdef TOOLS_ENABLED + if (!can_call) { + continue; // To avoid spamming the preview in editor. + } +#endif // TOOLS_ENABLED int idx = a->track_find_key(i, p_time); if (idx < 0) { continue; @@ -2190,6 +2203,8 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false)); ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); + GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation"); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 7e7d12f982..2901c43dcf 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -317,6 +317,8 @@ protected: static void _bind_methods(); + GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int); + Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); public: diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 077a5696bb..a2a1a0aaef 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1015,8 +1015,9 @@ void AnimationTree::_process_graph(double p_delta) { // Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks. { +#ifdef TOOLS_ENABLED bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); - +#endif // TOOLS_ENABLED for (const AnimationNode::AnimationState &as : state.animation_states) { Ref<Animation> a = as.animation; double time = as.time; @@ -1104,9 +1105,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, (double)a->get_length(), &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = 0; } @@ -1116,9 +1117,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, 0, &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = (double)a->get_length(); } @@ -1128,10 +1129,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, time, &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1142,7 +1143,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc = _post_process_key_value(a, i, loc, t->object, t->bone_idx); + loc = post_process_key_value(a, i, loc, t->object, t->bone_idx); t->loc += (loc - t->init_loc) * blend; } @@ -1195,9 +1196,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); - rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } @@ -1207,7 +1208,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, 0, &rot[1]); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = (double)a->get_length(); @@ -1218,10 +1219,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, time, &rot[1]); - rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = !backward ? 0 : (double)a->get_length(); @@ -1232,7 +1233,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot = _post_process_key_value(a, i, rot, t->object, t->bone_idx); + rot = post_process_key_value(a, i, rot, t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized(); } @@ -1285,10 +1286,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]); t->scale += (scale[1] - scale[0]) * blend; - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); prev_time = 0; } } else { @@ -1297,9 +1298,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, 0, &scale[1]); - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = (double)a->get_length(); } @@ -1309,10 +1310,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, time, &scale[1]); - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1323,7 +1324,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale = _post_process_key_value(a, i, scale, t->object, t->bone_idx); + scale = post_process_key_value(a, i, scale, t->object, t->bone_idx); t->scale += (scale - t->init_scale) * blend; } @@ -1341,7 +1342,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - value = _post_process_key_value(a, i, value, t->object, t->shape_index); + value = post_process_key_value(a, i, value, t->object, t->shape_index); t->value += (value - t->init_value) * blend; #endif // _3D_DISABLED @@ -1353,7 +1354,7 @@ void AnimationTree::_process_graph(double p_delta) { if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, time); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); if (value == Variant()) { continue; @@ -1393,14 +1394,14 @@ void AnimationTree::_process_graph(double p_delta) { continue; } Variant value = a->track_get_key_value(i, idx); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { Variant value = a->track_get_key_value(i, F); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } } @@ -1408,6 +1409,11 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_METHOD: { +#ifdef TOOLS_ENABLED + if (!can_call) { + return; + } +#endif // TOOLS_ENABLED TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); if (seeked) { @@ -1417,18 +1423,14 @@ void AnimationTree::_process_graph(double p_delta) { } StringName method = a->method_track_get_name(i, idx); Vector<Variant> params = a->method_track_get_params(i, idx); - if (can_call) { - _call_object(t->object, method, params, false); - } + _call_object(t->object, method, params, false); } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { StringName method = a->method_track_get_name(i, F); Vector<Variant> params = a->method_track_get_params(i, F); - if (can_call) { - _call_object(t->object, method, params, true); - } + _call_object(t->object, method, params, true); } } } break; @@ -1436,7 +1438,7 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); real_t bezier = a->bezier_track_interpolate(i, time); - bezier = _post_process_key_value(a, i, bezier, t->object); + bezier = post_process_key_value(a, i, bezier, t->object); t->value += (bezier - t->init_value) * blend; } break; @@ -1444,7 +1446,6 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); if (seeked) { - //find whatever should be playing int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); if (idx < 0) { continue; @@ -1700,6 +1701,15 @@ void AnimationTree::_process_graph(double p_delta) { } } +Variant AnimationTree::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + Variant res; + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) { + return res; + } + + return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx); +} + Variant AnimationTree::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { switch (p_anim->track_get_type(p_track)) { #ifndef _3D_DISABLED @@ -2034,6 +2044,8 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTree::advance); + GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 52d3e1bd41..ab538feb58 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -328,6 +328,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int); + Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); public: diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 472299b135..c26a00221a 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -479,14 +479,16 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "button_pressed"), "set_pressed", "is_pressed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + + ADD_GROUP("Shortcut", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_feedback"), "set_shortcut_feedback", "is_shortcut_feedback"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 1e07a53642..2a8b1cd8c4 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -575,9 +575,13 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Text Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Icon Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fead0878fb..f7c056316d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1387,13 +1387,7 @@ Point2 Control::get_global_position() const { Point2 Control::get_screen_position() const { ERR_FAIL_COND_V(!is_inside_tree(), Point2()); - Point2 global_pos = get_global_transform_with_canvas().get_origin(); - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - global_pos += w->get_position(); - } - - return global_pos; + return get_screen_transform().get_origin(); } void Control::_set_size(const Size2 &p_size) { @@ -1444,24 +1438,20 @@ void Control::set_rect(const Rect2 &p_rect) { } Rect2 Control::get_rect() const { - return Rect2(get_position(), get_size()); + Transform2D xform = get_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_global_rect() const { - return Rect2(get_global_position(), get_size()); + Transform2D xform = get_global_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_screen_rect() const { ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - Rect2 r(get_global_position(), get_size()); - - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - r.position += w->get_position(); - } - - return r; + Transform2D xform = get_screen_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_anchorable_rect() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 6c495ab2c9..af52f6664a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1380,34 +1380,15 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { accept_event(); } } - - Ref<InputEventMagnifyGesture> magnify_gesture = p_ev; - if (magnify_gesture.is_valid()) { - set_zoom_custom(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - } - - Ref<InputEventPanGesture> pan_gesture = p_ev; - if (pan_gesture.is_valid()) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); - } -} - -void GraphEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_scroll_vec.x != 0) { - h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x)); - } else { - v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y)); - } } -void GraphEdit::_pan_callback(Vector2 p_scroll_vec) { +void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x); v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y); } -void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin); +void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + set_zoom_custom(zoom * p_zoom_factor, p_origin); } void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) { @@ -1502,6 +1483,7 @@ void GraphEdit::set_zoom_step(float p_zoom_step) { } zoom_step = p_zoom_step; + panner->set_scroll_zoom_factor(zoom_step); } float GraphEdit::get_zoom_step() const { @@ -2421,7 +2403,7 @@ GraphEdit::GraphEdit() { zoom_max = (1 * Math::pow(zoom_step, 4)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); top_layer = memnew(GraphEditFilter(this)); add_child(top_layer, false, INTERNAL_MODE_BACK); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 030f40e370..dfe6b94906 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -129,9 +129,8 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool arrange_nodes_button_hidden = false; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e2a5b0c8a3..5aa777b40c 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1724,6 +1724,10 @@ void LineEdit::insert_text_at_caret(String p_text) { input_direction = (TextDirection)dir; } set_caret_column(caret_column + p_text.length()); + + if (!ime_text.is_empty()) { + _shape(); + } } void LineEdit::clear_internal() { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a7e50a765e..9baf7c9180 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2778,7 +2778,7 @@ bool RichTextLabel::_validate_line_caches() { main->first_resized_line.store(main->lines.size()); - if (fit_content_height) { + if (fit_content) { update_minimum_size(); } return true; @@ -2862,7 +2862,7 @@ void RichTextLabel::_process_line_caches() { main->first_resized_line.store(main->lines.size()); main->first_invalid_font_line.store(main->lines.size()); - if (fit_content_height) { + if (fit_content) { update_minimum_size(); } emit_signal(SNAME("finished")); @@ -2963,7 +2963,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) _invalidate_current_line(current_frame); - if (fixed_width != -1) { + if (fit_content) { update_minimum_size(); } queue_redraw(); @@ -3435,6 +3435,8 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar item->custom_effect = p_custom_effect; item->char_fx_transform->environment = p_environment; _add_item(item, true); + + set_process_internal(true); } void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { @@ -3551,7 +3553,7 @@ void RichTextLabel::clear() { scroll_following = true; } - if (fixed_width != -1) { + if (fit_content) { update_minimum_size(); } } @@ -3572,15 +3574,17 @@ int RichTextLabel::get_tab_size() const { return tab_size; } -void RichTextLabel::set_fit_content_height(bool p_enabled) { - if (p_enabled != fit_content_height) { - fit_content_height = p_enabled; - update_minimum_size(); +void RichTextLabel::set_fit_content(bool p_enabled) { + if (p_enabled == fit_content) { + return; } + + fit_content = p_enabled; + update_minimum_size(); } -bool RichTextLabel::is_fit_content_height_enabled() const { - return fit_content_height; +bool RichTextLabel::is_fit_content_enabled() const { + return fit_content; } void RichTextLabel::set_meta_underline(bool p_underline) { @@ -4550,7 +4554,6 @@ void RichTextLabel::append_text(const String &p_bbcode) { push_customfx(effect, properties); pos = brk_end + 1; tag_stack.push_front(identifier); - set_process_internal(true); } else { add_text("["); //ignore pos = brk_pos + 1; @@ -5344,6 +5347,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor); ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor); + ClassDB::bind_method(D_METHOD("push_customfx", "effect", "env"), &RichTextLabel::push_customfx); ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop); ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear); @@ -5381,8 +5385,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size); ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size); - ClassDB::bind_method(D_METHOD("set_fit_content_height", "enabled"), &RichTextLabel::set_fit_content_height); - ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &RichTextLabel::is_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("set_fit_content", "enabled"), &RichTextLabel::set_fit_content); + ClassDB::bind_method(D_METHOD("is_fit_content_enabled"), &RichTextLabel::is_fit_content_enabled); ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled); ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled); @@ -5457,7 +5461,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content"), "set_fit_content", "is_fit_content_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); @@ -5644,27 +5648,17 @@ int RichTextLabel::get_total_glyph_count() const { return tg; } -void RichTextLabel::set_fixed_size_to_width(int p_width) { - if (fixed_width == p_width) { - return; - } - - fixed_width = p_width; - update_minimum_size(); -} - Size2 RichTextLabel::get_minimum_size() const { - Size2 size = theme_cache.normal_style->get_minimum_size(); - - if (fixed_width != -1) { - size.x += fixed_width; - } + Size2 sb_min_size = theme_cache.normal_style->get_minimum_size(); + Size2 min_size; - if (fit_content_height) { - size.y += get_content_height(); + if (fit_content) { + min_size.x = get_content_width(); + min_size.y = get_content_height(); } - return size; + return sb_min_size + + ((autowrap_mode != TextServer::AUTOWRAP_OFF) ? Size2(1, min_size.height) : min_size); } // Context menu. diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 8ac77d5b47..58b82d4672 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -524,9 +524,7 @@ private: bool use_bbcode = false; String text; - int fixed_width = -1; - - bool fit_content_height = false; + bool fit_content = false; struct ThemeCache { Ref<StyleBox> normal_style; @@ -640,8 +638,8 @@ public: void set_shortcut_keys_enabled(bool p_enabled); bool is_shortcut_keys_enabled() const; - void set_fit_content_height(bool p_enabled); - bool is_fit_content_height_enabled() const; + void set_fit_content(bool p_enabled); + bool is_fit_content_enabled() const; bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false); @@ -731,7 +729,6 @@ public: void install_effect(const Variant effect); - void set_fixed_size_to_width(int p_width); virtual Size2 get_minimum_size() const override; RichTextLabel(const String &p_text = String()); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 56bd5c872a..723f3fe7af 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -3544,6 +3544,19 @@ void TextEdit::insert_text_at_caret(const String &p_text, int p_caret) { adjust_carets_after_edit(i, new_line, new_column, from_line, from_col); } + + if (!ime_text.is_empty()) { + for (int i = 0; i < carets.size(); i++) { + String t; + if (get_caret_column(i) >= 0) { + t = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length()); + } else { + t = ime_text; + } + text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, t, structured_text_parser(st_parser, st_args, t)); + } + } + end_complex_operation(); queue_redraw(); } @@ -6374,7 +6387,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); ADD_GROUP("Scroll", "scroll_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled"); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 2d985c2324..d9e6157489 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3554,7 +3554,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { icon_size_x = icon_region.size.width; } } - // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_double_clicked signal. + // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_icon_double_clicked signal. if (rtl) { mpos.x = get_size().width - (mpos.x + icon_size_x); } else { @@ -3562,10 +3562,10 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } if (rect.has_point(mpos)) { if (!edit_selected()) { - emit_signal(SNAME("item_double_clicked")); + emit_signal(SNAME("item_icon_double_clicked")); } } else { - emit_signal(SNAME("item_double_clicked")); + emit_signal(SNAME("item_icon_double_clicked")); } } pressing_for_editor = false; @@ -5257,7 +5257,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_edited")); ADD_SIGNAL(MethodInfo("custom_item_clicked", PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("item_custom_button_pressed")); - ADD_SIGNAL(MethodInfo("item_double_clicked")); + ADD_SIGNAL(MethodInfo("item_icon_double_clicked")); ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"))); ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"))); ADD_SIGNAL(MethodInfo("button_clicked", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "mouse_button_index"))); diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index e8d54e6937..145497fa61 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -43,36 +43,42 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (scroll_vec != Vector2() && mb->is_pressed()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed())); + // Compute the zoom factor. + float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); + zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; + float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); return true; } else { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } } else { if (mb->is_ctrl_pressed()) { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } else if (!mb->is_shift_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed())); + // Compute the zoom factor. + float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); + zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; + float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); return true; } } @@ -102,14 +108,31 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (mm.is_valid()) { if (is_dragging) { if (p_canvas_rect != Rect2()) { - callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect))); + callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event)); } else { - callback_helper(pan_callback, varray(mm->get_relative())); + callback_helper(pan_callback, varray(mm->get_relative(), p_event)); } return true; } } + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + // Zoom gesture + callback_helper(zoom_callback, varray(magnify_gesture->get_factor(), magnify_gesture->get_position(), p_event)); + return true; + } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + callback_helper(pan_callback, varray(-pan_gesture->get_delta(), p_event)); + } + + Ref<InputEventScreenDrag> screen_drag = p_event; + if (screen_drag.is_valid()) { + callback_helper(pan_callback, varray(screen_drag->get_relative(), p_event)); + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) { @@ -140,8 +163,7 @@ void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) { p_callback.callp(argptr, p_args.size(), result, ce); } -void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) { - scroll_callback = p_scroll_callback; +void ViewPanner::set_callbacks(Callable p_pan_callback, Callable p_zoom_callback) { pan_callback = p_pan_callback; zoom_callback = p_zoom_callback; } @@ -163,6 +185,20 @@ void ViewPanner::set_simple_panning_enabled(bool p_enabled) { simple_panning_enabled = p_enabled; } +void ViewPanner::set_scroll_speed(int p_scroll_speed) { + ERR_FAIL_COND(p_scroll_speed <= 0); + scroll_speed = p_scroll_speed; +} + +void ViewPanner::set_scroll_zoom_factor(float p_scroll_zoom_factor) { + ERR_FAIL_COND(p_scroll_zoom_factor <= 1.0); + scroll_zoom_factor = p_scroll_zoom_factor; +} + +void ViewPanner::set_pan_axis(PanAxis p_pan_axis) { + pan_axis = p_pan_axis; +} + void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) { set_control_scheme(p_scheme); set_pan_shortcut(p_shortcut); diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h index 861574a80c..60d36ca04c 100644 --- a/scene/gui/view_panner.h +++ b/scene/gui/view_panner.h @@ -45,7 +45,17 @@ public: SCROLL_PANS, }; + enum PanAxis { + PAN_AXIS_BOTH, + PAN_AXIS_HORIZONTAL, + PAN_AXIS_VERTICAL, + }; + private: + int scroll_speed = 32; + float scroll_zoom_factor = 1.1; + PanAxis pan_axis = PAN_AXIS_BOTH; + bool is_dragging = false; bool pan_key_pressed = false; bool force_drag = false; @@ -55,7 +65,6 @@ private: Ref<Shortcut> pan_view_shortcut; - Callable scroll_callback; Callable pan_callback; Callable zoom_callback; @@ -63,11 +72,14 @@ private: ControlScheme control_scheme = SCROLL_ZOOMS; public: - void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback); + void set_callbacks(Callable p_pan_callback, Callable p_zoom_callback); void set_control_scheme(ControlScheme p_scheme); void set_enable_rmb(bool p_enable); void set_pan_shortcut(Ref<Shortcut> p_shortcut); void set_simple_panning_enabled(bool p_enabled); + void set_scroll_speed(int p_scroll_speed); + void set_scroll_zoom_factor(float p_scroll_zoom_factor); + void set_pan_axis(PanAxis p_pan_axis); void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 7b0554442c..3323c0f848 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -154,17 +154,7 @@ Transform2D CanvasItem::get_global_transform_with_canvas() const { Transform2D CanvasItem::get_screen_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); - Transform2D xform = get_global_transform_with_canvas(); - - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - Transform2D s; - s.set_origin(w->get_position()); - - xform = s * xform; - } - - return xform; + return get_viewport()->get_popup_base_transform() * get_global_transform_with_canvas(); } Transform2D CanvasItem::get_global_transform() const { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0b1f8f101c..de486094fe 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -946,7 +946,7 @@ String Node::validate_child_name(Node *p_child) { #endif String Node::adjust_name_casing(const String &p_name) { - switch (GLOBAL_GET("editor/node_naming/name_casing").operator int()) { + switch (GLOBAL_GET("editor/naming/node_name_casing").operator int()) { case NAME_CASING_PASCAL_CASE: return p_name.to_pascal_case(); case NAME_CASING_CAMEL_CASE: @@ -2213,7 +2213,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c Variant value = N->get()->get(name).duplicate(true); - if (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) { + if (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE) { Resource *res = Object::cast_to<Resource>(value); if (res) { // Duplicate only if it's a resource current_node->set(name, res->duplicate()); @@ -2799,8 +2799,8 @@ void Node::unhandled_key_input(const Ref<InputEvent> &p_key_event) { } void Node::_bind_methods() { - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/node_naming/name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"), 0); - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/node_naming/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"), NAME_CASING_PASCAL_CASE); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"), 0); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"), NAME_CASING_PASCAL_CASE); ClassDB::bind_static_method("Node", D_METHOD("print_orphan_nodes"), &Node::print_orphan_nodes); ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); @@ -3022,7 +3022,7 @@ void Node::_bind_methods() { } String Node::_get_name_num_separator() { - switch (GLOBAL_GET("editor/node_naming/name_num_separator").operator int()) { + switch (GLOBAL_GET("editor/naming/node_name_num_separator").operator int()) { case 0: return ""; case 1: diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 07bcf45899..48cff5aa8e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -704,25 +704,15 @@ void Viewport::_process_picking() { } #ifndef _3D_DISABLED - bool captured = false; - + CollisionObject3D *capture_object = nullptr; if (physics_object_capture.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture)); - if (co && camera_3d) { - _collision_object_3d_input_event(co, camera_3d, ev, Vector3(), Vector3(), 0); - captured = true; - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { - physics_object_capture = ObjectID(); - } - - } else { + capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture)); + if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) { physics_object_capture = ObjectID(); } } - if (captured) { - // None. - } else if (pos == last_pos) { + if (pos == last_pos) { if (last_id.is_valid()) { if (ObjectDB::get_instance(last_id) && last_object) { // Good, exists. @@ -748,13 +738,12 @@ void Viewport::_process_picking() { bool col = space->intersect_ray(ray_params, result); ObjectID new_collider; - if (col) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider); - if (co && co->can_process()) { - _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape); + CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr; + if (co && co->can_process()) { + new_collider = result.collider_id; + if (!capture_object) { last_object = co; last_id = result.collider_id; - new_collider = last_id; if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { physics_object_capture = last_id; } @@ -763,21 +752,24 @@ void Viewport::_process_picking() { if (is_mouse && new_collider != physics_object_over) { if (physics_object_over.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); - if (co) { - co->_mouse_exit(); + CollisionObject3D *previous_co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); + if (previous_co) { + previous_co->_mouse_exit(); } } if (new_collider.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(new_collider)); - if (co) { - co->_mouse_enter(); - } + DEV_ASSERT(co); + co->_mouse_enter(); } physics_object_over = new_collider; } + if (capture_object) { + _collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape); + } else if (new_collider.is_valid()) { + _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape); + } } last_pos = pos; @@ -1050,7 +1042,7 @@ Camera2D *Viewport::get_camera_2d() const { } Transform2D Viewport::get_final_transform() const { - return stretch_transform * global_canvas_transform; + return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform; } void Viewport::_update_canvas_items(Node *p_node) { @@ -1133,7 +1125,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { return ev; // No transformation defined for null event } - Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); + Transform2D ai = get_final_transform().affine_inverse(); return ev->xformed_by(ai); } @@ -1795,9 +1787,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (w->is_embedded()) { embedder = w->_get_embedder(); - Transform2D ai = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse(); - - viewport_pos = ai.xform(mpos) + w->get_position(); // To parent coords. + viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords. } } } @@ -1847,7 +1837,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (viewport_under) { if (viewport_under != this) { - Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform()); + Transform2D ai = viewport_under->get_final_transform().affine_inverse(); viewport_pos = ai.xform(viewport_pos); } // Find control under at position. @@ -2853,7 +2843,7 @@ bool Viewport::get_physics_object_picking() { } Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const { - Transform2D xf = get_final_transform(); + Transform2D xf = stretch_transform * global_canvas_transform; return xf.xform(p_viewport_coords); } @@ -3245,7 +3235,7 @@ Viewport::SDFScale Viewport::get_sdf_scale() const { } Transform2D Viewport::get_screen_transform() const { - return _get_input_pre_xform().affine_inverse() * get_final_transform(); + return get_final_transform(); } void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) { @@ -4188,6 +4178,21 @@ Transform2D SubViewport::get_screen_transform() const { return container_transform * Viewport::get_screen_transform(); } +Transform2D SubViewport::get_popup_base_transform() const { + if (is_embedding_subwindows()) { + return Transform2D(); + } + SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); + if (!c) { + return Viewport::get_screen_transform(); + } + Transform2D container_transform; + if (c->is_stretch_enabled()) { + container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink())); + } + return c->get_screen_transform() * container_transform * Viewport::get_screen_transform(); +} + void SubViewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 603e92b071..d5d5201e9a 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -649,6 +649,7 @@ public: bool get_canvas_cull_mask_bit(uint32_t p_layer) const; virtual Transform2D get_screen_transform() const; + virtual Transform2D get_popup_base_transform() const { return Transform2D(); } #ifndef _3D_DISABLED bool use_xr = false; @@ -775,6 +776,7 @@ public: ClearMode get_clear_mode() const; virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_popup_base_transform() const override; SubViewport(); ~SubViewport(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 2c6599d849..3ceb27b3e6 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -2104,6 +2104,19 @@ Transform2D Window::get_screen_transform() const { return embedder_transform * Viewport::get_screen_transform(); } +Transform2D Window::get_popup_base_transform() const { + if (is_embedding_subwindows()) { + return Transform2D(); + } + Transform2D window_transform; + window_transform.set_origin(get_position()); + window_transform *= Viewport::get_screen_transform(); + if (_get_embedder()) { + return _get_embedder()->get_popup_base_transform() * window_transform; + } + return window_transform; +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); diff --git a/scene/main/window.h b/scene/main/window.h index e9c217f973..18841a4f1a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -373,6 +373,7 @@ public: // virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_popup_base_transform() const override; Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index deee187e8e..e5a1adff20 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -2868,6 +2868,12 @@ void SystemFont::_bind_methods() { ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &SystemFont::set_multichannel_signed_distance_field); ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &SystemFont::is_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &SystemFont::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &SystemFont::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &SystemFont::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &SystemFont::get_msdf_size); + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling); ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling); @@ -2890,6 +2896,8 @@ void SystemFont::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range"), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size"), "set_msdf_size", "get_msdf_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } @@ -2987,6 +2995,8 @@ void SystemFont::_update_base_font() { file->set_hinting(hinting); file->set_subpixel_positioning(subpixel_positioning); file->set_multichannel_signed_distance_field(msdf); + file->set_msdf_pixel_range(msdf_pixel_range); + file->set_msdf_size(msdf_size); file->set_oversampling(oversampling); base_font = file; @@ -3186,6 +3196,34 @@ bool SystemFont::is_multichannel_signed_distance_field() const { return msdf; } +void SystemFont::set_msdf_pixel_range(int p_msdf_pixel_range) { + if (msdf_pixel_range != p_msdf_pixel_range) { + msdf_pixel_range = p_msdf_pixel_range; + if (base_font.is_valid()) { + base_font->set_msdf_pixel_range(msdf_pixel_range); + } + emit_changed(); + } +} + +int SystemFont::get_msdf_pixel_range() const { + return msdf_pixel_range; +} + +void SystemFont::set_msdf_size(int p_msdf_size) { + if (msdf_size != p_msdf_size) { + msdf_size = p_msdf_size; + if (base_font.is_valid()) { + base_font->set_msdf_size(msdf_size); + } + emit_changed(); + } +} + +int SystemFont::get_msdf_size() const { + return msdf_size; +} + void SystemFont::set_oversampling(real_t p_oversampling) { if (oversampling != p_oversampling) { oversampling = p_oversampling; diff --git a/scene/resources/font.h b/scene/resources/font.h index 4d468a8841..ef79f8bd02 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -452,6 +452,8 @@ class SystemFont : public Font { TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; bool msdf = false; + int msdf_pixel_range = 16; + int msdf_size = 48; protected: static void _bind_methods(); @@ -488,6 +490,12 @@ public: virtual void set_multichannel_signed_distance_field(bool p_msdf); virtual bool is_multichannel_signed_distance_field() const; + virtual void set_msdf_pixel_range(int p_msdf_pixel_range); + virtual int get_msdf_pixel_range() const; + + virtual void set_msdf_size(int p_msdf_size); + virtual int get_msdf_size() const; + virtual void set_font_names(const PackedStringArray &p_names); virtual PackedStringArray get_font_names() const; diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp index 121108965b..34d31bac8a 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.cpp +++ b/scene/resources/skeleton_modification_2d_stackholder.cpp @@ -64,7 +64,7 @@ bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant & } void SkeletonModification2DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 4fa287e7b6..71ddbc0898 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -37,7 +37,7 @@ void SkeletonModificationStack2D::_get_property_list(List<PropertyInfo> *p_list) PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModification2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_ALWAYS_DUPLICATE)); } } diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 4f16986392..0beb62185d 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -231,22 +231,6 @@ bool StyleBoxTexture::is_draw_center_enabled() const { return draw_center; } -Size2 StyleBoxTexture::get_minimum_size() const { - Size2 min_size = StyleBox::get_minimum_size(); - - // Make sure that the min size is no smaller than the used texture region. - if (texture.is_valid()) { - if (min_size.x < region_rect.size.x) { - min_size.x = region_rect.size.x; - } - if (min_size.y < region_rect.size.y) { - min_size.y = region_rect.size.y; - } - } - - return min_size; -} - void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) { ERR_FAIL_INDEX((int)p_side, 4); expand_margin[p_side] = p_size; diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 91033617ab..17acfd773e 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -107,8 +107,6 @@ protected: static void _bind_methods(); public: - virtual Size2 get_minimum_size() const override; - void set_expand_margin(Side p_expand_side, float p_size); void set_expand_margin_all(float p_expand_margin_size); void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 17e92ddfca..5a2b917b9a 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -146,6 +146,25 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) { return h; } +bool SurfaceTool::SmoothGroupVertex::operator==(const SmoothGroupVertex &p_vertex) const { + if (vertex != p_vertex.vertex) { + return false; + } + + if (smooth_group != p_vertex.smooth_group) { + return false; + } + + return true; +} + +uint32_t SurfaceTool::SmoothGroupVertexHasher::hash(const SmoothGroupVertex &p_vtx) { + uint32_t h = hash_djb2_buffer((const uint8_t *)&p_vtx.vertex, sizeof(real_t) * 3); + h = hash_murmur3_one_32(p_vtx.smooth_group, h); + h = hash_fmix32(h); + return h; +} + uint32_t SurfaceTool::TriangleHasher::hash(const int *p_triangle) { int t0 = p_triangle[0]; int t1 = p_triangle[1]; @@ -1152,7 +1171,7 @@ void SurfaceTool::generate_normals(bool p_flip) { ERR_FAIL_COND((vertex_array.size() % 3) != 0); - HashMap<Vertex, Vector3, VertexHasher> vertex_hash; + HashMap<SmoothGroupVertex, Vector3, SmoothGroupVertexHasher> smooth_hash; for (uint32_t vi = 0; vi < vertex_array.size(); vi += 3) { Vertex *v = &vertex_array[vi]; @@ -1165,21 +1184,28 @@ void SurfaceTool::generate_normals(bool p_flip) { } for (int i = 0; i < 3; i++) { - Vector3 *lv = vertex_hash.getptr(v[i]); - if (!lv) { - vertex_hash.insert(v[i], normal); + // Add face normal to smooth vertex influence if vertex is member of a smoothing group + if (v[i].smooth_group != UINT32_MAX) { + Vector3 *lv = smooth_hash.getptr(v[i]); + if (!lv) { + smooth_hash.insert(v[i], normal); + } else { + (*lv) += normal; + } } else { - (*lv) += normal; + v[i].normal = normal; } } } for (Vertex &vertex : vertex_array) { - Vector3 *lv = vertex_hash.getptr(vertex); - if (!lv) { - vertex.normal = Vector3(); - } else { - vertex.normal = lv->normalized(); + if (vertex.smooth_group != UINT32_MAX) { + Vector3 *lv = smooth_hash.getptr(vertex); + if (!lv) { + vertex.normal = Vector3(); + } else { + vertex.normal = lv->normalized(); + } } } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 25e078d2ff..00438c4a53 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -97,6 +97,21 @@ private: static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx); }; + struct SmoothGroupVertex { + Vector3 vertex; + uint32_t smooth_group = 0; + bool operator==(const SmoothGroupVertex &p_vertex) const; + + SmoothGroupVertex(const Vertex &p_vertex) { + vertex = p_vertex.vertex; + smooth_group = p_vertex.smooth_group; + }; + }; + + struct SmoothGroupVertexHasher { + static _FORCE_INLINE_ uint32_t hash(const SmoothGroupVertex &p_vtx); + }; + struct TriangleHasher { static _FORCE_INLINE_ uint32_t hash(const int *p_triangle); static _FORCE_INLINE_ bool compare(const int *p_lhs, const int *p_rhs); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 1cbeaae428..bfcf5cb137 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1561,7 +1561,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { prop_name += "/" + itos(E.key); if (E.key != NODE_ID_OUTPUT) { - p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_ALWAYS_DUPLICATE)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index e78d9b924d..12be0f46a6 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3690,16 +3690,47 @@ String VisualShaderNodeDerivativeFunc::get_output_port_name(int p_port) const { String VisualShaderNodeDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { static const char *functions[FUNC_MAX] = { - "fwidth($)", - "dFdx($)", - "dFdy($)" + "fwidth$($)", + "dFdx$($)", + "dFdy$($)" + }; + + static const char *precisions[PRECISION_MAX] = { + "", + "Coarse", + "Fine" }; String code; - code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n"; + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + code += " " + p_output_vars[0] + " = " + String(functions[func]).replace_first("$", "").replace_first("$", p_input_vars[0]) + ";\n"; + return code; + } + + code += " " + p_output_vars[0] + " = " + String(functions[func]).replace_first("$", String(precisions[precision])).replace_first("$", p_input_vars[0]) + ";\n"; return code; } +String VisualShaderNodeDerivativeFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + if (precision != PRECISION_NONE && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + String precision_str; + switch (precision) { + case PRECISION_COARSE: { + precision_str = "Coarse"; + } break; + case PRECISION_FINE: { + precision_str = "Fine"; + } break; + default: { + } break; + } + + return vformat(RTR("`%s` precision mode is not available for `gl_compatibility` profile.\nReverted to `None` precision."), precision_str); + } + + return String(); +} + void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) { ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX)); if (op_type == p_op_type) { @@ -3742,10 +3773,24 @@ VisualShaderNodeDerivativeFunc::Function VisualShaderNodeDerivativeFunc::get_fun return func; } +void VisualShaderNodeDerivativeFunc::set_precision(Precision p_precision) { + ERR_FAIL_INDEX(int(p_precision), int(PRECISION_MAX)); + if (precision == p_precision) { + return; + } + precision = p_precision; + emit_changed(); +} + +VisualShaderNodeDerivativeFunc::Precision VisualShaderNodeDerivativeFunc::get_precision() const { + return precision; +} + Vector<StringName> VisualShaderNodeDerivativeFunc::get_editable_properties() const { Vector<StringName> props; props.push_back("op_type"); props.push_back("function"); + props.push_back("precision"); return props; } @@ -3756,8 +3801,12 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function); + ClassDB::bind_method(D_METHOD("set_precision", "precision"), &VisualShaderNodeDerivativeFunc::set_precision); + ClassDB::bind_method(D_METHOD("get_precision"), &VisualShaderNodeDerivativeFunc::get_precision); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "precision", PROPERTY_HINT_ENUM, "None,Coarse,Fine"), "set_precision", "get_precision"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); @@ -3769,6 +3818,11 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_X); BIND_ENUM_CONSTANT(FUNC_Y); BIND_ENUM_CONSTANT(FUNC_MAX); + + BIND_ENUM_CONSTANT(PRECISION_NONE); + BIND_ENUM_CONSTANT(PRECISION_COARSE); + BIND_ENUM_CONSTANT(PRECISION_FINE); + BIND_ENUM_CONSTANT(PRECISION_MAX); } VisualShaderNodeDerivativeFunc::VisualShaderNodeDerivativeFunc() { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index e3b101cf84..fa6b134526 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1536,9 +1536,17 @@ public: FUNC_MAX, }; + enum Precision { + PRECISION_NONE, + PRECISION_COARSE, + PRECISION_FINE, + PRECISION_MAX, + }; + protected: OpType op_type = OP_TYPE_SCALAR; Function func = FUNC_SUM; + Precision precision = PRECISION_NONE; protected: static void _bind_methods(); @@ -1555,6 +1563,7 @@ public: virtual String get_output_port_name(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; void set_op_type(OpType p_op_type); OpType get_op_type() const; @@ -1562,6 +1571,9 @@ public: void set_function(Function p_func); Function get_function() const; + void set_precision(Precision p_precision); + Precision get_precision() const; + virtual Vector<StringName> get_editable_properties() const override; VisualShaderNodeDerivativeFunc(); @@ -1569,6 +1581,7 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::OpType) VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Function) +VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Precision) /////////////////////////////////////// /// FACEFORWARD diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 39f14439f4..11bbd9dae2 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -344,7 +344,6 @@ DisplayServer::MouseMode DisplayServer::mouse_get_mode() const { } void DisplayServer::warp_mouse(const Point2i &p_position) { - WARN_PRINT("Mouse warping is not supported by this display server."); } Point2i DisplayServer::mouse_get_position() const { diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 943b2ae467..88178d5c7d 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -285,10 +285,10 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional); ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers); ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers); - ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer2D::link_set_start_location); - ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer2D::link_get_start_location); - ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer2D::link_set_end_location); - ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer2D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer2D::link_set_start_position); + ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer2D::link_get_start_position); + ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer2D::link_set_end_position); + ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer2D::link_get_end_position); ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost); ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost); ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost); @@ -392,10 +392,10 @@ void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_t bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid); void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32); uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid); -void FORWARD_2(link_set_start_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); -Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_location, RID, p_link, rid_to_rid); -void FORWARD_2(link_set_end_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); -Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_location, RID, p_link, rid_to_rid); +void FORWARD_2(link_set_start_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_position, RID, p_link, rid_to_rid); +void FORWARD_2(link_set_end_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_position, RID, p_link, rid_to_rid); void FORWARD_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real); real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid); void FORWARD_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 0adf736b2a..b50808cddf 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -130,7 +130,7 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const; - /// Creates a new link between locations in the nav map. + /// Creates a new link between positions in the nav map. virtual RID link_create(); /// Set the map of this link. @@ -145,13 +145,13 @@ public: virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers); virtual uint32_t link_get_navigation_layers(RID p_link) const; - /// Set the start location of the link. - virtual void link_set_start_location(RID p_link, Vector2 p_location); - virtual Vector2 link_get_start_location(RID p_link) const; + /// Set the start position of the link. + virtual void link_set_start_position(RID p_link, Vector2 p_position); + virtual Vector2 link_get_start_position(RID p_link) const; - /// Set the end location of the link. - virtual void link_set_end_location(RID p_link, Vector2 p_location); - virtual Vector2 link_get_end_location(RID p_link) const; + /// Set the end position of the link. + virtual void link_set_end_position(RID p_link, Vector2 p_position); + virtual Vector2 link_get_end_position(RID p_link) const; /// Set the enter cost of the link. virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost); diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 253b28a623..07256badef 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -90,10 +90,10 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer3D::link_is_bidirectional); ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer3D::link_set_navigation_layers); ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer3D::link_get_navigation_layers); - ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer3D::link_set_start_location); - ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer3D::link_get_start_location); - ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer3D::link_set_end_location); - ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer3D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer3D::link_set_start_position); + ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer3D::link_get_start_position); + ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer3D::link_set_end_position); + ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer3D::link_get_end_position); ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer3D::link_set_enter_cost); ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer3D::link_get_enter_cost); ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer3D::link_set_travel_cost); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 6c4bd2e151..4d81726fc3 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -145,7 +145,7 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; - /// Creates a new link between locations in the nav map. + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; /// Set the map of this link. @@ -160,13 +160,13 @@ public: virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0; virtual uint32_t link_get_navigation_layers(RID p_link) const = 0; - /// Set the start location of the link. - virtual void link_set_start_location(RID p_link, Vector3 p_location) = 0; - virtual Vector3 link_get_start_location(RID p_link) const = 0; + /// Set the start position of the link. + virtual void link_set_start_position(RID p_link, Vector3 p_position) = 0; + virtual Vector3 link_get_start_position(RID p_link) const = 0; - /// Set the end location of the link. - virtual void link_set_end_location(RID p_link, Vector3 p_location) = 0; - virtual Vector3 link_get_end_location(RID p_link) const = 0; + /// Set the end position of the link. + virtual void link_set_end_position(RID p_link, Vector3 p_position) = 0; + virtual Vector3 link_get_end_position(RID p_link) const = 0; /// Set the enter cost of the link. virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0; diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 1dbad79477..814d145b66 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1648,7 +1648,10 @@ void SkyRD::update_dirty_skys() { tf.mipmaps = MIN(mipmaps, layers); tf.width = w; tf.height = h; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { + tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; + } sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); 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 ad63d3fd4b..1bba95f511 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -3768,6 +3768,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceForwardClustered *ginstance = static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata); @@ -3782,6 +3783,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D } void RenderForwardClustered::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata)->data->dirty_dependencies = true; } RenderGeometryInstance *RenderForwardClustered::geometry_instance_create(RID p_base) { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 412406c0b4..2aaea4288b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -717,6 +717,8 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.global_buffer_array_variable = "global_shader_uniforms.data"; actions.instance_uniform_index_variable = "instances.data[instance_index_interp].instance_uniforms_ofs"; + actions.check_multiview_samplers = true; // make sure we check sampling multiview textures + compiler.initialize(actions); } 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 25204f1abf..4065d253bb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2237,6 +2237,9 @@ RenderGeometryInstance *RenderForwardMobile::geometry_instance_create(RID p_base ginstance->data->base = p_base; ginstance->data->base_type = type; + ginstance->data->dependency_tracker.userdata = ginstance; + ginstance->data->dependency_tracker.changed_callback = _geometry_instance_dependency_changed; + ginstance->data->dependency_tracker.deleted_callback = _geometry_instance_dependency_deleted; ginstance->_mark_dirty(); @@ -2673,6 +2676,7 @@ void RenderForwardMobile::_geometry_instance_dependency_changed(Dependency::Depe case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceForwardMobile *ginstance = static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata); @@ -2687,6 +2691,7 @@ void RenderForwardMobile::_geometry_instance_dependency_changed(Dependency::Depe } void RenderForwardMobile::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata)->data->dirty_dependencies = true; } /* misc */ diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 3d1d78c63d..f9eecf4dc7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -609,6 +609,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.instance_uniform_index_variable = "draw_call.instance_uniforms_ofs"; actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture + actions.check_multiview_samplers = true; // make sure we check sampling multiview textures compiler.initialize(actions); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 2a87c4fa8d..33f6ac0276 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -253,6 +253,11 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); + if (!rb->has_internal_texture()) { + // We're likely rendering reflection probes where we can't use our backbuffers. + return; + } + RD::get_singleton()->draw_command_begin_label("Copy screen texture"); rb->allocate_blur_textures(); @@ -292,6 +297,11 @@ void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataR Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); + if (!rb->has_depth_texture()) { + // We're likely rendering reflection probes where we can't use our backbuffers. + return; + } + RD::get_singleton()->draw_command_begin_label("Copy depth texture"); // note, this only creates our back depth texture if we haven't already created it. diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 91c2000c1a..c6a3c42e57 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -118,13 +118,13 @@ layout(location = 10) out flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview -vec3 normal_roughness_uv(vec2 uv) { +vec3 multiview_uv(vec2 uv) { return vec3(uv, ViewIndex); } #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 -vec2 normal_roughness_uv(vec2 uv) { +vec2 multiview_uv(vec2 uv) { return uv; } #endif //USE_MULTIVIEW @@ -550,13 +550,13 @@ layout(location = 10) in flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview -vec3 normal_roughness_uv(vec2 uv) { +vec3 multiview_uv(vec2 uv) { return vec3(uv, ViewIndex); } #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 -vec2 normal_roughness_uv(vec2 uv) { +vec2 multiview_uv(vec2 uv) { return uv; } #endif //USE_MULTIVIEW diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 1f524313f2..8ff7a784dc 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -271,15 +271,16 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; #define multiviewSampler sampler2D #else -layout(set = 1, binding = 10) uniform texture2D depth_buffer; -layout(set = 1, binding = 11) uniform texture2D color_buffer; - #ifdef USE_MULTIVIEW +layout(set = 1, binding = 10) uniform texture2DArray depth_buffer; +layout(set = 1, binding = 11) uniform texture2DArray color_buffer; layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer; layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer; #define multiviewSampler sampler2DArray #else // USE_MULTIVIEW +layout(set = 1, binding = 10) uniform texture2D depth_buffer; +layout(set = 1, binding = 11) uniform texture2D color_buffer; layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2D ambient_buffer; layout(set = 1, binding = 15) uniform texture2D reflection_buffer; diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 2966a2ff65..90e902ca33 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -112,9 +112,15 @@ layout(location = 9) out highp float dp_clip; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW invariant gl_Position; @@ -523,9 +529,15 @@ layout(location = 9) highp in float dp_clip; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW //defines to keep compatibility with vertex diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 631ff0575b..78b39a356d 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -154,8 +154,15 @@ layout(set = 1, binding = 5) uniform highp texture2D directional_shadow_atlas; // this needs to change to providing just the lightmap we're using.. layout(set = 1, binding = 6) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES]; +#ifdef USE_MULTIVIEW +layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer; +layout(set = 1, binding = 10) uniform mediump texture2DArray color_buffer; +#define multiviewSampler sampler2DArray +#else layout(set = 1, binding = 9) uniform highp texture2D depth_buffer; layout(set = 1, binding = 10) uniform mediump texture2D color_buffer; +#define multiviewSampler sampler2D +#endif // USE_MULTIVIEW /* Set 2 Skeleton & Instancing (can change per item) */ diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 33e35a7a64..cdecb3828b 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1464,7 +1464,6 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_ fb.push_back(atlas->depth_buffer); atlas->depth_fb = RD::get_singleton()->framebuffer_create(fb); - atlas->render_buffers->cleanup(); atlas->render_buffers->configure_for_reflections(Size2i(atlas->size, atlas->size)); } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 46a0d85ba8..2015a2fca0 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -842,7 +842,7 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { - for (const RendererRD::MeshStorage::MeshInstance::Surface surface : mi->surfaces) { + for (const RendererRD::MeshStorage::MeshInstance::Surface &surface : mi->surfaces) { if (surface.versions) { for (uint32_t j = 0; j < surface.version_count; j++) { RD::get_singleton()->free(surface.versions[j].vertex_array); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 7771f75a65..255719183f 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -490,6 +490,16 @@ Ref<RenderBufferCustomDataRD> RenderSceneBuffersRD::get_custom_data(const String // Depth texture +bool RenderSceneBuffersRD::has_depth_texture() { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RID depth = texture_storage->render_target_get_override_depth(render_target); + if (depth.is_valid()) { + return true; + } else { + return has_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH); + } +} + RID RenderSceneBuffersRD::get_depth_texture() { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RID depth = texture_storage->render_target_get_override_depth(render_target); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index dc849fd56a..50fb06c0d9 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -185,6 +185,9 @@ public: // For our internal textures we provide some easy access methods. + _FORCE_INLINE_ bool has_internal_texture() const { + return has_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR); + } _FORCE_INLINE_ RID get_internal_texture() const { return get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR); } @@ -192,6 +195,7 @@ public: return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR, p_layer, 0); } + bool has_depth_texture(); RID get_depth_texture(); RID get_depth_texture(const uint32_t p_layer); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 68542f32af..2710724066 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1173,9 +1173,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene code += "("; - // if normal roughness texture is used, we will add logic to automatically switch between + // if color backbuffer, depth backbuffer or normal roughness texture is used, + // we will add logic to automatically switch between // sampler2D and sampler2D array and vec2 UV and vec3 UV. - bool normal_roughness_texture_used = false; + bool multiview_uv_needed = false; for (int i = 1; i < onode->arguments.size(); i++) { if (i > 1) { @@ -1220,8 +1221,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - if (!RS::get_singleton()->is_low_end() && is_texture_func && i == 1) { - //need to map from texture to sampler in order to sample when using Vulkan GLSL + if (is_texture_func && i == 1) { + // If we're doing a texture lookup we need to check our texture argument StringName texture_uniform; bool correct_texture_uniform = false; @@ -1240,8 +1241,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene break; } - if (correct_texture_uniform) { + if (correct_texture_uniform && !RS::get_singleton()->is_low_end()) { + // Need to map from texture to sampler in order to sample when using Vulkan GLSL. String sampler_name; + bool is_depth_texture = false; bool is_normal_roughness_texture = false; if (actions.custom_samplers.has(texture_uniform)) { @@ -1251,6 +1254,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform]; if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { is_screen_texture = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + is_depth_texture = true; } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) { is_normal_roughness_texture = true; } @@ -1281,24 +1286,40 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } String data_type_name = ""; - if (is_normal_roughness_texture) { + if (actions.check_multiview_samplers && (is_screen_texture || is_depth_texture || is_normal_roughness_texture)) { data_type_name = "multiviewSampler"; - normal_roughness_texture_used = true; + multiview_uv_needed = true; } else { data_type_name = ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()); } code += data_type_name + "(" + node_code + ", " + sampler_name + ")"; + } else if (actions.check_multiview_samplers && correct_texture_uniform && RS::get_singleton()->is_low_end()) { + // Texture function on low end hardware (i.e. OpenGL). + // We just need to know if the texture supports multiview. + + if (shader->uniforms.has(texture_uniform)) { + const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform]; + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { + multiview_uv_needed = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + multiview_uv_needed = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) { + multiview_uv_needed = true; + } + } + + code += node_code; } else { code += node_code; } - } else { - if (normal_roughness_texture_used && i == 2) { - // UV coordinate after using normal roughness texture. - node_code = "normal_roughness_uv(" + node_code + ".xy)"; - } + } else if (multiview_uv_needed && i == 2) { + // UV coordinate after using color, depth or normal roughness texture. + node_code = "multiview_uv(" + node_code + ".xy)"; code += node_code; + } else { + code += node_code; } } code += ")"; diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 43bea213da..3bb29a545b 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -101,6 +101,7 @@ public: String instance_uniform_index_variable; uint32_t base_varying_index = 0; bool apply_luminance_multiplier = false; + bool check_multiview_samplers = false; }; private: diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index ba7ffda00a..7a4ac709f4 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -90,8 +90,9 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "IDENTIFIER", "TRUE", "FALSE", - "REAL_CONSTANT", + "FLOAT_CONSTANT", "INT_CONSTANT", + "UINT_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_BVEC2", @@ -126,6 +127,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "INTERPOLATION_FLAT", "INTERPOLATION_SMOOTH", "CONST", + "STRUCT", "PRECISION_LOW", "PRECISION_MID", "PRECISION_HIGH", @@ -169,6 +171,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "CF_DO", "CF_SWITCH", "CF_CASE", + "CF_DEFAULT", "CF_BREAK", "CF_CONTINUE", "CF_RETURN", @@ -185,19 +188,26 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "SEMICOLON", "PERIOD", "UNIFORM", + "UNIFORM_GROUP", "INSTANCE", "GLOBAL", "VARYING", - "IN", - "OUT", - "INOUT", + "ARG_IN", + "ARG_OUT", + "ARG_INOUT", "RENDER_MODE", - "SOURCE_COLOR", "HINT_DEFAULT_WHITE_TEXTURE", "HINT_DEFAULT_BLACK_TEXTURE", "HINT_DEFAULT_TRANSPARENT_TEXTURE", "HINT_NORMAL_TEXTURE", + "HINT_ROUGHNESS_NORMAL_TEXTURE", + "HINT_ROUGHNESS_R", + "HINT_ROUGHNESS_G", + "HINT_ROUGHNESS_B", + "HINT_ROUGHNESS_A", + "HINT_ROUGHNESS_GRAY", "HINT_ANISOTROPY_TEXTURE", + "HINT_SOURCE_COLOR", "HINT_RANGE", "HINT_INSTANCE_INDEX", "HINT_SCREEN_TEXTURE", @@ -612,7 +622,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { char_idx += 2; include_positions.resize(include_positions.size() - 1); // Pop back. - tk_line = include_positions[include_positions.size() - 1].line; // Restore line. + tk_line = include_positions[include_positions.size() - 1].line - 1; // Restore line. } else { return _make_token(TK_ERROR, "Invalid include enter/exit hint token (@@> and @@<)"); @@ -2827,6 +2837,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // dFdxCoarse + + { "dFdxCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // dFdxFine + + { "dFdxFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // dFdy { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, @@ -2834,6 +2858,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // dFdyCoarse + + { "dFdyCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // dFdyFine + + { "dFdyFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // fwidth { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, @@ -2841,6 +2879,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // fwidthCoarse + + { "fwidthCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // fwidthFine + + { "fwidthFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // Sub-functions. // array @@ -8233,6 +8285,10 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f _set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier)); return ERR_PARSE_ERROR; } + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + _set_error(RTR("Uniform instances are not supported in gl_compatibility shaders.")); + return ERR_PARSE_ERROR; + } if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) { tk = _get_token(); if (tk.type != TK_UNIFORM) { diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp index 97efd2df94..816d937e9f 100644 --- a/servers/rendering/shader_preprocessor.cpp +++ b/servers/rendering/shader_preprocessor.cpp @@ -78,19 +78,46 @@ char32_t ShaderPreprocessor::Tokenizer::peek() { return 0; } +int ShaderPreprocessor::Tokenizer::consume_line_continuations(int p_offset) { + int skips = 0; + + for (int i = index + p_offset; i < size; i++) { + char32_t c = code[i]; + if (c == '\\') { + if (i + 1 < size && code[i + 1] == '\n') { + // This line ends with "\" and "\n" continuation. + add_generated(Token('\n', line)); + line++; + skips++; + + i = i + 2; + index = i; + } else { + break; + } + } else if (!is_whitespace(c)) { + break; + } + } + return skips; +} + LocalVector<ShaderPreprocessor::Token> ShaderPreprocessor::Tokenizer::advance(char32_t p_what) { LocalVector<ShaderPreprocessor::Token> tokens; while (index < size) { char32_t c = code[index++]; - - tokens.push_back(ShaderPreprocessor::Token(c, line)); + if (c == '\\' && consume_line_continuations(-1) > 0) { + continue; + } if (c == '\n') { add_generated(ShaderPreprocessor::Token('\n', line)); line++; } + tokens.push_back(ShaderPreprocessor::Token(c, line)); + if (c == p_what || c == 0) { return tokens; } @@ -104,6 +131,11 @@ void ShaderPreprocessor::Tokenizer::skip_whitespace() { } } +bool ShaderPreprocessor::Tokenizer::consume_empty_line() { + // Read until newline and return true if the content was all whitespace/empty. + return tokens_to_string(advance('\n')).strip_edges().size() == 0; +} + String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_started) { if (r_is_cursor != nullptr) { *r_is_cursor = false; @@ -113,6 +145,10 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s while (true) { char32_t c = peek(); + if (c == '\\' && consume_line_continuations(0) > 0) { + continue; + } + if (is_char_end(c) || c == '(' || c == ')' || c == ',' || c == ';') { break; } @@ -146,8 +182,10 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s String ShaderPreprocessor::Tokenizer::peek_identifier() { const int original = index; + const int original_line = line; String id = get_identifier(); index = original; + line = original_line; return id; } @@ -485,7 +523,9 @@ void ShaderPreprocessor::process_else(Tokenizer *p_tokenizer) { state->previous_region->to_line = line - 1; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid else."), p_tokenizer->get_line()); + } bool skip = false; for (int i = 0; i < state->current_branch->conditions.size(); i++) { @@ -508,17 +548,21 @@ void ShaderPreprocessor::process_else(Tokenizer *p_tokenizer) { } void ShaderPreprocessor::process_endif(Tokenizer *p_tokenizer) { + const int line = p_tokenizer->get_line(); + state->condition_depth--; if (state->condition_depth < 0) { - set_error(RTR("Unmatched endif."), p_tokenizer->get_line()); + set_error(RTR("Unmatched endif."), line); return; } if (state->previous_region != nullptr) { - state->previous_region->to_line = p_tokenizer->get_line() - 1; + state->previous_region->to_line = line - 1; state->previous_region = state->previous_region->parent; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid endif."), line); + } state->current_branch = state->current_branch->parent; state->branches.pop_back(); @@ -574,12 +618,10 @@ void ShaderPreprocessor::process_ifdef(Tokenizer *p_tokenizer) { return; } - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (!p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid ifdef."), line); return; } - p_tokenizer->advance('\n'); bool success = state->defines.has(label); start_branch_condition(p_tokenizer, success); @@ -598,12 +640,10 @@ void ShaderPreprocessor::process_ifndef(Tokenizer *p_tokenizer) { return; } - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (!p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid ifndef."), line); return; } - p_tokenizer->advance('\n'); bool success = !state->defines.has(label); start_branch_condition(p_tokenizer, success); @@ -628,9 +668,8 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { } } path = path.substr(0, path.length() - 1); - p_tokenizer->skip_whitespace(); - if (path.is_empty() || !is_char_end(p_tokenizer->peek())) { + if (path.is_empty() || !p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid path."), line); return; } @@ -728,25 +767,24 @@ void ShaderPreprocessor::process_pragma(Tokenizer *p_tokenizer) { return; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid pragma directive."), line); + return; + } } void ShaderPreprocessor::process_undef(Tokenizer *p_tokenizer) { const int line = p_tokenizer->get_line(); const String label = p_tokenizer->get_identifier(); - if (label.is_empty() || !state->defines.has(label)) { - set_error(RTR("Invalid name."), line); - return; - } - - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (label.is_empty() || !p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid undef."), line); return; } - memdelete(state->defines[label]); - state->defines.erase(label); + if (state->defines.has(label)) { + memdelete(state->defines[label]); + state->defines.erase(label); + } } void ShaderPreprocessor::add_region(int p_line, bool p_enabled, Region *p_parent_region) { @@ -957,15 +995,57 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num String body = define->body; if (define->arguments.size() > 0) { // Complex macro with arguments. - int args_start = index + key.length(); - int args_end = p_line.find(")", args_start); - if (args_start == -1 || args_end == -1) { - set_error(RTR("Missing macro argument parenthesis."), p_line_number); - return false; + + int args_start = -1; + int args_end = -1; + int brackets_open = 0; + Vector<String> args; + for (int i = index_start - 1; i < p_line.length(); i++) { + bool add_argument = false; + bool reached_end = false; + char32_t c = p_line[i]; + + if (c == '(') { + brackets_open++; + if (brackets_open == 1) { + args_start = i + 1; + args_end = -1; + } + } else if (c == ')') { + brackets_open--; + if (brackets_open == 0) { + args_end = i; + add_argument = true; + reached_end = true; + } + } else if (c == ',') { + if (brackets_open == 1) { + args_end = i; + add_argument = true; + } + } + + if (add_argument) { + if (args_start == -1 || args_end == -1) { + set_error(RTR("Invalid macro argument list."), p_line_number); + return false; + } + + String arg = p_line.substr(args_start, args_end - args_start).strip_edges(); + if (arg.is_empty()) { + set_error(RTR("Invalid macro argument."), p_line_number); + return false; + } + args.append(arg); + + args_start = args_end + 1; + } + + if (reached_end) { + break; + } } - String values = result.substr(args_start + 1, args_end - (args_start + 1)); - Vector<String> args = values.split(","); if (args.size() != define->arguments.size()) { set_error(RTR("Invalid macro argument count."), p_line_number); return false; @@ -987,9 +1067,6 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length()); } else { result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length())); - // Manually reset index_start to where the body value of the define finishes. - // This ensures we don't skip another instance of this macro in the string. - index_start = index + body.length() + 1; } r_expanded = result; diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h index 6e5533c575..2b7d2c274e 100644 --- a/servers/rendering/shader_preprocessor.h +++ b/servers/rendering/shader_preprocessor.h @@ -93,11 +93,13 @@ private: int get_line() const; int get_index() const; char32_t peek(); + int consume_line_continuations(int p_offset); void get_and_clear_generated(Vector<Token> *r_out); void backtrack(char32_t p_what); LocalVector<Token> advance(char32_t p_what); void skip_whitespace(); + bool consume_empty_line(); String get_identifier(bool *r_is_cursor = nullptr, bool p_started = false); String peek_identifier(); Token get_token(); diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 85b53264f2..dfbaa36af8 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -868,6 +868,196 @@ TEST_CASE("[Variant] Basic comparison") { CHECK_NE(Variant(Dictionary()), Variant()); } +TEST_CASE("[Variant] Identity comparison") { + // Value types are compared by value + Variant aabb = AABB(); + CHECK(aabb.identity_compare(aabb)); + CHECK(aabb.identity_compare(AABB())); + CHECK_FALSE(aabb.identity_compare(AABB(Vector3(1, 2, 3), Vector3(1, 2, 3)))); + + Variant basis = Basis(); + CHECK(basis.identity_compare(basis)); + CHECK(basis.identity_compare(Basis())); + CHECK_FALSE(basis.identity_compare(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)))); + + Variant bool_var = true; + CHECK(bool_var.identity_compare(bool_var)); + CHECK(bool_var.identity_compare(true)); + CHECK_FALSE(bool_var.identity_compare(false)); + + Variant callable = Callable(); + CHECK(callable.identity_compare(callable)); + CHECK(callable.identity_compare(Callable())); + CHECK_FALSE(callable.identity_compare(Callable(ObjectID(), StringName("lambda")))); + + Variant color = Color(); + CHECK(color.identity_compare(color)); + CHECK(color.identity_compare(Color())); + CHECK_FALSE(color.identity_compare(Color(255, 0, 255))); + + Variant float_var = 1.0; + CHECK(float_var.identity_compare(float_var)); + CHECK(float_var.identity_compare(1.0)); + CHECK_FALSE(float_var.identity_compare(2.0)); + + Variant int_var = 1; + CHECK(int_var.identity_compare(int_var)); + CHECK(int_var.identity_compare(1)); + CHECK_FALSE(int_var.identity_compare(2)); + + Variant nil = Variant(); + CHECK(nil.identity_compare(nil)); + CHECK(nil.identity_compare(Variant())); + CHECK_FALSE(nil.identity_compare(true)); + + Variant node_path = NodePath("godot"); + CHECK(node_path.identity_compare(node_path)); + CHECK(node_path.identity_compare(NodePath("godot"))); + CHECK_FALSE(node_path.identity_compare(NodePath("waiting"))); + + Variant plane = Plane(); + CHECK(plane.identity_compare(plane)); + CHECK(plane.identity_compare(Plane())); + CHECK_FALSE(plane.identity_compare(Plane(Vector3(1, 2, 3), 42))); + + Variant projection = Projection(); + CHECK(projection.identity_compare(projection)); + CHECK(projection.identity_compare(Projection())); + CHECK_FALSE(projection.identity_compare(Projection(Transform3D(Basis(Vector3(1, 2, 3).normalized(), 45), Vector3(1, 2, 3))))); + + Variant quaternion = Quaternion(); + CHECK(quaternion.identity_compare(quaternion)); + CHECK(quaternion.identity_compare(Quaternion())); + CHECK_FALSE(quaternion.identity_compare(Quaternion(Vector3(1, 2, 3).normalized(), 45))); + + Variant rect2 = Rect2(); + CHECK(rect2.identity_compare(rect2)); + CHECK(rect2.identity_compare(Rect2())); + CHECK_FALSE(rect2.identity_compare(Rect2(Point2(Vector2(1, 2)), Size2(Vector2(1, 2))))); + + Variant rect2i = Rect2i(); + CHECK(rect2i.identity_compare(rect2i)); + CHECK(rect2i.identity_compare(Rect2i())); + CHECK_FALSE(rect2i.identity_compare(Rect2i(Point2i(Vector2i(1, 2)), Size2i(Vector2i(1, 2))))); + + Variant rid = RID(); + CHECK(rid.identity_compare(rid)); + CHECK(rid.identity_compare(RID())); + CHECK_FALSE(rid.identity_compare(RID::from_uint64(123))); + + Variant signal = Signal(); + CHECK(signal.identity_compare(signal)); + CHECK(signal.identity_compare(Signal())); + CHECK_FALSE(signal.identity_compare(Signal(ObjectID(), StringName("lambda")))); + + Variant str = "godot"; + CHECK(str.identity_compare(str)); + CHECK(str.identity_compare("godot")); + CHECK_FALSE(str.identity_compare("waiting")); + + Variant str_name = StringName("godot"); + CHECK(str_name.identity_compare(str_name)); + CHECK(str_name.identity_compare(StringName("godot"))); + CHECK_FALSE(str_name.identity_compare(StringName("waiting"))); + + Variant transform2d = Transform2D(); + CHECK(transform2d.identity_compare(transform2d)); + CHECK(transform2d.identity_compare(Transform2D())); + CHECK_FALSE(transform2d.identity_compare(Transform2D(45, Vector2(1, 2)))); + + Variant transform3d = Transform3D(); + CHECK(transform3d.identity_compare(transform3d)); + CHECK(transform3d.identity_compare(Transform3D())); + CHECK_FALSE(transform3d.identity_compare(Transform3D(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)), Vector3(1, 2, 3)))); + + Variant vect2 = Vector2(); + CHECK(vect2.identity_compare(vect2)); + CHECK(vect2.identity_compare(Vector2())); + CHECK_FALSE(vect2.identity_compare(Vector2(1, 2))); + + Variant vect2i = Vector2i(); + CHECK(vect2i.identity_compare(vect2i)); + CHECK(vect2i.identity_compare(Vector2i())); + CHECK_FALSE(vect2i.identity_compare(Vector2i(1, 2))); + + Variant vect3 = Vector3(); + CHECK(vect3.identity_compare(vect3)); + CHECK(vect3.identity_compare(Vector3())); + CHECK_FALSE(vect3.identity_compare(Vector3(1, 2, 3))); + + Variant vect3i = Vector3i(); + CHECK(vect3i.identity_compare(vect3i)); + CHECK(vect3i.identity_compare(Vector3i())); + CHECK_FALSE(vect3i.identity_compare(Vector3i(1, 2, 3))); + + Variant vect4 = Vector4(); + CHECK(vect4.identity_compare(vect4)); + CHECK(vect4.identity_compare(Vector4())); + CHECK_FALSE(vect4.identity_compare(Vector4(1, 2, 3, 4))); + + Variant vect4i = Vector4i(); + CHECK(vect4i.identity_compare(vect4i)); + CHECK(vect4i.identity_compare(Vector4i())); + CHECK_FALSE(vect4i.identity_compare(Vector4i(1, 2, 3, 4))); + + // Reference types are compared by reference + Variant array = Array(); + CHECK(array.identity_compare(array)); + CHECK_FALSE(array.identity_compare(Array())); + + Variant dictionary = Dictionary(); + CHECK(dictionary.identity_compare(dictionary)); + CHECK_FALSE(dictionary.identity_compare(Dictionary())); + + Variant packed_byte_array = PackedByteArray(); + CHECK(packed_byte_array.identity_compare(packed_byte_array)); + CHECK_FALSE(packed_byte_array.identity_compare(PackedByteArray())); + + Variant packed_color_array = PackedColorArray(); + CHECK(packed_color_array.identity_compare(packed_color_array)); + CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray())); + + Variant packed_float32_array = PackedFloat32Array(); + CHECK(packed_float32_array.identity_compare(packed_float32_array)); + CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array())); + + Variant packed_float64_array = PackedFloat64Array(); + CHECK(packed_float64_array.identity_compare(packed_float64_array)); + CHECK_FALSE(packed_float64_array.identity_compare(PackedFloat64Array())); + + Variant packed_int32_array = PackedInt32Array(); + CHECK(packed_int32_array.identity_compare(packed_int32_array)); + CHECK_FALSE(packed_int32_array.identity_compare(PackedInt32Array())); + + Variant packed_int64_array = PackedInt64Array(); + CHECK(packed_int64_array.identity_compare(packed_int64_array)); + CHECK_FALSE(packed_int64_array.identity_compare(PackedInt64Array())); + + Variant packed_string_array = PackedStringArray(); + CHECK(packed_string_array.identity_compare(packed_string_array)); + CHECK_FALSE(packed_string_array.identity_compare(PackedStringArray())); + + Variant packed_vector2_array = PackedVector2Array(); + CHECK(packed_vector2_array.identity_compare(packed_vector2_array)); + CHECK_FALSE(packed_vector2_array.identity_compare(PackedVector2Array())); + + Variant packed_vector3_array = PackedVector3Array(); + CHECK(packed_vector3_array.identity_compare(packed_vector3_array)); + CHECK_FALSE(packed_vector3_array.identity_compare(PackedVector3Array())); + + Object obj_one = Object(); + Variant obj_one_var = &obj_one; + Object obj_two = Object(); + Variant obj_two_var = &obj_two; + CHECK(obj_one_var.identity_compare(obj_one_var)); + CHECK_FALSE(obj_one_var.identity_compare(obj_two_var)); + + Variant obj_null_one_var = Variant((Object *)nullptr); + Variant obj_null_two_var = Variant((Object *)nullptr); + CHECK(obj_null_one_var.identity_compare(obj_null_one_var)); + CHECK(obj_null_one_var.identity_compare(obj_null_two_var)); +} + TEST_CASE("[Variant] Nested array comparison") { Array a1 = build_array(1, build_array(2, 3)); Array a2 = build_array(1, build_array(2, 3)); |