diff --git a/core/math/basis.h b/core/math/basis.h index adacd1c21697..860cb9cbb00a 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -189,6 +189,10 @@ struct _NO_DISCARD_ Basis { rows[2].zero(); } + _FORCE_INLINE_ Basis rotate_toward(Basis p_to_basis, real_t p_delta) const { + return Basis(get_rotation_quaternion().rotate_toward(p_to_basis.get_rotation_quaternion(), p_delta)); + } + _FORCE_INLINE_ Basis transpose_xform(const Basis &m) const { return Basis( rows[0].x * m[0].x + rows[1].x * m[1].x + rows[2].x * m[2].x, diff --git a/core/math/quaternion.h b/core/math/quaternion.h index ea952304a523..f12e7cf85396 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -85,6 +85,22 @@ struct _NO_DISCARD_ Quaternion { r_axis.z = z * r; } + _FORCE_INLINE_ Quaternion rotate_toward(const Quaternion &p_to, real_t p_delta) const { + Quaternion to = p_to; + if (Math::is_zero_approx(p_delta)) { + return *this; + } + if (p_delta < 0.0) { + p_delta = -p_delta; + to = p_to.inverse(); + } + real_t angle = this->angle_to(p_to); + if (angle < p_delta) { + return to; + } + return this->slerp(to, p_delta / angle); + } + void operator*=(const Quaternion &p_q); Quaternion operator*(const Quaternion &p_q) const; diff --git a/core/math/vector2.h b/core/math/vector2.h index b9d7709acd07..7bf24e55205f 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -114,6 +114,25 @@ struct _NO_DISCARD_ Vector2 { _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; _FORCE_INLINE_ Vector2 bezier_derivative(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; + _FORCE_INLINE_ Vector2 rotate_toward(Vector2 p_to, real_t p_delta) const { + if (Math::is_zero_approx(p_delta)) { + return *this; + } + + if (p_delta < 0.0f) { + p_delta = -p_delta; + p_to = -p_to; + } + + real_t angle = angle_to(p_to); + + if (angle < p_delta) { + return p_to; + } + + return slerp(p_to, p_delta / angle); + } + Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; Vector2 slide(const Vector2 &p_normal) const; diff --git a/core/math/vector3.h b/core/math/vector3.h index 18943a820f2d..c44a20dd2d20 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -84,6 +84,25 @@ struct _NO_DISCARD_ Vector3 { return Vector3(MAX(x, p_vector3.x), MAX(y, p_vector3.y), MAX(z, p_vector3.z)); } + _FORCE_INLINE_ Vector3 rotate_toward(Vector3 p_to, real_t p_delta) const { + if (Math::is_zero_approx(p_delta)) { + return *this; + } + + if (p_delta < 0.0f) { + p_delta = -p_delta; + p_to = -p_to; + } + + real_t angle = angle_to(p_to); + + if (angle < p_delta) { + return p_to; + } + + return slerp(p_to, p_delta / angle); + } + _FORCE_INLINE_ real_t length() const; _FORCE_INLINE_ real_t length_squared() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index f041d2c95ee4..648110abcd60 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1770,6 +1770,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, cubic_interpolate_in_time, sarray("b", "pre_a", "post_b", "weight", "b_t", "pre_a_t", "post_b_t"), varray()); bind_method(Vector2, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray()); bind_method(Vector2, bezier_derivative, sarray("control_1", "control_2", "end", "t"), varray()); + bind_method(Vector2, rotate_toward, sarray("to", "delta"), varray()); bind_method(Vector2, max_axis_index, sarray(), varray()); bind_method(Vector2, min_axis_index, sarray(), varray()); bind_method(Vector2, move_toward, sarray("to", "delta"), varray()); @@ -1865,6 +1866,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray()); bind_method(Vector3, bezier_derivative, sarray("control_1", "control_2", "end", "t"), varray()); bind_method(Vector3, move_toward, sarray("to", "delta"), varray()); + bind_method(Vector3, rotate_toward, sarray("to", "delta"), varray()); bind_method(Vector3, dot, sarray("with"), varray()); bind_method(Vector3, cross, sarray("with"), varray()); bind_method(Vector3, outer, sarray("with"), varray()); @@ -1959,6 +1961,7 @@ static void _register_variant_builtin_methods() { bind_method(Quaternion, log, sarray(), varray()); bind_method(Quaternion, exp, sarray(), varray()); bind_method(Quaternion, angle_to, sarray("to"), varray()); + bind_method(Quaternion, rotate_toward, sarray("to", "delta"), varray()); bind_method(Quaternion, dot, sarray("with"), varray()); bind_method(Quaternion, slerp, sarray("to", "weight"), varray()); bind_method(Quaternion, slerpni, sarray("to", "weight"), varray()); @@ -2087,6 +2090,7 @@ static void _register_variant_builtin_methods() { bind_method(Basis, orthonormalized, sarray(), varray()); bind_method(Basis, determinant, sarray(), varray()); bind_methodv(Basis, rotated, static_cast(&Basis::rotated), sarray("axis", "angle"), varray()); + bind_method(Basis, rotate_toward, sarray("to", "delta"), varray()); bind_method(Basis, scaled, sarray("scale"), varray()); bind_method(Basis, get_scale, sarray(), varray()); bind_method(Basis, get_euler, sarray("order"), varray((int64_t)EulerOrder::YXZ)); diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 972a8eb114cb..9481b57ab289 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -142,6 +142,15 @@ Returns the orthonormalized version of the matrix (useful to call from time to time to avoid rounding error for orthogonal matrices). This performs a Gram-Schmidt orthonormalization on the basis of the matrix. + + + + + + Assuming that the matrix is a proper rotation matrix, rotates toward [param to] by [param delta]. + Passing a negative [param delta] will rotate toward the inverse of [param to]. + + diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 0499d51d79d9..e96e5e8667f2 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -153,6 +153,16 @@ Returns a copy of the quaternion, normalized to unit length. + + + + + + Returns the result of rotating toward [param to] by [param delta]. + Passing a negative [param delta] will rotate toward the inverse of [param to]. + [b]Note:[/b] Both quaternions must be normalized. + + diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index b4718d96a316..abaaf00aef98 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -325,6 +325,15 @@ Returns the result of reflecting the vector from a line defined by the given direction vector [param n]. + + + + + + Returns the result of rotating this vector towards [param to], by increment [param delta] (in radians). + Passing a negative [param delta] will rotate toward the opposite of [param to]. + + diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 840e2ba61a8f..061fa9284324 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -309,6 +309,15 @@ Returns the result of reflecting the vector from a plane defined by the given normal [param n]. + + + + + + Returns the result of rotating this vector towards [param to], by increment [param delta] (in radians). + Passing a negative [param delta] will rotate toward the opposite of [param to]. + +