diff options
32 files changed, 796 insertions, 167 deletions
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index bb8bf9f569..226076029b 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -217,34 +217,48 @@ Transform2D Transform2D::operator*(const Transform2D &p_transform) const {  	return t;  } -Transform2D Transform2D::scaled(const Size2 &p_scale) const { +Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const {  	Transform2D copy = *this; -	copy.scale(p_scale); +	copy.scale_basis(p_scale);  	return copy;  } -Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { +Transform2D Transform2D::scaled(const Size2 &p_scale) const { +	// Equivalent to left multiplication  	Transform2D copy = *this; -	copy.scale_basis(p_scale); +	copy.scale(p_scale);  	return copy;  } +Transform2D Transform2D::scaled_local(const Size2 &p_scale) const { +	// Equivalent to right multiplication +	return Transform2D(columns[0] * p_scale.x, columns[1] * p_scale.y, columns[2]); +} +  Transform2D Transform2D::untranslated() const {  	Transform2D copy = *this;  	copy.columns[2] = Vector2();  	return copy;  } +Transform2D Transform2D::translated(const Vector2 &p_offset) const { +	// Equivalent to left multiplication +	return Transform2D(columns[0], columns[1], columns[2] + p_offset); +} +  Transform2D Transform2D::translated_local(const Vector2 &p_offset) const { -	Transform2D copy = *this; -	copy.translate_local(p_offset); -	return copy; +	// Equivalent to right multiplication +	return Transform2D(columns[0], columns[1], columns[2] + basis_xform(p_offset));  }  Transform2D Transform2D::rotated(const real_t p_angle) const { -	Transform2D copy = *this; -	copy.rotate(p_angle); -	return copy; +	// Equivalent to left multiplication +	return Transform2D(p_angle, Vector2()) * (*this); +} + +Transform2D Transform2D::rotated_local(const real_t p_angle) const { +	// Equivalent to right multiplication +	return (*this) * Transform2D(p_angle, Vector2()); // Could be optimized, because origin transform can be skipped.  }  real_t Transform2D::basis_determinant() const { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index e64d050f0c..f23f32867a 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -85,10 +85,13 @@ struct _NO_DISCARD_ Transform2D {  	_FORCE_INLINE_ const Vector2 &get_origin() const { return columns[2]; }  	_FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { columns[2] = p_origin; } -	Transform2D scaled(const Size2 &p_scale) const;  	Transform2D basis_scaled(const Size2 &p_scale) const; +	Transform2D scaled(const Size2 &p_scale) const; +	Transform2D scaled_local(const Size2 &p_scale) const; +	Transform2D translated(const Vector2 &p_offset) const;  	Transform2D translated_local(const Vector2 &p_offset) const;  	Transform2D rotated(const real_t p_angle) const; +	Transform2D rotated_local(const real_t p_angle) const;  	Transform2D untranslated() const; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index c497a276f3..a634faca9a 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -62,7 +62,15 @@ void Transform3D::rotate(const Vector3 &p_axis, real_t p_angle) {  }  Transform3D Transform3D::rotated(const Vector3 &p_axis, real_t p_angle) const { -	return Transform3D(Basis(p_axis, p_angle), Vector3()) * (*this); +	// Equivalent to left multiplication +	Basis p_basis(p_axis, p_angle); +	return Transform3D(p_basis * basis, p_basis.xform(origin)); +} + +Transform3D Transform3D::rotated_local(const Vector3 &p_axis, real_t p_angle) const { +	// Equivalent to right multiplication +	Basis p_basis(p_axis, p_angle); +	return Transform3D(basis * p_basis, origin);  }  void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_angle) { @@ -120,9 +128,13 @@ void Transform3D::scale(const Vector3 &p_scale) {  }  Transform3D Transform3D::scaled(const Vector3 &p_scale) const { -	Transform3D t = *this; -	t.scale(p_scale); -	return t; +	// Equivalent to left multiplication +	return Transform3D(basis.scaled(p_scale), origin * p_scale); +} + +Transform3D Transform3D::scaled_local(const Vector3 &p_scale) const { +	// Equivalent to right multiplication +	return Transform3D(basis.scaled_local(p_scale), origin);  }  void Transform3D::scale_basis(const Vector3 &p_scale) { @@ -139,10 +151,14 @@ void Transform3D::translate_local(const Vector3 &p_translation) {  	}  } +Transform3D Transform3D::translated(const Vector3 &p_translation) const { +	// Equivalent to left multiplication +	return Transform3D(basis, origin + p_translation); +} +  Transform3D Transform3D::translated_local(const Vector3 &p_translation) const { -	Transform3D t = *this; -	t.translate_local(p_translation); -	return t; +	// Equivalent to right multiplication +	return Transform3D(basis, origin + basis.xform(p_translation));  }  void Transform3D::orthonormalize() { diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index 1f8026043f..b572e90859 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -46,6 +46,7 @@ struct _NO_DISCARD_ Transform3D {  	Transform3D affine_inverse() const;  	Transform3D rotated(const Vector3 &p_axis, real_t p_angle) const; +	Transform3D rotated_local(const Vector3 &p_axis, real_t p_angle) const;  	void rotate(const Vector3 &p_axis, real_t p_angle);  	void rotate_basis(const Vector3 &p_axis, real_t p_angle); @@ -55,9 +56,11 @@ struct _NO_DISCARD_ Transform3D {  	void scale(const Vector3 &p_scale);  	Transform3D scaled(const Vector3 &p_scale) const; +	Transform3D scaled_local(const Vector3 &p_scale) const;  	void scale_basis(const Vector3 &p_scale);  	void translate_local(real_t p_tx, real_t p_ty, real_t p_tz);  	void translate_local(const Vector3 &p_translation); +	Transform3D translated(const Vector3 &p_translation) const;  	Transform3D translated_local(const Vector3 &p_translation) const;  	const Basis &get_basis() const { return basis; } diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index d67315f20d..9790cc44e3 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -963,8 +963,11 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro  	ERR_FAIL_COND(!type->property_map.has(p_property));  	ERR_FAIL_COND(!type->property_map.has(p_linked_property)); -	PropertyInfo &pinfo = type->property_map[p_property]; -	pinfo.linked_properties.push_back(p_linked_property); +	if (!type->linked_properties.has(p_property)) { +		type->linked_properties.insert(p_property, List<StringName>()); +	} +	type->linked_properties[p_property].push_back(p_linked_property); +  #endif  } @@ -992,6 +995,25 @@ void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p  	}  } +void ClassDB::get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance) { +#ifdef TOOLS_ENABLED +	ClassInfo *check = classes.getptr(p_class); +	while (check) { +		if (!check->linked_properties.has(p_property)) { +			return; +		} +		for (const StringName &E : check->linked_properties[p_property]) { +			r_properties->push_back(E); +		} + +		if (p_no_inheritance) { +			break; +		} +		check = check->inherits_ptr; +	} +#endif +} +  bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {  	OBJTYPE_RLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index 8b6a260d86..5fba52e23e 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -120,6 +120,7 @@ public:  		List<MethodInfo> virtual_methods;  		HashMap<StringName, MethodInfo> virtual_methods_map;  		HashMap<StringName, Vector<Error>> method_error_values; +		HashMap<StringName, List<StringName>> linked_properties;  #endif  		HashMap<StringName, PropertySetGet> property_setget; @@ -312,6 +313,7 @@ public:  	static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property);  	static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);  	static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); +	static void get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance = false);  	static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr);  	static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value);  	static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); diff --git a/core/object/object.h b/core/object/object.h index 649f42c9a0..35d0aaaa7d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -154,9 +154,7 @@ struct PropertyInfo {  	String hint_string;  	uint32_t usage = PROPERTY_USAGE_DEFAULT; -#ifdef TOOLS_ENABLED -	Vector<String> linked_properties; -#endif +	// If you are thinking about adding another member to this class, ask the maintainer (Juan) first.  	_FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const {  		PropertyInfo pi = *this; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index b933a90a48..a774aac52f 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1882,7 +1882,10 @@ static void _register_variant_builtin_methods() {  	bind_method(Transform2D, get_skew, sarray(), varray());  	bind_method(Transform2D, orthonormalized, sarray(), varray());  	bind_method(Transform2D, rotated, sarray("angle"), varray()); +	bind_method(Transform2D, rotated_local, sarray("angle"), varray());  	bind_method(Transform2D, scaled, sarray("scale"), varray()); +	bind_method(Transform2D, scaled_local, sarray("scale"), varray()); +	bind_method(Transform2D, translated, sarray("offset"), varray());  	bind_method(Transform2D, translated_local, sarray("offset"), varray());  	bind_method(Transform2D, basis_xform, sarray("v"), varray());  	bind_method(Transform2D, basis_xform_inv, sarray("v"), varray()); @@ -1947,7 +1950,10 @@ static void _register_variant_builtin_methods() {  	bind_method(Transform3D, affine_inverse, sarray(), varray());  	bind_method(Transform3D, orthonormalized, sarray(), varray());  	bind_method(Transform3D, rotated, sarray("axis", "angle"), varray()); +	bind_method(Transform3D, rotated_local, sarray("axis", "angle"), varray());  	bind_method(Transform3D, scaled, sarray("scale"), varray()); +	bind_method(Transform3D, scaled_local, sarray("scale"), varray()); +	bind_method(Transform3D, translated, sarray("offset"), varray());  	bind_method(Transform3D, translated_local, sarray("offset"), varray());  	bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));  	bind_method(Transform3D, spherical_interpolate_with, sarray("xform", "weight"), varray()); diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 3aedbbd1e6..5595abc02a 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -113,7 +113,7 @@  			<argument index="2" name="z_near" type="float" />  			<argument index="3" name="z_far" type="float" />  			<description> -				Sets the camera projection to frustum mode (see [constant PROJECTION_FRUSTUM]), by specifying a [code]size[/code], an [code]offset[/code], and the [code]z_near[/code] and [code]z_far[/code] clip planes in world space units. +				Sets the camera projection to frustum mode (see [constant PROJECTION_FRUSTUM]), by specifying a [code]size[/code], an [code]offset[/code], and the [code]z_near[/code] and [code]z_far[/code] clip planes in world space units. See also [member frustum_offset].  			</description>  		</method>  		<method name="set_orthogonal"> @@ -179,6 +179,7 @@  		</member>  		<member name="frustum_offset" type="Vector2" setter="set_frustum_offset" getter="get_frustum_offset" default="Vector2(0, 0)">  			The camera's frustum offset. This can be changed from the default to create "tilted frustum" effects such as [url=https://zdoom.org/wiki/Y-shearing]Y-shearing[/url]. +			[b]Note:[/b] Only effective if [member projection] is [constant PROJECTION_FRUSTUM].  		</member>  		<member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset" default="0.0">  			The horizontal (X) offset of the camera viewport. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b1e3d2f628..ae0ec64c27 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1962,9 +1962,11 @@  			Lower-end override for [member rendering/shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support.  		</member>  		<member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3"> +			The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level].  		</member>  		<member name="rendering/textures/default_filters/anisotropic_filtering_level" type="int" setter="" getter="" default="2">  			Sets the maximum number of samples to take when using anisotropic filtering on textures (as a power of two). A higher sample count will result in sharper textures at oblique angles, but is more expensive to compute. A value of [code]0[/code] forcibly disables anisotropic filtering, even on materials where it is enabled. +			The anisotropic filtering level also affects decals and light projectors if they are configured to use anisotropic filtering. See [member rendering/textures/decals/filter] and [member rendering/textures/light_projectors/filter].  			[b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time.  		</member>  		<member name="rendering/textures/default_filters/texture_mipmap_bias" type="float" setter="" getter="" default="0.0"> @@ -1977,6 +1979,7 @@  			[b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time.  		</member>  		<member name="rendering/textures/light_projectors/filter" type="int" setter="" getter="" default="3"> +			The filtering quality to use for [OmniLight3D] and [SpotLight3D] projectors. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level].  		</member>  		<member name="rendering/textures/lossless_compression/force_png" type="bool" setter="" getter="" default="false">  			If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 9616ab3515..9a398b1f33 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3747,14 +3747,22 @@  			Use [Transform3D] to store MultiMesh transform.  		</constant>  		<constant name="LIGHT_PROJECTOR_FILTER_NEAREST" value="0" enum="LightProjectorFilter"> +			Nearest-neighbor filter for light projectors (use for pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps.  		</constant> -		<constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS" value="1" enum="LightProjectorFilter"> +		<constant name="LIGHT_PROJECTOR_FILTER_LINEAR" value="1" enum="LightProjectorFilter"> +			Linear filter for light projectors (use for non-pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as using mipmaps.  		</constant> -		<constant name="LIGHT_PROJECTOR_FILTER_LINEAR" value="2" enum="LightProjectorFilter"> +		<constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS" value="2" enum="LightProjectorFilter"> +			Nearest-neighbor filter for light projectors (use for pixel art light projectors). Isotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps.  		</constant>  		<constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS" value="3" enum="LightProjectorFilter"> +			Linear filter for light projectors (use for non-pixel art light projectors). Isotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps.  		</constant> -		<constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="4" enum="LightProjectorFilter"> +		<constant name="LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC" value="4" enum="LightProjectorFilter"> +			Nearest-neighbor filter for light projectors (use for pixel art light projectors). Anisotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. +		</constant> +		<constant name="LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="5" enum="LightProjectorFilter"> +			Linear filter for light projectors (use for non-pixel art light projectors). Anisotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].  		</constant>  		<constant name="LIGHT_DIRECTIONAL" value="0" enum="LightType">  			Is a directional (sun) light. @@ -3896,14 +3904,22 @@  		<constant name="DECAL_TEXTURE_MAX" value="4" enum="DecalTexture">  		</constant>  		<constant name="DECAL_FILTER_NEAREST" value="0" enum="DecalFilter"> +			Nearest-neighbor filter for decals (use for pixel art decals). No mipmaps are used for rendering, which means decals at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps.  		</constant> -		<constant name="DECAL_FILTER_NEAREST_MIPMAPS" value="1" enum="DecalFilter"> +		<constant name="DECAL_FILTER_LINEAR" value="1" enum="DecalFilter"> +			Linear filter for decals (use for non-pixel art decals). No mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as using mipmaps.  		</constant> -		<constant name="DECAL_FILTER_LINEAR" value="2" enum="DecalFilter"> +		<constant name="DECAL_FILTER_NEAREST_MIPMAPS" value="2" enum="DecalFilter"> +			Nearest-neighbor filter for decals (use for pixel art decals). Isotropic mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps.  		</constant>  		<constant name="DECAL_FILTER_LINEAR_MIPMAPS" value="3" enum="DecalFilter"> +			Linear filter for decals (use for non-pixel art decals). Isotropic mipmaps are used for rendering, which means decals at a distance will look smooth but blurry. This has roughly the same performance cost as not using mipmaps. +		</constant> +		<constant name="DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC" value="4" enum="DecalFilter"> +			Nearest-neighbor filter for decals (use for pixel art decals). Anisotropic mipmaps are used for rendering, which means decals at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].  		</constant> -		<constant name="DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="4" enum="DecalFilter"> +		<constant name="DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC" value="5" enum="DecalFilter"> +			Linear filter for decals (use for non-pixel art decals). Anisotropic mipmaps are used for rendering, which means decals at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].  		</constant>  		<constant name="VOXEL_GI_QUALITY_LOW" value="0" enum="VoxelGIQuality">  		</constant> diff --git a/doc/classes/RootMotionView.xml b/doc/classes/RootMotionView.xml index 88b8f2cd03..3f3b00e2cb 100644 --- a/doc/classes/RootMotionView.xml +++ b/doc/classes/RootMotionView.xml @@ -5,25 +5,25 @@  	</brief_description>  	<description>  		[i]Root motion[/i] refers to an animation technique where a mesh's skeleton is used to give impulse to a character. When working with 3D animations, a popular technique is for animators to use the root skeleton bone to give motion to the rest of the skeleton. This allows animating characters in a way where steps actually match the floor below. It also allows precise interaction with objects during cinematics. See also [AnimationTree]. -		[b]Note:[/b] [RootMotionView] is only visible in the editor. It will be hidden automatically in the running project, and will also be converted to a plain [Node] in the running project. This means a script attached to a [RootMotionView] node [i]must[/i] have [code]extends Node[/code] instead of [code]extends RootMotionView[/code]. Additionally, it must not be a [code]@tool[/code] script. +		[b]Note:[/b] [RootMotionView] is only visible in the editor. It will be hidden automatically in the running project.  	</description>  	<tutorials>  		<link title="Using AnimationTree - Root motion">$DOCS_URL/tutorials/animation/animation_tree.html#root-motion</link>  	</tutorials>  	<members> -		<member name="animation_path" type="NodePath" setter="set_animation_path" getter="get_animation_path"> +		<member name="animation_path" type="NodePath" setter="set_animation_path" getter="get_animation_path" default="NodePath("")">  			Path to an [AnimationTree] node to use as a basis for root motion.  		</member> -		<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size"> +		<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="1.0">  			The grid's cell size in 3D units.  		</member> -		<member name="color" type="Color" setter="set_color" getter="get_color"> +		<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0.5, 0.5, 1, 1)">  			The grid's color.  		</member> -		<member name="radius" type="float" setter="set_radius" getter="get_radius"> +		<member name="radius" type="float" setter="set_radius" getter="get_radius" default="10.0">  			The grid's radius in 3D units. The grid's opacity will fade gradually as the distance from the origin increases until this [member radius] is reached.  		</member> -		<member name="zero_y" type="bool" setter="set_zero_y" getter="get_zero_y"> +		<member name="zero_y" type="bool" setter="set_zero_y" getter="get_zero_y" default="true">  			If [code]true[/code], the grid's points will all be on the same Y coordinate ([i]local[/i] Y = 0). If [code]false[/code], the points' original Y coordinate is preserved.  		</member>  	</members> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 924b4cd8e4..9979a73527 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -141,14 +141,40 @@  			<return type="Transform2D" />  			<argument index="0" name="angle" type="float" />  			<description> -				Returns a copy of the transform rotated by the given [code]angle[/code] (in radians), using matrix multiplication. +				Returns a copy of the transform rotated by the given [code]angle[/code] (in radians). +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method> +		<method name="rotated_local" qualifiers="const"> +			<return type="Transform2D" /> +			<argument index="0" name="angle" type="float" /> +			<description> +				Returns a copy of the transform rotated by the given [code]angle[/code] (in radians). +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding rotation transform [code]R[/code] from the right, i.e., [code]X * R[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  		<method name="scaled" qualifiers="const">  			<return type="Transform2D" />  			<argument index="0" name="scale" type="Vector2" />  			<description> -				Returns a copy of the transform scaled by the given [code]scale[/code] factor, using matrix multiplication. +				Returns a copy of the transform scaled by the given [code]scale[/code] factor. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding scaling transform [code]S[/code] from the left, i.e., [code]S * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method> +		<method name="scaled_local" qualifiers="const"> +			<return type="Transform2D" /> +			<argument index="0" name="scale" type="Vector2" /> +			<description> +				Returns a copy of the transform scaled by the given [code]scale[/code] factor. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding scaling transform [code]S[/code] from the right, i.e., [code]X * S[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  		<method name="set_rotation"> @@ -173,12 +199,24 @@  				Sets the transform's skew (in radians).  			</description>  		</method> +		<method name="translated" qualifiers="const"> +			<return type="Transform2D" /> +			<argument index="0" name="offset" type="Vector2" /> +			<description> +				Returns a copy of the transform translated by the given [code]offset[/code]. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding translation transform [code]T[/code] from the left, i.e., [code]T * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method>  		<method name="translated_local" qualifiers="const">  			<return type="Transform2D" />  			<argument index="0" name="offset" type="Vector2" />  			<description> -				Returns a copy of the transform translated by the given [code]offset[/code], relative to the transform's basis vectors. -				Unlike [method rotated] and [method scaled], this does not use matrix multiplication. +				Returns a copy of the transform translated by the given [code]offset[/code]. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding translation transform [code]T[/code] from the right, i.e., [code]X * T[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  	</methods> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index de1db718c2..9b673701ae 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -102,14 +102,43 @@  			<argument index="0" name="axis" type="Vector3" />  			<argument index="1" name="angle" type="float" />  			<description> -				Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians), using matrix multiplication. The [code]axis[/code] must be a normalized vector. +				Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians). +				The [code]axis[/code] must be a normalized vector. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method> +		<method name="rotated_local" qualifiers="const"> +			<return type="Transform3D" /> +			<argument index="0" name="axis" type="Vector3" /> +			<argument index="1" name="angle" type="float" /> +			<description> +				Returns a copy of the transform rotated around the given [code]axis[/code] by the given [code]angle[/code] (in radians). +				The [code]axis[/code] must be a normalized vector. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding rotation transform [code]R[/code] from the right, i.e., [code]X * R[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  		<method name="scaled" qualifiers="const">  			<return type="Transform3D" />  			<argument index="0" name="scale" type="Vector3" />  			<description> -				Returns a copy of the transform with its basis and origin scaled by the given [code]scale[/code] factor, using matrix multiplication. +				Returns a copy of the transform scaled by the given [code]scale[/code] factor. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding scaling transform [code]S[/code] from the left, i.e., [code]S * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method> +		<method name="scaled_local" qualifiers="const"> +			<return type="Transform3D" /> +			<argument index="0" name="scale" type="Vector3" /> +			<description> +				Returns a copy of the transform scaled by the given [code]scale[/code] factor. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding scaling transform [code]S[/code] from the right, i.e., [code]X * S[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  		<method name="spherical_interpolate_with" qualifiers="const"> @@ -120,12 +149,24 @@  				Returns a transform spherically interpolated between this transform and another by a given [code]weight[/code] (on the range of 0.0 to 1.0).  			</description>  		</method> +		<method name="translated" qualifiers="const"> +			<return type="Transform3D" /> +			<argument index="0" name="offset" type="Vector3" /> +			<description> +				Returns a copy of the transform translated by the given [code]offset[/code]. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding translation transform [code]T[/code] from the left, i.e., [code]T * X[/code]. +				This can be seen as transforming with respect to the global/parent frame. +			</description> +		</method>  		<method name="translated_local" qualifiers="const">  			<return type="Transform3D" />  			<argument index="0" name="offset" type="Vector3" />  			<description> -				Returns a copy of the transform translated by the given [code]offset[/code], relative to the transform's basis vectors. -				Unlike [method rotated] and [method scaled], this does not use matrix multiplication. +				Returns a copy of the transform translated by the given [code]offset[/code]. +				This method is an optimized version of multiplying the given transform [code]X[/code] +				with a corresponding translation transform [code]T[/code] from the right, i.e., [code]X * T[/code]. +				This can be seen as transforming with respect to the local frame.  			</description>  		</method>  	</methods> diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index e06e3cbc5f..9f047c775e 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1677,7 +1677,7 @@ void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index  	Ref<InputEventMouseButton> mb = p_event;  	if (mb.is_valid()) { -		if (mb->get_button_index() == MouseButton::RIGHT) { +		if (movable && mb->get_button_index() == MouseButton::RIGHT) {  			popup_array_index_pressed = begin_array_index + p_index;  			rmb_popup->set_item_disabled(OPTION_MOVE_UP, popup_array_index_pressed == 0);  			rmb_popup->set_item_disabled(OPTION_MOVE_DOWN, popup_array_index_pressed == count - 1); @@ -1712,43 +1712,112 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {  		}  	} else if (mode == MODE_USE_COUNT_PROPERTY) {  		ERR_FAIL_COND(p_to_pos < -1 || p_to_pos > count); -		List<PropertyInfo> object_property_list; -		object->get_property_list(&object_property_list); -		Array properties_as_array = _extract_properties_as_array(object_property_list); -		properties_as_array.resize(count); +		if (!swap_method.is_empty()) { +			ERR_FAIL_COND(!object->has_method(swap_method)); -		// For undoing things -		undo_redo->add_undo_property(object, count_property, properties_as_array.size()); -		for (int i = 0; i < (int)properties_as_array.size(); i++) { -			Dictionary d = Dictionary(properties_as_array[i]); -			Array keys = d.keys(); -			for (int j = 0; j < keys.size(); j++) { -				String key = keys[j]; -				undo_redo->add_undo_property(object, vformat(key, i), d[key]); -			} -		} +			// Swap method was provided, use it. +			if (p_element_index < 0) { +				// Add an element at position +				undo_redo->add_do_property(object, count_property, count + 1); +				if (p_to_pos >= 0) { +					for (int i = count; i > p_to_pos; i--) { +						undo_redo->add_do_method(object, swap_method, i, i - 1); +					} +					for (int i = p_to_pos; i < count; i++) { +						undo_redo->add_undo_method(object, swap_method, i, i + 1); +					} +				} +				undo_redo->add_undo_property(object, count_property, count); + +			} else if (p_to_pos < 0) { +				if (count > 0) { +					// Remove element at position +					undo_redo->add_undo_property(object, count_property, count); + +					List<PropertyInfo> object_property_list; +					object->get_property_list(&object_property_list); + +					for (int i = p_element_index; i < count - 1; i++) { +						undo_redo->add_do_method(object, swap_method, i, i + 1); +					} + +					for (int i = count; i > p_element_index; i--) { +						undo_redo->add_undo_method(object, swap_method, i, i - 1); +					} + +					String erase_prefix = String(array_element_prefix) + itos(p_element_index); + +					for (const PropertyInfo &E : object_property_list) { +						if (E.name.begins_with(erase_prefix)) { +							undo_redo->add_undo_property(object, E.name, object->get(E.name)); +						} +					} -		if (p_element_index < 0) { -			// Add an element. -			properties_as_array.insert(p_to_pos < 0 ? properties_as_array.size() : p_to_pos, Dictionary()); -		} else if (p_to_pos < 0) { -			// Delete the element. -			properties_as_array.remove_at(p_element_index); +					undo_redo->add_do_property(object, count_property, count - 1); +				} +			} else { +				if (p_to_pos > p_element_index) { +					p_to_pos--; +				} + +				if (p_to_pos < p_element_index) { +					for (int i = p_element_index; i > p_to_pos; i--) { +						undo_redo->add_do_method(object, swap_method, i, i - 1); +					} +					for (int i = p_to_pos; i < p_element_index; i++) { +						undo_redo->add_undo_method(object, swap_method, i, i + 1); +					} +				} else if (p_to_pos > p_element_index) { +					for (int i = p_element_index; i < p_to_pos; i++) { +						undo_redo->add_do_method(object, swap_method, i, i + 1); +					} + +					for (int i = p_to_pos; i > p_element_index; i--) { +						undo_redo->add_undo_method(object, swap_method, i, i - 1); +					} +				} +			}  		} else { -			// Move the element. -			properties_as_array.insert(p_to_pos, properties_as_array[p_element_index].duplicate()); -			properties_as_array.remove_at(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); -		} +			// Use standard properties. +			List<PropertyInfo> object_property_list; +			object->get_property_list(&object_property_list); -		// Change the array size then set the properties. -		undo_redo->add_do_property(object, count_property, properties_as_array.size()); -		for (int i = 0; i < (int)properties_as_array.size(); i++) { -			Dictionary d = properties_as_array[i]; -			Array keys = d.keys(); -			for (int j = 0; j < keys.size(); j++) { -				String key = keys[j]; -				undo_redo->add_do_property(object, vformat(key, i), d[key]); +			Array properties_as_array = _extract_properties_as_array(object_property_list); +			properties_as_array.resize(count); + +			// For undoing things +			undo_redo->add_undo_property(object, count_property, properties_as_array.size()); +			for (int i = 0; i < (int)properties_as_array.size(); i++) { +				Dictionary d = Dictionary(properties_as_array[i]); +				Array keys = d.keys(); +				for (int j = 0; j < keys.size(); j++) { +					String key = keys[j]; +					undo_redo->add_undo_property(object, vformat(key, i), d[key]); +				} +			} + +			if (p_element_index < 0) { +				// Add an element. +				properties_as_array.insert(p_to_pos < 0 ? properties_as_array.size() : p_to_pos, Dictionary()); +			} else if (p_to_pos < 0) { +				// Delete the element. +				properties_as_array.remove_at(p_element_index); +			} else { +				// Move the element. +				properties_as_array.insert(p_to_pos, properties_as_array[p_element_index].duplicate()); +				properties_as_array.remove_at(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); +			} + +			// Change the array size then set the properties. +			undo_redo->add_do_property(object, count_property, properties_as_array.size()); +			for (int i = 0; i < (int)properties_as_array.size(); i++) { +				Dictionary d = properties_as_array[i]; +				Array keys = d.keys(); +				for (int j = 0; j < keys.size(); j++) { +					String key = keys[j]; +					undo_redo->add_do_property(object, vformat(key, i), d[key]); +				}  			}  		}  	} @@ -1988,6 +2057,20 @@ void EditorInspectorArray::_setup() {  		page = CLAMP(page, 0, max_page);  	} +	Ref<Font> numbers_font; +	int numbers_min_w = 0; + +	if (numbered) { +		numbers_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); +		int digits_found = count; +		String test; +		while (digits_found) { +			test += "8"; +			digits_found /= 10; +		} +		numbers_min_w = numbers_font->get_string_size(test).width; +	} +  	for (int i = 0; i < (int)array_elements.size(); i++) {  		ArrayElement &ae = array_elements[i]; @@ -2022,19 +2105,38 @@ void EditorInspectorArray::_setup() {  		ae.margin->add_child(ae.hbox);  		// Move button. -		ae.move_texture_rect = memnew(TextureRect); -		ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); -		ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE); -		if (is_inside_tree()) { -			ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); +		if (movable) { +			ae.move_texture_rect = memnew(TextureRect); +			ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); +			ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE); + +			if (is_inside_tree()) { +				ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); +			} +			ae.hbox->add_child(ae.move_texture_rect); +		} + +		if (numbered) { +			ae.number = memnew(Label); +			ae.number->add_theme_font_override("font", numbers_font); +			ae.number->set_custom_minimum_size(Size2(numbers_min_w, 0)); +			ae.number->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); +			ae.number->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); +			ae.number->set_text(itos(begin_array_index + i)); +			ae.hbox->add_child(ae.number);  		} -		ae.hbox->add_child(ae.move_texture_rect);  		// Right vbox.  		ae.vbox = memnew(VBoxContainer);  		ae.vbox->set_h_size_flags(SIZE_EXPAND_FILL);  		ae.vbox->set_v_size_flags(SIZE_EXPAND_FILL);  		ae.hbox->add_child(ae.vbox); + +		ae.erase = memnew(Button); +		ae.erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); +		ae.erase->set_v_size_flags(SIZE_SHRINK_CENTER); +		ae.erase->connect("pressed", callable_mp(this, &EditorInspectorArray::_remove_item).bind(begin_array_index + i)); +		ae.hbox->add_child(ae.erase);  	}  	// Hide/show the add button. @@ -2049,7 +2151,14 @@ void EditorInspectorArray::_setup() {  	}  } +void EditorInspectorArray::_remove_item(int p_index) { +	_move_element(p_index, -1); +} +  Variant EditorInspectorArray::get_drag_data_fw(const Point2 &p_point, Control *p_from) { +	if (!movable) { +		return Variant(); +	}  	int index = p_from->get_meta("index");  	Dictionary dict;  	dict["type"] = "property_array_element"; @@ -2071,6 +2180,9 @@ void EditorInspectorArray::drop_data_fw(const Point2 &p_point, const Variant &p_  }  bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { +	if (!movable) { +		return false; +	}  	// First, update drawing.  	control_dropping->update(); @@ -2100,13 +2212,19 @@ void EditorInspectorArray::_notification(int p_what) {  			for (int i = 0; i < (int)array_elements.size(); i++) {  				ArrayElement &ae = array_elements[i]; -				ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); +				if (ae.move_texture_rect) { +					ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); +				}  				Size2 min_size = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->get_minimum_size();  				ae.margin->add_theme_constant_override("margin_left", min_size.x / 2);  				ae.margin->add_theme_constant_override("margin_top", min_size.y / 2);  				ae.margin->add_theme_constant_override("margin_right", min_size.x / 2);  				ae.margin->add_theme_constant_override("margin_bottom", min_size.y / 2); + +				if (ae.erase) { +					ae.erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); +				}  			}  			add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -2142,23 +2260,31 @@ void EditorInspectorArray::set_undo_redo(UndoRedo *p_undo_redo) {  	undo_redo = p_undo_redo;  } -void EditorInspectorArray::setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable) { +void EditorInspectorArray::setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable, bool p_numbered, int p_page_length, const String &p_add_item_text) {  	count_property = "";  	mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION;  	array_element_prefix = p_array_element_prefix;  	page = p_page; +	movable = p_movable; +	page_length = p_page_length; +	numbered = p_numbered;  	EditorInspectorSection::setup(String(p_array_element_prefix) + "_array", p_label, p_object, p_bg_color, p_foldable, 0);  	_setup();  } -void EditorInspectorArray::setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable) { +void EditorInspectorArray::setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable, bool p_numbered, int p_page_length, const String &p_add_item_text, const String &p_swap_method) {  	count_property = p_count_property;  	mode = MODE_USE_COUNT_PROPERTY;  	array_element_prefix = p_array_element_prefix;  	page = p_page; +	movable = p_movable; +	page_length = p_page_length; +	numbered = p_numbered; +	swap_method = p_swap_method; +	add_button->set_text(p_add_item_text);  	EditorInspectorSection::setup(String(count_property) + "_array", p_label, p_object, p_bg_color, p_foldable, 0);  	_setup(); @@ -2887,9 +3013,35 @@ void EditorInspector::update_tree() {  			StringName array_element_prefix;  			Color c = sscolor;  			c.a /= level; + +			Vector<String> class_name_components = String(p.class_name).split(","); + +			array_element_prefix = class_name_components[1]; +			int page_size = 5; +			bool movable = true; +			bool numbered = false; +			bool foldable = use_folding; +			String add_button_text; +			String swap_method; +			for (int i = (p.type == Variant::NIL ? 1 : 2); i < class_name_components.size(); i++) { +				if (class_name_components[i].begins_with("page_size") && class_name_components[i].get_slice_count("=") == 2) { +					page_size = class_name_components[i].get_slice("=", 1).to_int(); +				} else if (class_name_components[i].begins_with("add_button_text") && class_name_components[i].get_slice_count("=") == 2) { +					add_button_text = class_name_components[i].get_slice("=", 1).strip_edges(); +				} else if (class_name_components[i] == "static") { +					movable = false; +				} else if (class_name_components[i] == "numbered") { +					numbered = true; +				} else if (class_name_components[i] == "unfoldable") { +					foldable = false; +				} else if (class_name_components[i].begins_with("swap_method") && class_name_components[i].get_slice_count("=") == 2) { +					swap_method = class_name_components[i].get_slice("=", 1).strip_edges(); +				} +			} +  			if (p.type == Variant::NIL) {  				// Setup the array to use a method to create/move/delete elements. -				array_element_prefix = p.class_name; +				array_element_prefix = class_name_components[0];  				editor_inspector_array = memnew(EditorInspectorArray);  				String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; @@ -2900,13 +3052,14 @@ void EditorInspector::update_tree() {  				editor_inspector_array->set_undo_redo(undo_redo);  			} else if (p.type == Variant::INT) {  				// Setup the array to use the count property and built-in functions to create/move/delete elements. -				Vector<String> class_name_components = String(p.class_name).split(","); -				if (class_name_components.size() == 2) { -					array_element_prefix = class_name_components[1]; + +				if (class_name_components.size() > 2) {  					editor_inspector_array = memnew(EditorInspectorArray);  					int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; -					editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, use_folding); + +					editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, foldable, movable, numbered, page_size, add_button_text, swap_method);  					editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix)); +  					editor_inspector_array->set_undo_redo(undo_redo);  				}  			} @@ -2915,6 +3068,7 @@ void EditorInspector::update_tree() {  				current_vbox->add_child(editor_inspector_array);  				editor_inspector_array_per_prefix[array_element_prefix] = editor_inspector_array;  			} +  			continue;  		} @@ -3396,14 +3550,23 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo  			undo_redo->add_undo_property(object, p_name, value);  		} -		PropertyInfo prop_info; -		if (ClassDB::get_property_info(object->get_class_name(), p_name, &prop_info)) { -			for (const String &linked_prop : prop_info.linked_properties) { -				valid = false; -				value = object->get(linked_prop, &valid); -				if (valid) { -					undo_redo->add_undo_property(object, linked_prop, value); -				} +		List<StringName> linked_properties; +		ClassDB::get_linked_properties_info(object->get_class_name(), p_name, &linked_properties); + +		for (const StringName &linked_prop : linked_properties) { +			valid = false; +			Variant undo_value = object->get(linked_prop, &valid); +			if (valid) { +				undo_redo->add_undo_property(object, linked_prop, undo_value); +			} +		} + +		PackedStringArray linked_properties_dynamic = object->call("_get_linked_undo_properties", p_name, p_value); +		for (int i = 0; i < linked_properties_dynamic.size(); i++) { +			valid = false; +			Variant undo_value = object->get(linked_properties_dynamic[i], &valid); +			if (valid) { +				undo_redo->add_undo_property(object, linked_properties_dynamic[i], undo_value);  			}  		} diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 54533de960..9b5e295854 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -321,6 +321,7 @@ class EditorInspectorArray : public EditorInspectorSection {  	} mode;  	StringName count_property;  	StringName array_element_prefix; +	String swap_method;  	int count = 0; @@ -342,6 +343,9 @@ class EditorInspectorArray : public EditorInspectorSection {  	int begin_array_index = 0;  	int end_array_index = 0; +	bool movable = true; +	bool numbered = false; +  	enum MenuOptions {  		OPTION_MOVE_UP = 0,  		OPTION_MOVE_DOWN, @@ -359,7 +363,9 @@ class EditorInspectorArray : public EditorInspectorSection {  		MarginContainer *margin = nullptr;  		HBoxContainer *hbox = nullptr;  		TextureRect *move_texture_rect = nullptr; +		Label *number = nullptr;  		VBoxContainer *vbox = nullptr; +		Button *erase = nullptr;  	};  	LocalVector<ArrayElement> array_elements; @@ -395,6 +401,8 @@ class EditorInspectorArray : public EditorInspectorSection {  	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);  	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; +	void _remove_item(int p_index); +  protected:  	void _notification(int p_what);  	static void _bind_methods(); @@ -402,8 +410,8 @@ protected:  public:  	void set_undo_redo(UndoRedo *p_undo_redo); -	void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable); -	void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable); +	void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = ""); +	void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");  	VBoxContainer *get_vbox(int p_index);  	EditorInspectorArray(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ce885032a3..5e00304122 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6157,8 +6157,6 @@ EditorNode::EditorNode() {  	register_exporters(); -	ClassDB::set_class_enabled("RootMotionView", true); -  	EDITOR_DEF("interface/editor/save_on_focus_loss", false);  	EDITOR_DEF("interface/editor/show_update_spinner", false);  	EDITOR_DEF("interface/editor/update_continuously", false); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index b8413f1e16..68d097eb4e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -297,7 +297,9 @@ namespace Godot          }          /// <summary> -        /// Rotates the transform by <paramref name="angle"/> (in radians), using matrix multiplication. +        /// Rotates the transform by <paramref name="angle"/> (in radians). +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left.          /// </summary>          /// <param name="angle">The angle to rotate, in radians.</param>          /// <returns>The rotated transformation matrix.</returns> @@ -307,7 +309,21 @@ namespace Godot          }          /// <summary> -        /// Scales the transform by the given scaling factor, using matrix multiplication. +        /// Rotates the transform by <paramref name="angle"/> (in radians). +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right. +        /// </summary> +        /// <param name="angle">The angle to rotate, in radians.</param> +        /// <returns>The rotated transformation matrix.</returns> +        public Transform2D RotatedLocal(real_t angle) +        { +            return new Transform2D(angle, new Vector2()) * this; +        } + +        /// <summary> +        /// Scales the transform by the given scaling factor. +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left.          /// </summary>          /// <param name="scale">The scale to introduce.</param>          /// <returns>The scaled transformation matrix.</returns> @@ -320,6 +336,21 @@ namespace Godot              return copy;          } +        /// <summary> +        /// Scales the transform by the given scaling factor. +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right. +        /// </summary> +        /// <param name="scale">The scale to introduce.</param> +        /// <returns>The scaled transformation matrix.</returns> +        public Transform2D ScaledLocal(Vector2 scale) +        { +            Transform2D copy = this; +            copy.x *= scale; +            copy.y *= scale; +            return copy; +        } +          private real_t Tdotx(Vector2 with)          {              return (this[0, 0] * with[0]) + (this[1, 0] * with[1]); @@ -331,11 +362,23 @@ namespace Godot          }          /// <summary> -        /// Translates the transform by the given <paramref name="offset"/>, -        /// relative to the transform's basis vectors. -        /// -        /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, -        /// this does not use matrix multiplication. +        /// Translates the transform by the given <paramref name="offset"/>. +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left. +        /// </summary> +        /// <param name="offset">The offset to translate by.</param> +        /// <returns>The translated matrix.</returns> +        public Transform2D Translated(Vector2 offset) +        { +            Transform2D copy = this; +            copy.origin += offset; +            return copy; +        } + +        /// <summary> +        /// Translates the transform by the given <paramref name="offset"/>. +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right.          /// </summary>          /// <param name="offset">The offset to translate by.</param>          /// <returns>The translated matrix.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 97b4a87713..c00b9d8e9b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -186,8 +186,10 @@ namespace Godot          }          /// <summary> -        /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians), -        /// using matrix multiplication. The axis must be a normalized vector. +        /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians). +        /// The axis must be a normalized vector. +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left.          /// </summary>          /// <param name="axis">The axis to rotate around. Must be normalized.</param>          /// <param name="angle">The angle to rotate, in radians.</param> @@ -198,7 +200,24 @@ namespace Godot          }          /// <summary> -        /// Scales the transform by the given 3D scaling factor, using matrix multiplication. +        /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians). +        /// The axis must be a normalized vector. +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right. +        /// </summary> +        /// <param name="axis">The axis to rotate around. Must be normalized.</param> +        /// <param name="angle">The angle to rotate, in radians.</param> +        /// <returns>The rotated transformation matrix.</returns> +        public Transform3D RotatedLocal(Vector3 axis, real_t angle) +        { +            Basis tmpBasis = new Basis(axis, angle); +            return new Transform3D(basis * tmpBasis, origin); +        } + +        /// <summary> +        /// Scales the transform by the given 3D <paramref name="scale"/> factor. +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left.          /// </summary>          /// <param name="scale">The scale to introduce.</param>          /// <returns>The scaled transformation matrix.</returns> @@ -207,6 +226,19 @@ namespace Godot              return new Transform3D(basis.Scaled(scale), origin * scale);          } +        /// <summary> +        /// Scales the transform by the given 3D <paramref name="scale"/> factor. +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right. +        /// </summary> +        /// <param name="scale">The scale to introduce.</param> +        /// <returns>The scaled transformation matrix.</returns> +        public Transform3D ScaledLocal(Vector3 scale) +        { +            Basis tmpBasis = new Basis(new Vector3(scale.x, 0, 0), new Vector3(0, scale.y, 0), new Vector3(0, 0, scale.z)); +            return new Transform3D(basis * tmpBasis, origin); +        } +          private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)          {              // Make rotation matrix @@ -231,11 +263,21 @@ namespace Godot          }          /// <summary> -        /// Translates the transform by the given <paramref name="offset"/>, -        /// relative to the transform's basis vectors. -        /// -        /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, -        /// this does not use matrix multiplication. +        /// Translates the transform by the given <paramref name="offset"/>. +        /// The operation is done in the parent/global frame, equivalent to +        /// multiplying the matrix from the left. +        /// </summary> +        /// <param name="offset">The offset to translate by.</param> +        /// <returns>The translated matrix.</returns> +        public Transform3D Translated(Vector3 offset) +        { +            return new Transform3D(basis, origin + offset); +        } + +        /// <summary> +        /// Translates the transform by the given <paramref name="offset"/>. +        /// The operation is done in the local frame, equivalent to +        /// multiplying the matrix from the right.          /// </summary>          /// <param name="offset">The offset to translate by.</param>          /// <returns>The translated matrix.</returns> diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6617bd1726..ea9788de27 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -653,21 +653,6 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE  	return err;  } -template <typename... VarArgs> -Error Node::rpc(const StringName &p_method, VarArgs... p_args) { -	return rpc_id(0, p_method, p_args...); -} - -template <typename... VarArgs> -Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { -	Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. -	const Variant *argptrs[sizeof...(p_args) + 1]; -	for (uint32_t i = 0; i < sizeof...(p_args); i++) { -		argptrs[i] = &args[i]; -	} -	return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); -} -  Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {  	ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);  	return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); diff --git a/scene/main/node.h b/scene/main/node.h index 0645c68eb9..ccd1d561d2 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -512,4 +512,22 @@ VARIANT_ENUM_CAST(Node::DuplicateFlags);  typedef HashSet<Node *, Node::Comparator> NodeSet; +// Template definitions must be in the header so they are always fully initialized before their usage. +// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file + +template <typename... VarArgs> +Error Node::rpc(const StringName &p_method, VarArgs... p_args) { +	return rpc_id(0, p_method, p_args...); +} + +template <typename... VarArgs> +Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { +	Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. +	const Variant *argptrs[sizeof...(p_args) + 1]; +	for (uint32_t i = 0; i < sizeof...(p_args); i++) { +		argptrs[i] = &args[i]; +	} +	return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); +} +  #endif // NODE_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0878a9f78f..d7fcd500b2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -523,9 +523,7 @@ void register_scene_types() {  	GDREGISTER_CLASS(GPUParticlesAttractorVectorField3D);  	GDREGISTER_CLASS(CPUParticles3D);  	GDREGISTER_CLASS(Position3D); -  	GDREGISTER_CLASS(RootMotionView); -	ClassDB::set_class_enabled("RootMotionView", false); // disabled by default, enabled by editor  	OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index e0918b17c7..4b2e029f47 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -1650,7 +1650,7 @@ void ParticlesMaterial::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height");  	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius");  	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); -	ADD_GROUP("ParticleFlags", "particle_flag_"); +	ADD_GROUP("Particle Flags", "particle_flag_");  	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY);  	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y);  	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 827cce6047..c3af0868e0 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -2265,15 +2265,18 @@ void RenderForwardClustered::_update_render_base_uniform_set() {  				case RS::DECAL_FILTER_NEAREST: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; -				case RS::DECAL_FILTER_NEAREST_MIPMAPS: { -					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); -				} break;  				case RS::DECAL_FILTER_LINEAR: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::DECAL_FILTER_NEAREST_MIPMAPS: { +					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::DECAL_FILTER_LINEAR_MIPMAPS: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { +					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; @@ -2292,15 +2295,18 @@ void RenderForwardClustered::_update_render_base_uniform_set() {  				case RS::LIGHT_PROJECTOR_FILTER_NEAREST: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; -				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { -					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); -				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { +					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { +					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: {  					sampler = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; @@ -3275,12 +3281,18 @@ void RenderForwardClustered::_update_shader_quality_settings() {  	sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;  	sc.constant_id = SPEC_CONSTANT_DECAL_FILTER; -	sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; +	sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || +			decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || +			decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || +			decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;  	spec_constants.push_back(sc);  	sc.constant_id = SPEC_CONSTANT_PROJECTOR_FILTER; -	sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; +	sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;  	spec_constants.push_back(sc); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 15f810fb3b..f74ad7617d 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1214,15 +1214,18 @@ void RenderForwardMobile::_update_render_base_uniform_set() {  				case RS::DECAL_FILTER_NEAREST: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; -				case RS::DECAL_FILTER_NEAREST_MIPMAPS: { -					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); -				} break;  				case RS::DECAL_FILTER_LINEAR: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::DECAL_FILTER_NEAREST_MIPMAPS: { +					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::DECAL_FILTER_LINEAR_MIPMAPS: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { +					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; @@ -1241,15 +1244,18 @@ void RenderForwardMobile::_update_render_base_uniform_set() {  				case RS::LIGHT_PROJECTOR_FILTER_NEAREST: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; -				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { -					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); -				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { +					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; +				case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { +					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); +				} break;  				case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: {  					sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);  				} break; @@ -2558,12 +2564,18 @@ void RenderForwardMobile::_update_shader_quality_settings() {  	sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;  	sc.constant_id = SPEC_CONSTANT_DECAL_USE_MIPMAPS; -	sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; +	sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS || +			decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS || +			decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || +			decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;  	spec_constants.push_back(sc);  	sc.constant_id = SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS; -	sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC; +	sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC || +			light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;  	spec_constants.push_back(sc); diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index d85ab3af2e..246ef81cb2 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -182,17 +182,18 @@ void main() {  	if (found) {  		float margin_blend = 1.0; -		vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.5 * 0.05); // make a uniform margin -		if (any(bvec4(lessThan(pos, -margin), greaterThan(pos, params.screen_size + margin)))) { -			// clip outside screen + margin +		vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // make a uniform margin +		if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, params.screen_size)))) { +			// clip at the screen edges  			imageStore(ssr_image, ssC, vec4(0.0));  			return;  		}  		{ -			//blend fading out towards external margin -			vec2 margin_grad = mix(pos - params.screen_size, -pos, lessThan(pos, vec2(0.0))); -			margin_blend = 1.0 - smoothstep(0.0, margin.x, max(margin_grad.x, margin_grad.y)); +			//blend fading out towards inner margin +			// 0.5 = midpoint of reflection +			vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5)); +			margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);  			//margin_blend = 1.0;  		} diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 7de175647b..019f10fe38 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -7158,9 +7158,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			if (!n) {  				return ERR_PARSE_ERROR;  			} -			if (n->get_datatype() != TYPE_INT) { -				_set_error(RTR("Expected an integer expression.")); -				return ERR_PARSE_ERROR; +			{ +				const ShaderLanguage::DataType switch_type = n->get_datatype(); +				if (switch_type != TYPE_INT && switch_type != TYPE_UINT) { +					_set_error(RTR("Expected an integer expression.")); +					return ERR_PARSE_ERROR; +				}  			}  			tk = _get_token();  			if (tk.type != TK_PARENTHESIS_CLOSE) { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index bb76281782..e02d82fbc3 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1885,9 +1885,10 @@ void RenderingServer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("light_projectors_set_filter", "filter"), &RenderingServer::light_projectors_set_filter);  	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST); -	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS);  	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR); +	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS);  	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS); +	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC);  	BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC);  	BIND_ENUM_CONSTANT(LIGHT_DIRECTIONAL); @@ -1989,9 +1990,10 @@ void RenderingServer::_bind_methods() {  	BIND_ENUM_CONSTANT(DECAL_TEXTURE_MAX);  	BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST); -	BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS);  	BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR); +	BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS);  	BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR_MIPMAPS); +	BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC);  	BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC);  	/* GI API (affects VoxelGI and SDFGI) */ @@ -2956,9 +2958,9 @@ void RenderingServer::init() {  					PROPERTY_HINT_RANGE, "-2,2,0.001"));  	GLOBAL_DEF("rendering/textures/decals/filter", DECAL_FILTER_LINEAR_MIPMAPS); -	ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/decals/filter", PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Nearest+Mipmaps,Linear,Linear+Mipmaps,Linear+Mipmaps Anisotropic (Slow)")); +	ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/decals/filter", PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)"));  	GLOBAL_DEF("rendering/textures/light_projectors/filter", LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS); -	ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/light_projectors/filter", PropertyInfo(Variant::INT, "rendering/textures/light_projectors/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Nearest+Mipmaps,Linear,Linear+Mipmaps,Linear+Mipmaps Anisotropic (Slow)")); +	ProjectSettings::get_singleton()->set_custom_property_info("rendering/textures/light_projectors/filter", PropertyInfo(Variant::INT, "rendering/textures/light_projectors/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)"));  	GLOBAL_DEF_RST("rendering/occlusion_culling/occlusion_rays_per_thread", 512);  	GLOBAL_DEF_RST("rendering/occlusion_culling/bvh_build_quality", 2); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 1f2012ac8d..845a4a7a3e 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -495,9 +495,10 @@ public:  	enum LightProjectorFilter {  		LIGHT_PROJECTOR_FILTER_NEAREST, -		LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS,  		LIGHT_PROJECTOR_FILTER_LINEAR, +		LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS,  		LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS, +		LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC,  		LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC,  	}; @@ -557,9 +558,10 @@ public:  	enum DecalFilter {  		DECAL_FILTER_NEAREST, -		DECAL_FILTER_NEAREST_MIPMAPS,  		DECAL_FILTER_LINEAR, +		DECAL_FILTER_NEAREST_MIPMAPS,  		DECAL_FILTER_LINEAR_MIPMAPS, +		DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC,  		DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC,  	}; diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h new file mode 100644 index 0000000000..697bf63fc5 --- /dev/null +++ b/tests/core/math/test_transform_2d.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/*  test_transform_2d.h                                                  */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef TEST_TRANSFORM_2D_H +#define TEST_TRANSFORM_2D_H + +#include "core/math/transform_2d.h" + +#include "tests/test_macros.h" + +namespace TestTransform2D { + +Transform2D create_dummy_transform() { +	return Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)); +} + +Transform2D identity() { +	return Transform2D(); +} + +TEST_CASE("[Transform2D] translation") { +	Vector2 offset = Vector2(1, 2); + +	// Both versions should give the same result applied to identity. +	CHECK(identity().translated(offset) == identity().translated_local(offset)); + +	// Check both versions against left and right multiplications. +	Transform2D orig = create_dummy_transform(); +	Transform2D T = identity().translated(offset); +	CHECK(orig.translated(offset) == T * orig); +	CHECK(orig.translated_local(offset) == orig * T); +} + +TEST_CASE("[Transform2D] scaling") { +	Vector2 scaling = Vector2(1, 2); + +	// Both versions should give the same result applied to identity. +	CHECK(identity().scaled(scaling) == identity().scaled_local(scaling)); + +	// Check both versions against left and right multiplications. +	Transform2D orig = create_dummy_transform(); +	Transform2D S = identity().scaled(scaling); +	CHECK(orig.scaled(scaling) == S * orig); +	CHECK(orig.scaled_local(scaling) == orig * S); +} + +TEST_CASE("[Transform2D] rotation") { +	real_t phi = 1.0; + +	// Both versions should give the same result applied to identity. +	CHECK(identity().rotated(phi) == identity().rotated_local(phi)); + +	// Check both versions against left and right multiplications. +	Transform2D orig = create_dummy_transform(); +	Transform2D R = identity().rotated(phi); +	CHECK(orig.rotated(phi) == R * orig); +	CHECK(orig.rotated_local(phi) == orig * R); +} +} // namespace TestTransform2D + +#endif // TEST_TRANSFORM_2D_H diff --git a/tests/core/math/test_transform_3d.h b/tests/core/math/test_transform_3d.h new file mode 100644 index 0000000000..da166b43f7 --- /dev/null +++ b/tests/core/math/test_transform_3d.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/*  test_transform_3d.h                                                  */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef TEST_TRANSFORM_3D_H +#define TEST_TRANSFORM_3D_H + +#include "core/math/transform_3d.h" + +#include "tests/test_macros.h" + +namespace TestTransform3D { + +Transform3D create_dummy_transform() { +	return Transform3D(Basis(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9)), Vector3(10, 11, 12)); +} + +Transform3D identity() { +	return Transform3D(); +} + +TEST_CASE("[Transform3D] translation") { +	Vector3 offset = Vector3(1, 2, 3); + +	// Both versions should give the same result applied to identity. +	CHECK(identity().translated(offset) == identity().translated_local(offset)); + +	// Check both versions against left and right multiplications. +	Transform3D orig = create_dummy_transform(); +	Transform3D T = identity().translated(offset); +	CHECK(orig.translated(offset) == T * orig); +	CHECK(orig.translated_local(offset) == orig * T); +} + +TEST_CASE("[Transform3D] scaling") { +	Vector3 scaling = Vector3(1, 2, 3); + +	// Both versions should give the same result applied to identity. +	CHECK(identity().scaled(scaling) == identity().scaled_local(scaling)); + +	// Check both versions against left and right multiplications. +	Transform3D orig = create_dummy_transform(); +	Transform3D S = identity().scaled(scaling); +	CHECK(orig.scaled(scaling) == S * orig); +	CHECK(orig.scaled_local(scaling) == orig * S); +} + +TEST_CASE("[Transform3D] rotation") { +	Vector3 axis = Vector3(1, 2, 3).normalized(); +	real_t phi = 1.0; + +	// Both versions should give the same result applied to identity. +	CHECK(identity().rotated(axis, phi) == identity().rotated_local(axis, phi)); + +	// Check both versions against left and right multiplications. +	Transform3D orig = create_dummy_transform(); +	Transform3D R = identity().rotated(axis, phi); +	CHECK(orig.rotated(axis, phi) == R * orig); +	CHECK(orig.rotated_local(axis, phi) == orig * R); +} +} // namespace TestTransform3D + +#endif // TEST_TRANSFORM_3D_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 16654181c6..d533ce4f63 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -49,6 +49,8 @@  #include "tests/core/math/test_random_number_generator.h"  #include "tests/core/math/test_rect2.h"  #include "tests/core/math/test_rect2i.h" +#include "tests/core/math/test_transform_2d.h" +#include "tests/core/math/test_transform_3d.h"  #include "tests/core/math/test_vector2.h"  #include "tests/core/math/test_vector2i.h"  #include "tests/core/math/test_vector3.h"  |