Browse Source

Merge pull request #97553 from AThousandShips/semantic_equal

[Core] Add `is_same` to types that have float components
Thaddeus Crews 6 months ago
parent
commit
a13067e61f

+ 4 - 0
core/math/aabb.cpp

@@ -76,6 +76,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {
 	return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
 }
 
+bool AABB::is_same(const AABB &p_aabb) const {
+	return position.is_same(p_aabb.position) && size.is_same(p_aabb.size);
+}
+
 bool AABB::is_finite() const {
 	return position.is_finite() && size.is_finite();
 }

+ 1 - 0
core/math/aabb.h

@@ -62,6 +62,7 @@ struct [[nodiscard]] AABB {
 	bool operator!=(const AABB &p_rval) const;
 
 	bool is_equal_approx(const AABB &p_aabb) const;
+	bool is_same(const AABB &p_aabb) const;
 	bool is_finite() const;
 	_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
 	_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap

+ 4 - 0
core/math/basis.cpp

@@ -699,6 +699,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const {
 	return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
 }
 
+bool Basis::is_same(const Basis &p_basis) const {
+	return rows[0].is_same(p_basis.rows[0]) && rows[1].is_same(p_basis.rows[1]) && rows[2].is_same(p_basis.rows[2]);
+}
+
 bool Basis::is_finite() const {
 	return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
 }

+ 1 - 0
core/math/basis.h

@@ -120,6 +120,7 @@ struct [[nodiscard]] Basis {
 	}
 
 	bool is_equal_approx(const Basis &p_basis) const;
+	bool is_same(const Basis &p_basis) const;
 	bool is_finite() const;
 
 	bool operator==(const Basis &p_matrix) const;

+ 4 - 0
core/math/color.cpp

@@ -259,6 +259,10 @@ bool Color::is_equal_approx(const Color &p_color) const {
 	return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a);
 }
 
+bool Color::is_same(const Color &p_color) const {
+	return Math::is_same(r, p_color.r) && Math::is_same(g, p_color.g) && Math::is_same(b, p_color.b) && Math::is_same(a, p_color.a);
+}
+
 Color Color::clamp(const Color &p_min, const Color &p_max) const {
 	return Color(
 			CLAMP(r, p_min.r, p_max.r),

+ 1 - 0
core/math/color.h

@@ -93,6 +93,7 @@ struct [[nodiscard]] Color {
 	void operator/=(float p_scalar);
 
 	bool is_equal_approx(const Color &p_color) const;
+	bool is_same(const Color &p_color) const;
 
 	Color clamp(const Color &p_min = Color(0, 0, 0, 0), const Color &p_max = Color(1, 1, 1, 1)) const;
 	void invert();

+ 8 - 0
core/math/math_funcs.h

@@ -593,6 +593,10 @@ public:
 		return abs(s) < (float)CMP_EPSILON;
 	}
 
+	static _ALWAYS_INLINE_ bool is_same(float a, float b) {
+		return (a == b) || (is_nan(a) && is_nan(b));
+	}
+
 	static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b) {
 		// Check for exact equality first, required to handle "infinity" values.
 		if (a == b) {
@@ -619,6 +623,10 @@ public:
 		return abs(s) < CMP_EPSILON;
 	}
 
+	static _ALWAYS_INLINE_ bool is_same(double a, double b) {
+		return (a == b) || (is_nan(a) && is_nan(b));
+	}
+
 	static _ALWAYS_INLINE_ float absf(float g) {
 		union {
 			float f;

+ 4 - 0
core/math/plane.cpp

@@ -172,6 +172,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
 	return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
 }
 
+bool Plane::is_same(const Plane &p_plane) const {
+	return normal.is_same(p_plane.normal) && Math::is_same(d, p_plane.d);
+}
+
 bool Plane::is_finite() const {
 	return normal.is_finite() && Math::is_finite(d);
 }

+ 1 - 0
core/math/plane.h

@@ -72,6 +72,7 @@ struct [[nodiscard]] Plane {
 
 	Plane operator-() const { return Plane(-normal, -d); }
 	bool is_equal_approx(const Plane &p_plane) const;
+	bool is_same(const Plane &p_plane) const;
 	bool is_equal_approx_any_side(const Plane &p_plane) const;
 	bool is_finite() const;
 

+ 4 - 0
core/math/projection.cpp

@@ -780,6 +780,10 @@ void Projection::flip_y() {
 	}
 }
 
+bool Projection::is_same(const Projection &p_cam) const {
+	return columns[0].is_same(p_cam.columns[0]) && columns[1].is_same(p_cam.columns[1]) && columns[2].is_same(p_cam.columns[2]) && columns[3].is_same(p_cam.columns[3]);
+}
+
 Projection::Projection() {
 	set_identity();
 }

+ 2 - 0
core/math/projection.h

@@ -132,6 +132,8 @@ struct [[nodiscard]] Projection {
 
 	void flip_y();
 
+	bool is_same(const Projection &p_cam) const;
+
 	bool operator==(const Projection &p_cam) const {
 		for (uint32_t i = 0; i < 4; i++) {
 			for (uint32_t j = 0; j < 4; j++) {

+ 4 - 0
core/math/quaternion.cpp

@@ -66,6 +66,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
 	return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
 }
 
+bool Quaternion::is_same(const Quaternion &p_quaternion) const {
+	return Math::is_same(x, p_quaternion.x) && Math::is_same(y, p_quaternion.y) && Math::is_same(z, p_quaternion.z) && Math::is_same(w, p_quaternion.w);
+}
+
 bool Quaternion::is_finite() const {
 	return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
 }

+ 1 - 0
core/math/quaternion.h

@@ -53,6 +53,7 @@ struct [[nodiscard]] Quaternion {
 	}
 	_FORCE_INLINE_ real_t length_squared() const;
 	bool is_equal_approx(const Quaternion &p_quaternion) const;
+	bool is_same(const Quaternion &p_quaternion) const;
 	bool is_finite() const;
 	real_t length() const;
 	void normalize();

+ 4 - 0
core/math/rect2.cpp

@@ -38,6 +38,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
 	return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
 }
 
+bool Rect2::is_same(const Rect2 &p_rect) const {
+	return position.is_same(p_rect.position) && size.is_same(p_rect.size);
+}
+
 bool Rect2::is_finite() const {
 	return position.is_finite() && size.is_finite();
 }

+ 1 - 0
core/math/rect2.h

@@ -202,6 +202,7 @@ struct [[nodiscard]] Rect2 {
 	}
 
 	bool is_equal_approx(const Rect2 &p_rect) const;
+	bool is_same(const Rect2 &p_rect) const;
 	bool is_finite() const;
 
 	bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }

+ 4 - 0
core/math/transform_2d.cpp

@@ -180,6 +180,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
 	return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
 }
 
+bool Transform2D::is_same(const Transform2D &p_transform) const {
+	return columns[0].is_same(p_transform.columns[0]) && columns[1].is_same(p_transform.columns[1]) && columns[2].is_same(p_transform.columns[2]);
+}
+
 bool Transform2D::is_finite() const {
 	return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
 }

+ 1 - 0
core/math/transform_2d.h

@@ -100,6 +100,7 @@ struct [[nodiscard]] Transform2D {
 	Transform2D orthonormalized() const;
 	bool is_conformal() const;
 	bool is_equal_approx(const Transform2D &p_transform) const;
+	bool is_same(const Transform2D &p_transform) const;
 	bool is_finite() const;
 
 	Transform2D looking_at(const Vector2 &p_target) const;

+ 4 - 0
core/math/transform_3d.cpp

@@ -173,6 +173,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
 	return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
 }
 
+bool Transform3D::is_same(const Transform3D &p_transform) const {
+	return basis.is_same(p_transform.basis) && origin.is_same(p_transform.origin);
+}
+
 bool Transform3D::is_finite() const {
 	return basis.is_finite() && origin.is_finite();
 }

+ 1 - 0
core/math/transform_3d.h

@@ -74,6 +74,7 @@ struct [[nodiscard]] Transform3D {
 	void orthogonalize();
 	Transform3D orthogonalized() const;
 	bool is_equal_approx(const Transform3D &p_transform) const;
+	bool is_same(const Transform3D &p_transform) const;
 	bool is_finite() const;
 
 	bool operator==(const Transform3D &p_transform) const;

+ 4 - 0
core/math/vector2.cpp

@@ -194,6 +194,10 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const {
 	return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y);
 }
 
+bool Vector2::is_same(const Vector2 &p_v) const {
+	return Math::is_same(x, p_v.x) && Math::is_same(y, p_v.y);
+}
+
 bool Vector2::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y);
 }

+ 1 - 0
core/math/vector2.h

@@ -128,6 +128,7 @@ struct [[nodiscard]] Vector2 {
 	Vector2 reflect(const Vector2 &p_normal) const;
 
 	bool is_equal_approx(const Vector2 &p_v) const;
+	bool is_same(const Vector2 &p_v) const;
 	bool is_zero_approx() const;
 	bool is_finite() const;
 

+ 4 - 0
core/math/vector3.cpp

@@ -156,6 +156,10 @@ bool Vector3::is_equal_approx(const Vector3 &p_v) const {
 	return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z);
 }
 
+bool Vector3::is_same(const Vector3 &p_v) const {
+	return Math::is_same(x, p_v.x) && Math::is_same(y, p_v.y) && Math::is_same(z, p_v.z);
+}
+
 bool Vector3::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
 }

+ 1 - 0
core/math/vector3.h

@@ -155,6 +155,7 @@ struct [[nodiscard]] Vector3 {
 	_FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const;
 
 	bool is_equal_approx(const Vector3 &p_v) const;
+	bool is_same(const Vector3 &p_v) const;
 	bool is_zero_approx() const;
 	bool is_finite() const;
 

+ 4 - 0
core/math/vector4.cpp

@@ -62,6 +62,10 @@ bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
 	return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
 }
 
+bool Vector4::is_same(const Vector4 &p_vec4) const {
+	return Math::is_same(x, p_vec4.x) && Math::is_same(y, p_vec4.y) && Math::is_same(z, p_vec4.z) && Math::is_same(w, p_vec4.w);
+}
+
 bool Vector4::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
 }

+ 1 - 0
core/math/vector4.h

@@ -88,6 +88,7 @@ struct [[nodiscard]] Vector4 {
 	_FORCE_INLINE_ real_t length_squared() const;
 	bool is_equal_approx(const Vector4 &p_vec4) const;
 	bool is_zero_approx() const;
+	bool is_same(const Vector4 &p_vec4) const;
 	bool is_finite() const;
 	real_t length() const;
 	void normalize();

+ 14 - 32
core/templates/hashfuncs.h

@@ -425,116 +425,98 @@ struct HashMapComparatorDefault {
 template <>
 struct HashMapComparatorDefault<float> {
 	static bool compare(const float &p_lhs, const float &p_rhs) {
-		return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs));
+		return Math::is_same(p_lhs, p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<double> {
 	static bool compare(const double &p_lhs, const double &p_rhs) {
-		return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs));
+		return Math::is_same(p_lhs, p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Color> {
 	static bool compare(const Color &p_lhs, const Color &p_rhs) {
-		return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Vector2> {
 	static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
-		return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Vector3> {
 	static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) {
-		return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Vector4> {
 	static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) {
-		return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Rect2> {
 	static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) {
-		return HashMapComparatorDefault<Vector2>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector2>().compare(p_lhs.size, p_rhs.size);
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<AABB> {
 	static bool compare(const AABB &p_lhs, const AABB &p_rhs) {
-		return HashMapComparatorDefault<Vector3>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector3>().compare(p_lhs.size, p_rhs.size);
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Plane> {
 	static bool compare(const Plane &p_lhs, const Plane &p_rhs) {
-		return HashMapComparatorDefault<Vector3>().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Transform2D> {
 	static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) {
-		for (int i = 0; i < 3; ++i) {
-			if (!HashMapComparatorDefault<Vector2>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
-				return false;
-			}
-		}
-
-		return true;
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Basis> {
 	static bool compare(const Basis &p_lhs, const Basis &p_rhs) {
-		for (int i = 0; i < 3; ++i) {
-			if (!HashMapComparatorDefault<Vector3>().compare(p_lhs.rows[i], p_rhs.rows[i])) {
-				return false;
-			}
-		}
-
-		return true;
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Transform3D> {
 	static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) {
-		return HashMapComparatorDefault<Basis>().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault<Vector3>().compare(p_lhs.origin, p_rhs.origin);
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Projection> {
 	static bool compare(const Projection &p_lhs, const Projection &p_rhs) {
-		for (int i = 0; i < 4; ++i) {
-			if (!HashMapComparatorDefault<Vector4>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
-				return false;
-			}
-		}
-
-		return true;
+		return p_lhs.is_same(p_rhs);
 	}
 };
 
 template <>
 struct HashMapComparatorDefault<Quaternion> {
 	static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) {
-		return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
+		return p_lhs.is_same(p_rhs);
 	}
 };
 

+ 20 - 59
core/variant/variant.cpp

@@ -3139,32 +3139,20 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
 #define hash_compare_scalar(p_lhs, p_rhs) \
 	(hash_compare_scalar_base(p_lhs, p_rhs, true))
 
-#define hash_compare_vector2(p_lhs, p_rhs)        \
-	(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
-			hash_compare_scalar((p_lhs).y, (p_rhs).y))
-
-#define hash_compare_vector3(p_lhs, p_rhs)               \
-	(hash_compare_scalar((p_lhs).x, (p_rhs).x) &&        \
-			hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
-			hash_compare_scalar((p_lhs).z, (p_rhs).z))
-
-#define hash_compare_vector4(p_lhs, p_rhs)               \
-	(hash_compare_scalar((p_lhs).x, (p_rhs).x) &&        \
-			hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
-			hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
-			hash_compare_scalar((p_lhs).w, (p_rhs).w))
-
-#define hash_compare_quaternion(p_lhs, p_rhs)            \
-	(hash_compare_scalar((p_lhs).x, (p_rhs).x) &&        \
-			hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
-			hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
-			hash_compare_scalar((p_lhs).w, (p_rhs).w))
-
-#define hash_compare_color(p_lhs, p_rhs)                 \
-	(hash_compare_scalar((p_lhs).r, (p_rhs).r) &&        \
-			hash_compare_scalar((p_lhs).g, (p_rhs).g) && \
-			hash_compare_scalar((p_lhs).b, (p_rhs).b) && \
-			hash_compare_scalar((p_lhs).a, (p_rhs).a))
+#define hash_compare_vector2(p_lhs, p_rhs) \
+	(p_lhs).is_same(p_rhs)
+
+#define hash_compare_vector3(p_lhs, p_rhs) \
+	(p_lhs).is_same(p_rhs)
+
+#define hash_compare_vector4(p_lhs, p_rhs) \
+	(p_lhs).is_same(p_rhs)
+
+#define hash_compare_quaternion(p_lhs, p_rhs) \
+	(p_lhs).is_same(p_rhs)
+
+#define hash_compare_color(p_lhs, p_rhs) \
+	(p_lhs).is_same(p_rhs)
 
 #define hash_compare_packed_array(p_lhs, p_rhs, p_type, p_compare_func) \
 	const Vector<p_type> &l = PackedArrayRef<p_type>::get_array(p_lhs); \
@@ -3235,13 +3223,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
 			Transform2D *l = _data._transform2d;
 			Transform2D *r = p_variant._data._transform2d;
 
-			for (int i = 0; i < 3; i++) {
-				if (!hash_compare_vector2(l->columns[i], r->columns[i])) {
-					return false;
-				}
-			}
-
-			return true;
+			return l->is_same(*r);
 		} break;
 
 		case VECTOR3: {
@@ -3273,17 +3255,14 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
 			const Plane *l = reinterpret_cast<const Plane *>(_data._mem);
 			const Plane *r = reinterpret_cast<const Plane *>(p_variant._data._mem);
 
-			return hash_compare_vector3(l->normal, r->normal) &&
-					hash_compare_scalar(l->d, r->d);
+			return l->is_same(*r);
 		} break;
 
 		case AABB: {
 			const ::AABB *l = _data._aabb;
 			const ::AABB *r = p_variant._data._aabb;
 
-			return hash_compare_vector3(l->position, r->position) &&
-					hash_compare_vector3(l->size, r->size);
-
+			return l->is_same(*r);
 		} break;
 
 		case QUATERNION: {
@@ -3297,38 +3276,20 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
 			const Basis *l = _data._basis;
 			const Basis *r = p_variant._data._basis;
 
-			for (int i = 0; i < 3; i++) {
-				if (!hash_compare_vector3(l->rows[i], r->rows[i])) {
-					return false;
-				}
-			}
-
-			return true;
+			return l->is_same(*r);
 		} break;
 
 		case TRANSFORM3D: {
 			const Transform3D *l = _data._transform3d;
 			const Transform3D *r = p_variant._data._transform3d;
 
-			for (int i = 0; i < 3; i++) {
-				if (!hash_compare_vector3(l->basis.rows[i], r->basis.rows[i])) {
-					return false;
-				}
-			}
-
-			return hash_compare_vector3(l->origin, r->origin);
+			return l->is_same(*r);
 		} break;
 		case PROJECTION: {
 			const Projection *l = _data._projection;
 			const Projection *r = p_variant._data._projection;
 
-			for (int i = 0; i < 4; i++) {
-				if (!hash_compare_vector4(l->columns[i], r->columns[i])) {
-					return false;
-				}
-			}
-
-			return true;
+			return l->is_same(*r);
 		} break;
 
 		case COLOR: {