diff options
52 files changed, 304 insertions, 129 deletions
diff --git a/SConstruct b/SConstruct index 8e9a536bdc..f3331f3b0d 100644 --- a/SConstruct +++ b/SConstruct @@ -700,12 +700,11 @@ if env.msvc: else: env.Append(LINKFLAGS=["/DEBUG:NONE"]) - if env["optimize"] == "speed": + if env["optimize"].startswith("speed"): env.Append(CCFLAGS=["/O2"]) env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "speed_trace": - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"]) + if env["optimize"] == "speed_trace": + env.Append(LINKFLAGS=["/OPT:NOICF"]) elif env["optimize"] == "size": env.Append(CCFLAGS=["/O1"]) env.Append(LINKFLAGS=["/OPT:REF"]) @@ -716,7 +715,13 @@ else: # Adding dwarf-4 explicitly makes stacktraces work with clang builds, # otherwise addr2line doesn't understand them env.Append(CCFLAGS=["-gdwarf-4"]) - if env.dev_build: + if methods.using_emcc(env): + # Emscripten only produces dwarf symbols when using "-g3". + env.Append(CCFLAGS=["-g3"]) + # Emscripten linker needs debug symbols options too. + env.Append(LINKFLAGS=["-gdwarf-4"]) + env.Append(LINKFLAGS=["-g3"]) + elif env.dev_build: env.Append(CCFLAGS=["-g3"]) else: env.Append(CCFLAGS=["-g2"]) @@ -731,17 +736,25 @@ else: else: env.Append(LINKFLAGS=["-s"]) + # Linker needs optimization flags too, at least for Emscripten. + # For other toolchains, this _may_ be useful for LTO too to disambiguate. + if env["optimize"] == "speed": env.Append(CCFLAGS=["-O3"]) + env.Append(LINKFLAGS=["-O3"]) # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces. elif env["optimize"] == "speed_trace": env.Append(CCFLAGS=["-O2"]) + env.Append(LINKFLAGS=["-O2"]) elif env["optimize"] == "size": env.Append(CCFLAGS=["-Os"]) + env.Append(LINKFLAGS=["-Os"]) elif env["optimize"] == "debug": env.Append(CCFLAGS=["-Og"]) + env.Append(LINKFLAGS=["-Og"]) elif env["optimize"] == "none": env.Append(CCFLAGS=["-O0"]) + env.Append(LINKFLAGS=["-O0"]) # Needs to happen after configure to handle "auto". if env["lto"] != "none": diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index eac1a66be7..e59f79fcc8 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -329,9 +329,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { String path = p_value; if (path.begins_with("*")) { autoload.is_singleton = true; - autoload.path = path.substr(1); + autoload.path = path.substr(1).simplify_path(); } else { - autoload.path = path; + autoload.path = path.simplify_path(); } add_autoload(autoload); } else if (p_name.operator String().begins_with("global_group/")) { diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 32030146bb..fae3de2a98 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -106,7 +106,7 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { } uint32_t size = 0; - uint8_t ipv6[16]; + uint8_t ipv6[16] = {}; rb.read(ipv6, 16, true); packet_ip.set_ipv6(ipv6); rb.read((uint8_t *)&packet_port, 4, true); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 24cf0a29c5..ceeb04b8ea 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -683,6 +683,21 @@ bool ClassDB::can_instantiate(const StringName &p_class) { return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance)); } +bool ClassDB::is_abstract(const StringName &p_class) { + OBJTYPE_RLOCK; + + ClassInfo *ti = classes.getptr(p_class); + if (!ti) { + if (!ScriptServer::is_global_class(p_class)) { + ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'."); + } + String path = ScriptServer::get_global_class_path(p_class); + Ref<Script> scr = ResourceLoader::load(path); + return scr.is_valid() && scr->is_valid() && scr->is_abstract(); + } + return ti->creation_func == nullptr && (!ti->gdextension || ti->gdextension->create_instance == nullptr); +} + bool ClassDB::is_virtual(const StringName &p_class) { OBJTYPE_RLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index fb671bdc84..228b82b588 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -287,6 +287,7 @@ public: static bool class_exists(const StringName &p_class); static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instantiate(const StringName &p_class); + static bool is_abstract(const StringName &p_class); static bool is_virtual(const StringName &p_class); static Object *instantiate(const StringName &p_class); static Object *instantiate_no_placeholders(const StringName &p_class); diff --git a/doc/classes/PhysicsPointQueryParameters2D.xml b/doc/classes/PhysicsPointQueryParameters2D.xml index 521e584173..642e87947a 100644 --- a/doc/classes/PhysicsPointQueryParameters2D.xml +++ b/doc/classes/PhysicsPointQueryParameters2D.xml @@ -24,6 +24,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject2D.get_rid] to get the [RID] associated with a [CollisionObject2D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)"> The position being queried for, in global coordinates. diff --git a/doc/classes/PhysicsPointQueryParameters3D.xml b/doc/classes/PhysicsPointQueryParameters3D.xml index 1cbc11bd03..a53300c78d 100644 --- a/doc/classes/PhysicsPointQueryParameters3D.xml +++ b/doc/classes/PhysicsPointQueryParameters3D.xml @@ -20,6 +20,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject3D.get_rid] to get the [RID] associated with a [CollisionObject3D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)"> The position being queried for, in global coordinates. diff --git a/doc/classes/PhysicsRayQueryParameters2D.xml b/doc/classes/PhysicsRayQueryParameters2D.xml index 3d69e092f6..a9738b4690 100644 --- a/doc/classes/PhysicsRayQueryParameters2D.xml +++ b/doc/classes/PhysicsRayQueryParameters2D.xml @@ -36,6 +36,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject2D.get_rid] to get the [RID] associated with a [CollisionObject2D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="from" type="Vector2" setter="set_from" getter="get_from" default="Vector2(0, 0)"> The starting point of the ray being queried for, in global coordinates. diff --git a/doc/classes/PhysicsRayQueryParameters3D.xml b/doc/classes/PhysicsRayQueryParameters3D.xml index b203b8f555..175b594fb0 100644 --- a/doc/classes/PhysicsRayQueryParameters3D.xml +++ b/doc/classes/PhysicsRayQueryParameters3D.xml @@ -36,6 +36,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject3D.get_rid] to get the [RID] associated with a [CollisionObject3D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="from" type="Vector3" setter="set_from" getter="get_from" default="Vector3(0, 0, 0)"> The starting point of the ray being queried for, in global coordinates. diff --git a/doc/classes/PhysicsShapeQueryParameters2D.xml b/doc/classes/PhysicsShapeQueryParameters2D.xml index 915d94a54c..3687a4dc5a 100644 --- a/doc/classes/PhysicsShapeQueryParameters2D.xml +++ b/doc/classes/PhysicsShapeQueryParameters2D.xml @@ -20,6 +20,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject2D.get_rid] to get the [RID] associated with a [CollisionObject2D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.0"> The collision margin for the shape. diff --git a/doc/classes/PhysicsShapeQueryParameters3D.xml b/doc/classes/PhysicsShapeQueryParameters3D.xml index eba2b8287f..f05322f1ab 100644 --- a/doc/classes/PhysicsShapeQueryParameters3D.xml +++ b/doc/classes/PhysicsShapeQueryParameters3D.xml @@ -20,6 +20,7 @@ </member> <member name="exclude" type="RID[]" setter="set_exclude" getter="get_exclude" default="[]"> The list of object [RID]s that will be excluded from collisions. Use [method CollisionObject3D.get_rid] to get the [RID] associated with a [CollisionObject3D]-derived node. + [b]Note:[/b] The returned array is copied and any changes to it will not update the original property value. To update the value you need to modify the returned array, and then assign it to the property again. </member> <member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.0"> The collision margin for the shape. diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 24d2d26beb..7e0c39ac7c 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -432,7 +432,7 @@ Adds [code skip-lint][ol][/code] or [code skip-lint][ul][/code] tag to the tag stack. Multiplies [param level] by current [member tab_size] to determine new margin length. </description> </method> - <method name="push_meta"> + <method name="push_meta" keywords="push_url"> <return type="void" /> <param index="0" name="data" type="Variant" /> <param index="1" name="underline_mode" type="int" enum="RichTextLabel.MetaUnderline" default="1" /> @@ -628,7 +628,7 @@ <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead. </member> - <member name="meta_underlined" type="bool" setter="set_meta_underline" getter="is_meta_underlined" default="true"> + <member name="meta_underlined" type="bool" setter="set_meta_underline" getter="is_meta_underlined" default="true" keywords="url_underlined"> If [code]true[/code], the label underlines meta tags such as [code skip-lint][url]{text}[/url][/code]. These tags can call a function when clicked if [signal meta_clicked] is connected to a function. </member> <member name="progress_bar_delay" type="int" setter="set_progress_bar_delay" getter="get_progress_bar_delay" default="1000"> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 345d0512ff..4158fbe710 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -4,8 +4,10 @@ A 2×3 matrix representing a 2D transformation. </brief_description> <description> - A 2×3 matrix (2 rows, 3 columns) used for 2D linear transformations. It can represent transformations such as translation, rotation, and scaling. It consists of three [Vector2] values: [member x], [member y], and the [member origin]. + The [Transform2D] built-in [Variant] type is a 2×3 [url=https://en.wikipedia.org/wiki/Matrix_(mathematics)]matrix[/url] representing a transformation in 2D space. It contains three [Vector2] values: [member x], [member y], and [member origin]. Together, they can represent translation, rotation, scale, and skew. + The [member x] and [member y] axes form a 2×2 matrix, known as the transform's [b]basis[/b]. The length of each axis ([method Vector2.length]) influences the transform's scale, while the direction of all axes influence the rotation. Usually, both axes are perpendicular to one another. However, when you rotate one axis individually, the transform becomes skewed. Applying a skewed transform to a 2D sprite will make the sprite appear distorted. For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial. + [b]Note:[/b] Unlike [Transform3D], there is no 2D equivalent to the [Basis] type. All mentions of "basis" refer to the [member x] and [member y] components of [Transform2D]. </description> <tutorials> <link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link> @@ -17,7 +19,7 @@ <constructor name="Transform2D"> <return type="Transform2D" /> <description> - Constructs a default-initialized [Transform2D] set to [constant IDENTITY]. + Constructs a [Transform2D] identical to [constant IDENTITY]. </description> </constructor> <constructor name="Transform2D"> @@ -32,7 +34,7 @@ <param index="0" name="rotation" type="float" /> <param index="1" name="position" type="Vector2" /> <description> - Constructs the transform from a given angle (in radians) and position. + Constructs a [Transform2D] from a given angle (in radians) and position. </description> </constructor> <constructor name="Transform2D"> @@ -42,7 +44,7 @@ <param index="2" name="skew" type="float" /> <param index="3" name="position" type="Vector2" /> <description> - Constructs the transform from a given angle (in radians), scale, skew (in radians) and position. + Constructs a [Transform2D] from a given angle (in radians), scale, skew (in radians), and position. </description> </constructor> <constructor name="Transform2D"> @@ -51,7 +53,7 @@ <param index="1" name="y_axis" type="Vector2" /> <param index="2" name="origin" type="Vector2" /> <description> - Constructs the transform from 3 [Vector2] values representing [member x], [member y], and the [member origin] (the three column vectors). + Constructs a [Transform2D] from 3 [Vector2] values representing [member x], [member y], and the [member origin] (the three matrix columns). </description> </constructor> </constructors> @@ -59,56 +61,81 @@ <method name="affine_inverse" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the inverse of the transform, under the assumption that the basis is invertible (must have non-zero determinant). + Returns the inverted version of this transform. Unlike [method inverse], this method works with almost any basis, including non-uniform ones, but is slower. See also [method inverse]. + [b]Note:[/b] For this method to return correctly, the transform's basis needs to have a determinant that is not exactly [code]0[/code] (see [method determinant]). </description> </method> <method name="basis_xform" qualifiers="const"> <return type="Vector2" /> <param index="0" name="v" type="Vector2" /> <description> - Returns a vector transformed (multiplied) by the basis matrix. - This method does not account for translation (the [member origin] vector). + Returns a copy of the [param v] vector, transformed (multiplied) by the transform basis's matrix. Unlike the multiplication operator ([code]*[/code]), this method ignores the [member origin]. </description> </method> <method name="basis_xform_inv" qualifiers="const"> <return type="Vector2" /> <param index="0" name="v" type="Vector2" /> <description> - Returns a vector transformed (multiplied) by the inverse basis matrix, under the assumption that the basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). - This method does not account for translation (the [member origin] vector). - [code]transform.basis_xform_inv(vector)[/code] is equivalent to [code]transform.inverse().basis_xform(vector)[/code]. See [method inverse]. - For non-orthonormal transforms (e.g. with scaling) [code]transform.affine_inverse().basis_xform(vector)[/code] can be used instead. See [method affine_inverse]. + Returns a copy of the [param v] vector, transformed (multiplied) by the inverse transform basis's matrix (see [method inverse]). This method ignores the [member origin]. + [b]Note:[/b] This method assumes that this transform's basis is [i]orthonormal[/i] (see [method orthonormalized]). If the basis is not orthonormal, [code]transform.affine_inverse().basis_xform(vector)[/code] should be used instead (see [method affine_inverse]). </description> </method> <method name="determinant" qualifiers="const"> <return type="float" /> <description> - Returns the determinant of the basis matrix. If the basis is uniformly scaled, then its determinant equals the square of the scale factor. - A negative determinant means the basis was flipped, so one part of the scale is negative. A zero determinant means the basis isn't invertible, and is usually considered invalid. + Returns the [url=https://en.wikipedia.org/wiki/Determinant]determinant[/url] of this transform basis's matrix. For advanced math, this number can be used to determine a few attributes: + - If the determinant is exactly [code]0[/code], the basis is not invertible (see [method inverse]). + - If the determinant is a negative number, the basis represents a negative scale. + [b]Note:[/b] If the basis's scale is the same for every axis, its determinant is always that scale by the power of 2. </description> </method> <method name="get_origin" qualifiers="const"> <return type="Vector2" /> <description> - Returns the transform's origin (translation). + Returns this transform's translation. Equivalent to [member origin]. </description> </method> <method name="get_rotation" qualifiers="const"> <return type="float" /> <description> - Returns the transform's rotation (in radians). + Returns this transform's rotation (in radians). This is equivalent to [member x]'s angle (see [method Vector2.angle]). </description> </method> <method name="get_scale" qualifiers="const"> <return type="Vector2" /> <description> - Returns the scale. + Returns the length of both [member x] and [member y], as a [Vector2]. If this transform's basis is not skewed, this value is the scaling factor. It is not affected by rotation. + [codeblocks] + [gdscript] + var my_transform = Transform2D( + Vector2(2, 0), + Vector2(0, 4), + Vector2(0, 0) + ) + # Rotating the Transform2D in any way preserves its scale. + my_transform = my_transform.rotated(TAU / 2) + + print(my_transform.get_scale()) # Prints (2, 4). + [/gdscript] + [csharp] + var myTransform = new Transform2D( + Vector3(2.0f, 0.0f), + Vector3(0.0f, 4.0f), + Vector3(0.0f, 0.0f) + ); + // Rotating the Transform2D in any way preserves its scale. + myTransform = myTransform.Rotated(Mathf.Tau / 2.0f); + + GD.Print(myTransform.GetScale()); // Prints (2, 4, 8). + [/csharp] + [/codeblocks] + [b]Note:[/b] If the value returned by [method determinant] is negative, the scale is also negative. </description> </method> <method name="get_skew" qualifiers="const"> <return type="float" /> <description> - Returns the transform's skew (in radians). + Returns this transform's skew (in radians). </description> </method> <method name="interpolate_with" qualifiers="const"> @@ -116,19 +143,21 @@ <param index="0" name="xform" type="Transform2D" /> <param index="1" name="weight" type="float" /> <description> - Returns a transform interpolated between this transform and another by a given [param weight] (on the range of 0.0 to 1.0). + Returns the result of the linear interpolation between this transform and [param xform] by the given [param weight]. + The [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). Values outside this range are allowed and can be used to perform [i]extrapolation[/i] instead. </description> </method> <method name="inverse" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the inverse of the transform, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). Use [method affine_inverse] for non-orthonormal transforms (e.g. with scaling). + Returns the [url=https://en.wikipedia.org/wiki/Invertible_matrix]inverted version of this transform[/url]. + [b]Note:[/b] For this method to return correctly, the transform's basis needs to be [i]orthonormal[/i] (see [method orthonormalized]). That means, the basis should only represent a rotation. If it does not, use [method affine_inverse] instead. </description> </method> <method name="is_conformal" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the transform's basis is conformal, meaning it preserves angles and distance ratios, and may only be composed of rotation and uniform scale. Returns [code]false[/code] if the transform's basis has non-uniform scale or shear/skew. This can be used to validate if the transform is non-distorted, which is important for physics and other use cases. + Returns [code]true[/code] if this transform's basis is conformal. A conformal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]uniform[/i] (the axes share the same length). This method can be especially useful during physics calculations. </description> </method> <method name="is_equal_approx" qualifiers="const"> @@ -148,14 +177,13 @@ <return type="Transform2D" /> <param index="0" name="target" type="Vector2" default="Vector2(0, 0)" /> <description> - Returns a copy of the transform rotated such that the rotated X-axis points towards the [param target] position. - Operations take place in global space. + Returns a copy of the transform rotated such that the rotated X-axis points towards the [param target] position, in global space. </description> </method> <method name="orthonormalized" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the transform with the basis orthogonal (90 degrees), and normalized axis vectors (scale of 1 or -1). + Returns a copy of this transform with its basis orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1[/code]), which also means it can only represent rotation. </description> </method> <method name="rotated" qualifiers="const"> @@ -215,24 +243,41 @@ </methods> <members> <member name="origin" type="Vector2" setter="" getter="" default="Vector2(0, 0)"> - The origin vector (column 2, the third column). Equivalent to array index [code]2[/code]. The origin vector represents translation. + The translation offset of this transform, and the column [code]2[/code] of the matrix. In 2D space, this can be seen as the position. </member> <member name="x" type="Vector2" setter="" getter="" default="Vector2(1, 0)"> - The basis matrix's X vector (column 0). Equivalent to array index [code]0[/code]. + The transform basis's X axis, and the column [code]0[/code] of the matrix. Combined with [member y], this represents the transform's rotation, scale, and skew. + On the identity transform, this vector points right ([constant Vector2.RIGHT]). </member> <member name="y" type="Vector2" setter="" getter="" default="Vector2(0, 1)"> - The basis matrix's Y vector (column 1). Equivalent to array index [code]1[/code]. + The transform basis's Y axis, and the column [code]1[/code] of the matrix. Combined with [member x], this represents the transform's rotation, scale, and skew. + On the identity transform, this vector points up ([constant Vector2.UP]). </member> </members> <constants> <constant name="IDENTITY" value="Transform2D(1, 0, 0, 1, 0, 0)"> - The identity [Transform2D] with no translation, rotation or scaling applied. When applied to other data structures, [constant IDENTITY] performs no transformation. + The identity [Transform2D]. A transform with no translation, no rotation, and its scale being [code]1[/code]. When multiplied by another [Variant] such as [Rect2] or another [Transform2D], no transformation occurs. This means that: + - The [member x] points right ([constant Vector2.RIGHT]); + - The [member y] points up ([constant Vector2.UP]). + [codeblock] + var transform = Transform2D.IDENTITY + print("| X | Y | Origin") + print("| %s | %s | %s" % [transform.x.x, transform.y.x, transform.origin.x]) + print("| %s | %s | %s" % [transform.x.y, transform.y.y, transform.origin.y]) + # Prints: + # | X | Y | Origin + # | 1 | 0 | 0 + # | 0 | 1 | 0 + [/codeblock] + This is identical to creating [constructor Transform2D] without any parameters. This constant can be used to make your code clearer, and for consistency with C#. </constant> <constant name="FLIP_X" value="Transform2D(-1, 0, 0, 1, 0, 0)"> - The [Transform2D] that will flip something along the X axis. + When any transform is multiplied by [constant FLIP_X], it negates all components of the [member x] axis (the X column). + When [constant FLIP_X] is multiplied by any basis, it negates the [member Vector2.x] component of all axes (the X row). </constant> <constant name="FLIP_Y" value="Transform2D(1, 0, 0, -1, 0, 0)"> - The [Transform2D] that will flip something along the Y axis. + When any transform is multiplied by [constant FLIP_Y], it negates all components of the [member y] axis (the Y column). + When [constant FLIP_Y] is multiplied by any basis, it negates the [member Vector2.y] component of all axes (the Y row). </constant> </constants> <operators> @@ -240,7 +285,7 @@ <return type="bool" /> <param index="0" name="right" type="Transform2D" /> <description> - Returns [code]true[/code] if the transforms are not equal. + Returns [code]true[/code] if the components of both transforms are not equal. [b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable. </description> </operator> @@ -248,63 +293,69 @@ <return type="PackedVector2Array" /> <param index="0" name="right" type="PackedVector2Array" /> <description> - Transforms (multiplies) each element of the [Vector2] array by the given [Transform2D] matrix. + Transforms (multiplies) every [Vector2] element of the given [PackedVector2Array] by this transformation matrix. + On larger arrays, this operation is much faster than transforming each [Vector2] individually. </description> </operator> <operator name="operator *"> <return type="Rect2" /> <param index="0" name="right" type="Rect2" /> <description> - Transforms (multiplies) the [Rect2] by the given [Transform2D] matrix. + Transforms (multiplies) the [Rect2] by this transformation matrix. </description> </operator> <operator name="operator *"> <return type="Transform2D" /> <param index="0" name="right" type="Transform2D" /> <description> - Composes these two transformation matrices by multiplying them together. This has the effect of transforming the second transform (the child) by the first transform (the parent). + Transforms (multiplies) this transform by the [param right] transform. + This is the operation performed between parent and child [CanvasItem] nodes. + [b]Note:[/b] If you need to only modify one attribute of this transform, consider using one of the following methods, instead: + - For translation, see [method translated] or [method translated_local]. + - For rotation, see [method rotated] or [method rotated_local]. + - For scale, see [method scaled] or [method scaled_local]. </description> </operator> <operator name="operator *"> <return type="Vector2" /> <param index="0" name="right" type="Vector2" /> <description> - Transforms (multiplies) the [Vector2] by the given [Transform2D] matrix. + Transforms (multiplies) the [Vector2] by this transformation matrix. </description> </operator> <operator name="operator *"> <return type="Transform2D" /> <param index="0" name="right" type="float" /> <description> - This operator multiplies all components of the [Transform2D], including the [member origin] vector, which scales it uniformly. + Multiplies all components of the [Transform2D] by the given [float], including the [member origin]. This affects the transform's scale uniformly. </description> </operator> <operator name="operator *"> <return type="Transform2D" /> <param index="0" name="right" type="int" /> <description> - This operator multiplies all components of the [Transform2D], including the [member origin] vector, which scales it uniformly. + Multiplies all components of the [Transform2D] by the given [int], including the [member origin]. This affects the transform's scale uniformly. </description> </operator> <operator name="operator /"> <return type="Transform2D" /> <param index="0" name="right" type="float" /> <description> - This operator divides all components of the [Transform2D], including the [member origin] vector, which inversely scales it uniformly. + Divides all components of the [Transform2D] by the given [float], including the [member origin]. This affects the transform's scale uniformly. </description> </operator> <operator name="operator /"> <return type="Transform2D" /> <param index="0" name="right" type="int" /> <description> - This operator divides all components of the [Transform2D], including the [member origin] vector, which inversely scales it uniformly. + Divides all components of the [Transform2D] by the given [int], including the [member origin]. This affects the transform's scale uniformly. </description> </operator> <operator name="operator =="> <return type="bool" /> <param index="0" name="right" type="Transform2D" /> <description> - Returns [code]true[/code] if the transforms are exactly equal. + Returns [code]true[/code] if the components of both transforms are exactly equal. [b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable. </description> </operator> @@ -312,7 +363,7 @@ <return type="Vector2" /> <param index="0" name="index" type="int" /> <description> - Access transform components using their index. [code]t[0][/code] is equivalent to [code]t.x[/code], [code]t[1][/code] is equivalent to [code]t.y[/code], and [code]t[2][/code] is equivalent to [code]t.origin[/code]. + Accesses each axis (column) of this transform by their index. Index [code]0[/code] is the same as [member x], index [code]1[/code] is the same as [member y], and index [code]2[/code] is the same as [member origin]. </description> </operator> </operators> diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 8a03d72b9b..1b83efee32 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -169,7 +169,7 @@ struct Texture { TYPE_3D }; - Type type; + Type type = TYPE_2D; RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY; GLenum target = GL_TEXTURE_2D; diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 9a0f2f18fa..72ab186036 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -423,8 +423,8 @@ EditorExport::EditorExport() { save_timer->set_one_shot(true); save_timer->connect("timeout", callable_mp(this, &EditorExport::_save)); - _export_presets_updated = "export_presets_updated"; - _export_presets_runnable_updated = "export_presets_runnable_updated"; + _export_presets_updated = StringName("export_presets_updated", true); + _export_presets_runnable_updated = StringName("export_presets_runnable_updated", true); singleton = this; set_process(true); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 3e94310c83..afc6d58d63 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -634,8 +634,10 @@ void EditorFileDialog::_item_selected(int p_item) { file->set_text(d["name"]); _request_single_thumbnail(get_current_dir().path_join(get_current_file())); - // FILE_MODE_OPEN_ANY can alternate this text depending on what's selected. - set_ok_button_text(TTR("Open")); + if (mode != FILE_MODE_SAVE_FILE) { + // FILE_MODE_OPEN_ANY can alternate this text depending on what's selected. + set_ok_button_text(TTR("Open")); + } } else if (mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) { file->set_text(""); set_ok_button_text(TTR("Select This Folder")); @@ -1968,6 +1970,7 @@ void EditorFileDialog::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults.name, &EditorFileDialog::set_option_name, &EditorFileDialog::get_option_name); base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &EditorFileDialog::set_option_values, &EditorFileDialog::get_option_values); base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &EditorFileDialog::set_option_default, &EditorFileDialog::get_option_default); + PropertyListHelper::register_base_helper(&base_property_helper); } void EditorFileDialog::set_show_hidden_files(bool p_show) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 294df95874..f9be1b08d9 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -772,7 +772,6 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po // Reselect if (Engine::get_singleton()->is_editor_hint()) { selected_from_canvas = true; - EditorNode::get_singleton()->edit_node(item); } } } diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp index 262a1ecc1a..be5fa8745c 100644 --- a/editor/project_manager/project_dialog.cpp +++ b/editor/project_manager/project_dialog.cpp @@ -315,6 +315,8 @@ void ProjectDialog::_create_dir_toggled(bool p_pressed) { target_path = target_path.path_join(last_custom_target_dir); } } else { + // Strip any trailing slash. + target_path = target_path.rstrip("/\\"); // Save and remove target dir name. if (target_path.get_file() == auto_dir) { last_custom_target_dir = ""; diff --git a/main/main.cpp b/main/main.cpp index 060b3fe2f6..32eb32142d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -60,6 +60,7 @@ #include "platform/register_platform_apis.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" +#include "scene/property_list_helper.h" #include "scene/register_scene_types.h" #include "scene/resources/packed_scene.h" #include "scene/theme/theme_db.h" @@ -793,6 +794,7 @@ void Main::test_cleanup() { ResourceLoader::remove_custom_loaders(); ResourceSaver::remove_custom_savers(); + PropertyListHelper::clear_base_helpers(); #ifdef TOOLS_ENABLED GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR); @@ -4246,6 +4248,7 @@ void Main::cleanup(bool p_force) { ResourceLoader::remove_custom_loaders(); ResourceSaver::remove_custom_savers(); + PropertyListHelper::clear_base_helpers(); // Flush before uninitializing the scene, but delete the MessageQueue as late as possible. message_queue->flush(); diff --git a/methods.py b/methods.py index 99c47ca077..b0f7df9ab2 100644 --- a/methods.py +++ b/methods.py @@ -268,7 +268,7 @@ def get_version_info(module_version_string="", silent=False): if os.path.exists(".git"): try: version_info["git_timestamp"] = subprocess.check_output( - ["git", "log", "-1", "--pretty=format:%ct", githash] + ["git", "log", "-1", "--pretty=format:%ct", "--no-show-signature", githash] ).decode("utf-8") except (subprocess.CalledProcessError, OSError): # `git` not found in PATH. @@ -648,6 +648,7 @@ def detect_visual_c_compiler_version(tools_env): def find_visual_c_batch_file(env): + # TODO: We should investigate if we can avoid relying on SCons internals here. from SCons.Tool.MSCommon.vc import find_batch_file, find_vc_pdir, get_default_version, get_host_target msvc_version = get_default_version(env) @@ -661,10 +662,11 @@ def find_visual_c_batch_file(env): if env.scons_version < (4, 6, 0): return find_batch_file(env, msvc_version, host_platform, target_platform)[0] - # Scons 4.6.0+ removed passing env, so we need to get the product_dir ourselves first, + # SCons 4.6.0+ removed passing env, so we need to get the product_dir ourselves first, # then pass that as the last param instead of env as the first param as before. - # We should investigate if we can avoid relying on SCons internals here. - product_dir = find_vc_pdir(env, msvc_version) + # Param names need to be explicit, as they were shuffled around in SCons 4.8.0. + product_dir = find_vc_pdir(msvc_version=msvc_version, env=env) + return find_batch_file(msvc_version, host_platform, target_platform, product_dir)[0] diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index a6b4bce000..67b40a6198 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -5180,7 +5180,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo if (!class_exists(base_native)) { push_error(vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native), p_source); return false; - } else if (p_is_constructor && !ClassDB::can_instantiate(base_native)) { + } else if (p_is_constructor && ClassDB::is_abstract(base_native)) { if (p_base_type.kind == GDScriptParser::DataType::CLASS) { push_error(vformat(R"(Class "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.class_type->fqcn.get_file(), base_native), p_source); } else if (p_base_type.kind == GDScriptParser::DataType::SCRIPT) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a1ea94667d..433f767f1e 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -413,7 +413,7 @@ Error GDScriptParser::parse_binary(const Vector<uint8_t> &p_binary, const String } tokenizer = buffer_tokenizer; - script_path = p_script_path; + script_path = p_script_path.simplify_path(); current = tokenizer->scan(); // Avoid error or newline as the first token. // The latter can mess with the parser when opening files filled exclusively with comments and newlines. diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 5d1805696d..912367764b 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -550,9 +550,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a return _get_default_variant_for_data_type(return_type); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { - Variant arg; - Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err); - memnew_placement(&stack[i + 3], Variant(arg)); + if (argument_types[i].builtin_type == Variant::ARRAY && argument_types[i].has_container_element_type(0)) { + const GDScriptDataType &arg_type = argument_types[i].container_element_types[0]; + Array array(p_args[i]->operator Array(), arg_type.builtin_type, arg_type.native_type, arg_type.script_type); + memnew_placement(&stack[i + 3], Variant(array)); + } else { + Variant variant; + Variant::construct(argument_types[i].builtin_type, variant, &p_args[i], 1, r_err); + if (unlikely(r_err.error)) { + r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].builtin_type; + call_depth--; + return _get_default_variant_for_data_type(return_type); + } + memnew_placement(&stack[i + 3], Variant(variant)); + } } else { memnew_placement(&stack[i + 3], Variant(*p_args[i])); } diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.gd b/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.gd new file mode 100644 index 0000000000..13f2c3b956 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.gd @@ -0,0 +1,7 @@ +# GH-93990 + +func test_param(array: Array[String]) -> void: + print(array.get_typed_builtin() == TYPE_STRING) + +func test() -> void: + test_param(PackedStringArray()) diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.out b/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.out new file mode 100644 index 0000000000..55482c2b52 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_implicit_cast_param.out @@ -0,0 +1,2 @@ +GDTEST_OK +true diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 748ef3af94..81e9deb7ab 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -454,7 +454,6 @@ public: _FORCE_INLINE_ XrTime get_predicted_display_time() { return frame_state.predictedDisplayTime; } _FORCE_INLINE_ XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; } _FORCE_INLINE_ bool can_render() { - ERR_ON_RENDER_THREAD_V(false); return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && frame_state.shouldRender; } diff --git a/platform/ios/keyboard_input_view.mm b/platform/ios/keyboard_input_view.mm index 8b614662b7..4067701a41 100644 --- a/platform/ios/keyboard_input_view.mm +++ b/platform/ios/keyboard_input_view.mm @@ -149,23 +149,18 @@ return; } + NSString *substringToDelete = nil; if (self.previousSelectedRange.length == 0) { - // We are deleting all text before cursor if no range was selected. - // This way any inserted or changed text will be updated. - NSString *substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location]; - [self deleteText:substringToDelete.length]; + // Get previous text to delete. + substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location]; } else { - // If text was previously selected - // we are sending only one `backspace`. - // It will remove all text from text input. + // If text was previously selected we are sending only one `backspace`. It will remove all text from text input. [self deleteText:1]; } - NSString *substringToEnter; - + NSString *substringToEnter = nil; if (self.selectedRange.length == 0) { - // If previous cursor had a selection - // we have to calculate an inserted text. + // If previous cursor had a selection we have to calculate an inserted text. if (self.previousSelectedRange.length != 0) { NSInteger rangeEnd = self.selectedRange.location + self.selectedRange.length; NSInteger rangeStart = MIN(self.previousSelectedRange.location, self.selectedRange.location); @@ -187,7 +182,18 @@ substringToEnter = [self.text substringWithRange:self.selectedRange]; } - [self enterText:substringToEnter]; + NSInteger skip = 0; + if (substringToDelete != nil) { + for (NSInteger i = 0; i < MIN([substringToDelete length], [substringToEnter length]); i++) { + if ([substringToDelete characterAtIndex:i] == [substringToEnter characterAtIndex:i]) { + skip++; + } else { + break; + } + } + [self deleteText:[substringToDelete length] - skip]; // Delete changed part of previous text. + } + [self enterText:[substringToEnter substringFromIndex:skip]]; // Enter changed part of new text. self.previousText = self.text; self.previousSelectedRange = self.selectedRange; diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 3534c1afee..a67428b9a4 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -374,6 +374,12 @@ void JoypadLinux::open_joypad(const char *p_path) { name = namebuf; } + for (const String &word : name.to_lower().split(" ")) { + if (banned_words.has(word)) { + return; + } + } + if (ioctl(fd, EVIOCGID, &inpid) < 0) { close(fd); return; diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h index 26a9908d4e..bf24d8e5a5 100644 --- a/platform/linuxbsd/joypad_linux.h +++ b/platform/linuxbsd/joypad_linux.h @@ -94,6 +94,21 @@ private: Vector<String> attached_devices; + // List of lowercase words that will prevent the controller from being recognized if its name matches. + // This is done to prevent trackpads, graphics tablets and motherboard LED controllers from being + // recognized as controllers (and taking up controller ID slots as a result). + // Only whole words are matched within the controller name string. The match is case-insensitive. + const Vector<String> banned_words = { + "touchpad", // Matches e.g. "SynPS/2 Synaptics TouchPad", "Sony Interactive Entertainment DualSense Wireless Controller Touchpad" + "trackpad", + "clickpad", + "keyboard", // Matches e.g. "PG-90215 Keyboard", "Usb Keyboard Usb Keyboard Consumer Control" + "mouse", // Matches e.g. "Mouse passthrough" + "pen", // Matches e.g. "Wacom One by Wacom S Pen" + "finger", // Matches e.g. "Wacom HID 495F Finger" + "led", // Matches e.g. "ASRock LED Controller" + }; + static void monitor_joypads_thread_func(void *p_user); void monitor_joypads_thread_run(); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index c1b3540f68..750e8bb54c 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2009,7 +2009,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) } if (p_mode == WINDOW_MODE_WINDOWED) { - ShowWindow(wd.hWnd, SW_RESTORE); + ShowWindow(wd.hWnd, SW_NORMAL); wd.maximized = false; wd.minimized = false; } diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index b3f735e044..014419573c 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -574,7 +574,7 @@ StringName AnimatedSprite2D::get_animation() const { PackedStringArray AnimatedSprite2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); if (frames.is_null()) { - warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite2D to display frames.")); + warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Sprite Frames\" property in order for AnimatedSprite2D to display frames.")); } return warnings; } diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp index b3586a1da0..9dd9d4a376 100644 --- a/scene/2d/parallax_2d.cpp +++ b/scene/2d/parallax_2d.cpp @@ -31,6 +31,7 @@ #include "parallax_2d.h" #include "core/config/project_settings.h" +#include "scene/main/viewport.h" void Parallax2D::_notification(int p_what) { switch (p_what) { @@ -72,7 +73,11 @@ void Parallax2D::_validate_property(PropertyInfo &p_property) const { void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) { if (!ignore_camera_scroll) { - set_screen_offset(p_adj_screen_pos); + if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { + set_screen_offset((p_adj_screen_pos + Vector2(0.5, 0.5)).floor()); + } else { + set_screen_offset(p_adj_screen_pos); + } } } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index d1f1c97ca2..48ade1e5cc 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -996,6 +996,7 @@ TileMap::TileMap() { base_property_helper.register_property(PropertyInfo(Variant::INT, "z_index"), defaults->get_z_index(), &TileMap::set_layer_z_index, &TileMap::get_layer_z_index); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "navigation_enabled"), defaults->is_navigation_enabled(), &TileMap::set_layer_navigation_enabled, &TileMap::is_layer_navigation_enabled); base_property_helper.register_property(PropertyInfo(Variant::PACKED_INT32_ARRAY, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Vector<int>(), &TileMap::_set_layer_tile_data, &TileMap::_get_tile_map_data_using_compatibility_format); + PropertyListHelper::register_base_helper(&base_property_helper); memdelete(defaults); } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 8ac585719c..50218a6d86 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1435,7 +1435,7 @@ StringName AnimatedSprite3D::get_animation() const { PackedStringArray AnimatedSprite3D::get_configuration_warnings() const { PackedStringArray warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { - warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); + warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Sprite Frames\" property in order for AnimatedSprite3D to display frames.")); } return warnings; } diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index c9372525bd..8047369ab1 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -1350,6 +1350,7 @@ void FileDialog::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults.name, &FileDialog::set_option_name, &FileDialog::get_option_name); base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &FileDialog::set_option_values, &FileDialog::get_option_values); base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &FileDialog::set_option_default, &FileDialog::get_option_default); + PropertyListHelper::register_base_helper(&base_property_helper); } void FileDialog::set_show_hidden_files(bool p_show) { diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 933b4df6e3..bf16c0699e 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1905,6 +1905,7 @@ void ItemList::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &ItemList::set_item_icon, &ItemList::get_item_icon); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "selectable"), defaults.selectable, &ItemList::set_item_selectable, &ItemList::is_item_selectable); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &ItemList::set_item_disabled, &ItemList::is_item_disabled); + PropertyListHelper::register_base_helper(&base_property_helper); } ItemList::ItemList() { diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 998f99b2f9..e99187d283 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -198,6 +198,7 @@ void MenuButton::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator); + PropertyListHelper::register_base_helper(&base_property_helper); } void MenuButton::set_disable_shortcuts(bool p_disabled) { diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index da15b44bdc..a1425fb847 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -577,6 +577,7 @@ void OptionButton::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &OptionButton::_dummy_setter, &OptionButton::get_item_id); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &OptionButton::_dummy_setter, &OptionButton::is_item_disabled); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &OptionButton::_dummy_setter, &OptionButton::is_item_separator); + PropertyListHelper::register_base_helper(&base_property_helper); } void OptionButton::set_disable_shortcuts(bool p_disabled) { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 7f795ea710..f62421061b 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -2824,6 +2824,7 @@ void PopupMenu::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &PopupMenu::set_item_id, &PopupMenu::get_item_id); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &PopupMenu::set_item_disabled, &PopupMenu::is_item_disabled); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &PopupMenu::set_item_as_separator, &PopupMenu::is_item_separator); + PropertyListHelper::register_base_helper(&base_property_helper); } void PopupMenu::popup(const Rect2i &p_bounds) { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 8ffa0f8c63..5ef02bf19d 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2098,7 +2098,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { handled = true; } if (k->is_action("ui_down", true) && vscroll->is_visible_in_tree()) { - vscroll->scroll(vscroll->get_value() + theme_cache.normal_font->get_height(theme_cache.normal_font_size)); + vscroll->scroll(theme_cache.normal_font->get_height(theme_cache.normal_font_size)); handled = true; } if (k->is_action("ui_home", true) && vscroll->is_visible_in_tree()) { @@ -4947,10 +4947,10 @@ void RichTextLabel::append_text(const String &p_bbcode) { tag_stack.push_front("outline_size"); } else if (bbcode_name == "fade") { - int start_index = 0; + int start_index = brk_pos; OptionMap::Iterator start_option = bbcode_options.find("start"); if (start_option) { - start_index = start_option->value.to_int(); + start_index += start_option->value.to_int(); } int length = 10; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index ddc757c452..1ae18f5728 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -1873,6 +1873,7 @@ void TabBar::_bind_methods() { base_property_helper.register_property(PropertyInfo(Variant::STRING, "tooltip"), defaults.tooltip, &TabBar::set_tab_tooltip, &TabBar::get_tab_tooltip); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabBar::set_tab_icon, &TabBar::get_tab_icon); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabBar::set_tab_disabled, &TabBar::is_tab_disabled); + PropertyListHelper::register_base_helper(&base_property_helper); } TabBar::TabBar() { diff --git a/scene/property_list_helper.cpp b/scene/property_list_helper.cpp index ce258ee8c3..f840aaa759 100644 --- a/scene/property_list_helper.cpp +++ b/scene/property_list_helper.cpp @@ -30,6 +30,19 @@ #include "property_list_helper.h" +Vector<PropertyListHelper *> PropertyListHelper::base_helpers; // static + +void PropertyListHelper::clear_base_helpers() { // static + for (PropertyListHelper *helper : base_helpers) { + helper->clear(); + } + base_helpers.clear(); +} + +void PropertyListHelper::register_base_helper(PropertyListHelper *p_helper) { // static + base_helpers.push_back(p_helper); +} + const PropertyListHelper::Property *PropertyListHelper::_get_property(const String &p_property, int *r_index) const { const Vector<String> components = p_property.rsplit("/", true, 1); if (components.size() < 2 || !components[0].begins_with(prefix)) { @@ -176,9 +189,8 @@ bool PropertyListHelper::property_get_revert(const String &p_property, Variant & return false; } -PropertyListHelper::~PropertyListHelper() { - // No object = it's the main helper. Do a cleanup. - if (!object && is_initialized()) { +void PropertyListHelper::clear() { + if (is_initialized()) { memdelete(array_length_getter); for (const KeyValue<String, Property> &E : property_list) { @@ -187,5 +199,6 @@ PropertyListHelper::~PropertyListHelper() { memdelete(E.value.getter); } } + property_list.clear(); } } diff --git a/scene/property_list_helper.h b/scene/property_list_helper.h index 6bc65f6e3e..1ab923e76d 100644 --- a/scene/property_list_helper.h +++ b/scene/property_list_helper.h @@ -42,6 +42,8 @@ class PropertyListHelper { MethodBind *getter = nullptr; }; + static Vector<PropertyListHelper *> base_helpers; + String prefix; MethodBind *array_length_getter = nullptr; HashMap<String, Property> property_list; @@ -53,6 +55,9 @@ class PropertyListHelper { int _call_array_length_getter() const; public: + static void clear_base_helpers(); + static void register_base_helper(PropertyListHelper *p_helper); + void set_prefix(const String &p_prefix); template <typename G> void set_array_length_getter(G p_array_length_getter) { @@ -83,7 +88,7 @@ public: bool property_can_revert(const String &p_property) const; bool property_get_revert(const String &p_property, Variant &r_value) const; - ~PropertyListHelper(); + void clear(); }; #endif // PROPERTY_LIST_HELPER_H diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 0dc6d16050..7ab150c141 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -741,6 +741,7 @@ void AudioStreamRandomizer::_bind_methods() { base_property_helper.set_array_length_getter(&AudioStreamRandomizer::get_streams_count); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), defaults.stream, &AudioStreamRandomizer::set_stream, &AudioStreamRandomizer::get_stream); base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), defaults.weight, &AudioStreamRandomizer::set_stream_probability_weight, &AudioStreamRandomizer::get_stream_probability_weight); + PropertyListHelper::register_base_helper(&base_property_helper); } AudioStreamRandomizer::AudioStreamRandomizer() { 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 0d83264bfb..af190207db 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1018,11 +1018,6 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Draw Sky } - // rendering effects - if (ce_has_pre_transparent) { - _process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT, p_render_data); - } - if (merge_transparent_pass) { if (render_list[RENDER_LIST_ALPHA].element_info.size() > 0) { // transparent pass @@ -1058,6 +1053,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass + // rendering effects + if (ce_has_pre_transparent) { + _process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT, p_render_data); + } + if (scene_state.used_screen_texture) { // Copy screen texture to backbuffer so we can read from it _render_buffers_copy_screen_texture(p_render_data); diff --git a/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl b/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl index 4f81e36c58..0332e23993 100644 --- a/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl @@ -50,7 +50,7 @@ layout(r16f, set = 2, binding = 3) uniform restrict writeonly image2DArray dest_ vec4 screen_space_to_view_space_depth(vec4 p_depth) { if (params.orthogonal) { vec4 depth = p_depth * 2.0 - 1.0; - return ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0; } float depth_linearize_mul = params.z_near; @@ -68,7 +68,7 @@ vec4 screen_space_to_view_space_depth(vec4 p_depth) { float screen_space_to_view_space_depth(float p_depth) { if (params.orthogonal) { float depth = p_depth * 2.0 - 1.0; - return ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / (2.0 * params.z_far); + return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0; } float depth_linearize_mul = params.z_near; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 801ad1b825..c65d3bec95 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -3500,7 +3500,12 @@ Error RenderingDevice::screen_prepare_for_drawing(DisplayServer::WindowID p_scre framebuffer = driver->swap_chain_acquire_framebuffer(main_queue, it->value, resize_required); } - ERR_FAIL_COND_V_MSG(framebuffer.id == 0, FAILED, "Unable to acquire framebuffer."); + if (framebuffer.id == 0) { + // Some drivers like NVIDIA are fast enough to invalidate the swap chain between resizing and acquisition (GH-94104). + // This typically occurs during continuous window resizing operations, especially if done quickly. + // Allow this to fail silently since it has no visual consequences. + return ERR_CANT_CREATE; + } // Store the framebuffer that will be used next to draw to this screen. screen_framebuffers[p_screen] = framebuffer; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index f5e0b811a2..0b1595d988 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -39,6 +39,8 @@ #define HAS_WARNING(flag) (warning_flags & flag) +int ShaderLanguage::instance_counter = 0; + String ShaderLanguage::get_operator_text(Operator p_op) { static const char *op_names[OP_MAX] = { "==", "!=", @@ -10812,17 +10814,16 @@ ShaderLanguage::ShaderLanguage() { nodes = nullptr; completion_class = TAG_GLOBAL; - int idx = 0; - while (builtin_func_defs[idx].name) { - if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) { - const StringName &name = StringName(builtin_func_defs[idx].name); - - if (!global_func_set.has(name)) { - global_func_set.insert(name); + if (instance_counter == 0) { + int idx = 0; + while (builtin_func_defs[idx].name) { + if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) { + global_func_set.insert(builtin_func_defs[idx].name); } + idx++; } - idx++; } + instance_counter++; #ifdef DEBUG_ENABLED warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants); @@ -10837,5 +10838,8 @@ ShaderLanguage::ShaderLanguage() { ShaderLanguage::~ShaderLanguage() { clear(); - global_func_set.clear(); + instance_counter--; + if (instance_counter == 0) { + global_func_set.clear(); + } } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index edac819a1e..076bd8def4 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -800,6 +800,8 @@ public: static bool is_control_flow_keyword(String p_keyword); static void get_builtin_funcs(List<String> *r_keywords); + static int instance_counter; + struct BuiltInInfo { DataType type = TYPE_VOID; bool constant = false; diff --git a/servers/rendering_server.h b/servers/rendering_server.h index e15dba4353..693c822488 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -44,14 +44,6 @@ // Helper macros for code outside of the rendering server, but that is // called by the rendering server. #ifdef DEBUG_ENABLED -#define ERR_ON_RENDER_THREAD \ - RenderingServer *rendering_server = RenderingServer::get_singleton(); \ - ERR_FAIL_NULL(rendering_server); \ - ERR_FAIL_COND(rendering_server->is_on_render_thread()); -#define ERR_ON_RENDER_THREAD_V(m_ret) \ - RenderingServer *rendering_server = RenderingServer::get_singleton(); \ - ERR_FAIL_NULL_V(rendering_server, m_ret); \ - ERR_FAIL_COND_V(rendering_server->is_on_render_thread(), m_ret); #define ERR_NOT_ON_RENDER_THREAD \ RenderingServer *rendering_server = RenderingServer::get_singleton(); \ ERR_FAIL_NULL(rendering_server); \ @@ -61,8 +53,6 @@ ERR_FAIL_NULL_V(rendering_server, m_ret); \ ERR_FAIL_COND_V(!rendering_server->is_on_render_thread(), m_ret); #else -#define ERR_ON_RENDER_THREAD -#define ERR_ON_RENDER_THREAD_V(m_ret) #define ERR_NOT_ON_RENDER_THREAD #define ERR_NOT_ON_RENDER_THREAD_V(m_ret) #endif diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h index a9bc2e9b99..f8c5ef279d 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -93,9 +93,9 @@ void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) { Basis res = to_rotation.inverse() * rotation_from_computed_euler; - CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_column(0))).utf8().ptr()); - CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_column(1))).utf8().ptr()); - CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_column(0)))); + CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_column(1)))); + CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2)))); // Double check `to_rotation` decomposing with XYZ rotation order. const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(EulerOrder::XYZ); @@ -103,13 +103,13 @@ void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) { res = to_rotation.inverse() * rotation_from_xyz_computed_euler; - CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_column(0))).utf8().ptr()); - CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_column(1))).utf8().ptr()); - CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_column(2))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_column(0)))); + CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_column(1)))); + CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_column(2)))); - INFO(vformat("Rotation order: %s\n.", get_rot_order_name(rot_order)).utf8().ptr()); - INFO(vformat("Original Rotation: %s\n", String(deg_original_euler)).utf8().ptr()); - INFO(vformat("Quaternion to rotation order: %s\n", String(rad2deg(euler_from_rotation))).utf8().ptr()); + INFO(vformat("Rotation order: %s\n.", get_rot_order_name(rot_order))); + INFO(vformat("Original Rotation: %s\n", String(deg_original_euler))); + INFO(vformat("Quaternion to rotation order: %s\n", String(rad2deg(euler_from_rotation)))); } TEST_CASE("[Basis] Euler conversions") { diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index 358bbc08a3..c1aa39031d 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -405,7 +405,7 @@ void validate_argument(const Context &p_context, const ExposedClass &p_class, co err_msg += " " + type_error_msg; } - TEST_COND(!arg_defval_assignable_to_type, err_msg.utf8().get_data()); + TEST_COND(!arg_defval_assignable_to_type, err_msg); } } @@ -590,7 +590,7 @@ void add_exposed_classes(Context &r_context) { exposed_class.name, method.name); TEST_FAIL_COND_WARN( (exposed_class.name != r_context.names_cache.object_class || String(method.name) != "free"), - warn_msg.utf8().get_data()); + warn_msg); } else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { method.return_type.name = return_info.class_name; @@ -720,7 +720,7 @@ void add_exposed_classes(Context &r_context) { "Signal name conflicts with %s: '%s.%s.", method_conflict ? "method" : "property", class_name, signal.name); TEST_FAIL_COND((method_conflict || exposed_class.find_method_by_name(signal.name)), - warn_msg.utf8().get_data()); + warn_msg); exposed_class.signals_.push_back(signal); } |