diff --git a/Source/Engine/Core/Math/Packed.cpp b/Source/Engine/Core/Math/Packed.cpp
index fa6ac2b96..1ef7decbf 100644
--- a/Source/Engine/Core/Math/Packed.cpp
+++ b/Source/Engine/Core/Math/Packed.cpp
@@ -6,35 +6,65 @@
#include "Vector4.h"
#include "Color.h"
-Float1010102::Float1010102(float x, float y, float z, float w)
+FloatR10G10B10A2::FloatR10G10B10A2(uint32 packed)
+ : Value(packed)
{
- x = Math::Clamp(x, 0.0f, 1.0f);
- y = Math::Clamp(y, 0.0f, 1.0f);
- z = Math::Clamp(z, 0.0f, 1.0f);
- w = Math::Clamp(w, 0.0f, 1.0f);
+}
+
+FloatR10G10B10A2::FloatR10G10B10A2(float x, float y, float z, float w)
+{
+ x = Math::Saturate(x);
+ y = Math::Saturate(y);
+ z = Math::Saturate(z);
+ w = Math::Saturate(w);
x = Math::Round(x * 1023.0f);
y = Math::Round(y * 1023.0f);
z = Math::Round(z * 1023.0f);
w = Math::Round(w * 3.0f);
- Value = ((uint32)w << 30) |
- (((uint32)z & 0x3FF) << 20) |
- (((uint32)y & 0x3FF) << 10) |
- (((uint32)x & 0x3FF));
+ Value = ((uint32)w << 30) | (((uint32)z & 0x3FF) << 20) | (((uint32)y & 0x3FF) << 10) | (((uint32)x & 0x3FF));
}
-Float1010102::Float1010102(const Vector3& v, float alpha)
- : Float1010102(v.X, v.Y, v.Z, alpha)
+FloatR10G10B10A2::FloatR10G10B10A2(const Vector3& v, float alpha)
+ : FloatR10G10B10A2(v.X, v.Y, v.Z, alpha)
{
}
-Float1010102::Float1010102(const float* values)
- : Float1010102(values[0], values[1], values[2], values[3])
+FloatR10G10B10A2::FloatR10G10B10A2(const Vector4& v)
+ : FloatR10G10B10A2(v.X, v.Y, v.Z, v.W)
{
}
-Float1010102::operator Vector4() const
+FloatR10G10B10A2::FloatR10G10B10A2(const float* values)
+ : FloatR10G10B10A2(values[0], values[1], values[2], values[3])
+{
+}
+
+FloatR10G10B10A2::operator Vector3() const
+{
+ return ToVector3();
+}
+
+FloatR10G10B10A2::operator Vector4() const
+{
+ return ToVector4();
+}
+
+Vector3 FloatR10G10B10A2::ToVector3() const
+{
+ Vector3 vectorOut;
+ uint32 tmp;
+ tmp = Value & 0x3FF;
+ vectorOut.X = (float)tmp / 1023.f;
+ tmp = (Value >> 10) & 0x3FF;
+ vectorOut.Y = (float)tmp / 1023.f;
+ tmp = (Value >> 20) & 0x3FF;
+ vectorOut.Z = (float)tmp / 1023.f;
+ return vectorOut;
+}
+
+Vector4 FloatR10G10B10A2::ToVector4() const
{
Vector4 vectorOut;
uint32 tmp;
@@ -48,19 +78,6 @@ Float1010102::operator Vector4() const
return vectorOut;
}
-Vector3 Float1010102::ToVector3() const
-{
- Vector3 vectorOut;
- uint32 tmp;
- tmp = Value & 0x3FF;
- vectorOut.X = (float)tmp / 1023.f;
- tmp = (Value >> 10) & 0x3FF;
- vectorOut.Y = (float)tmp / 1023.f;
- tmp = (Value >> 20) & 0x3FF;
- vectorOut.Z = (float)tmp / 1023.f;
- return vectorOut;
-}
-
FloatR11G11B10::FloatR11G11B10(float x, float y, float z)
{
// Reference: https://github.com/microsoft/DirectXMath
diff --git a/Source/Engine/Core/Math/Packed.h b/Source/Engine/Core/Math/Packed.h
index 2337b86ba..eabaae619 100644
--- a/Source/Engine/Core/Math/Packed.h
+++ b/Source/Engine/Core/Math/Packed.h
@@ -13,7 +13,7 @@ typedef Half Float16;
///
/// Packed vector, layout: R:10 bytes, G:10 bytes, B:10 bytes, A:2 bytes, all values are stored as floats in range [0;1].
///
-struct FLAXENGINE_API Float1010102
+struct FLAXENGINE_API FloatR10G10B10A2
{
union
{
@@ -28,41 +28,41 @@ struct FLAXENGINE_API Float1010102
uint32 Value;
};
- Float1010102() = default;
+ FloatR10G10B10A2() = default;
- explicit Float1010102(uint32 packed)
- : Value(packed)
- {
- }
-
- Float1010102(float x, float y, float z, float w);
- Float1010102(const Vector3& v, float alpha = 0);
- explicit Float1010102(const float* values);
+ explicit FloatR10G10B10A2(uint32 packed);
+ FloatR10G10B10A2(float x, float y, float z, float w);
+ FloatR10G10B10A2(const Vector3& v, float alpha = 0);
+ FloatR10G10B10A2(const Vector4& v);
+ explicit FloatR10G10B10A2(const float* values);
operator uint32() const
{
return Value;
}
+ operator Vector3() const;
operator Vector4() const;
- Float1010102& operator=(const Float1010102& other)
+ FloatR10G10B10A2& operator=(const FloatR10G10B10A2& other)
{
Value = other.Value;
return *this;
}
- Float1010102& operator=(uint32 packed)
+ FloatR10G10B10A2& operator=(uint32 packed)
{
Value = packed;
return *this;
}
-public:
-
Vector3 ToVector3() const;
+ Vector4 ToVector4() const;
};
+// [Deprecated on 14.01.2022, expires on 14.01.2024]
+typedef FloatR10G10B10A2 Float1010102;
+
// The 3D vector is packed into 32 bits with 11/11/10 bits per floating-point component.
struct FLAXENGINE_API FloatR11G11B10
{
@@ -114,7 +114,6 @@ struct FLAXENGINE_API FloatR11G11B10
}
public:
-
Vector3 ToVector3() const;
};
diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs
index bef9d1d83..75e0b892c 100644
--- a/Source/Engine/Core/Math/Quaternion.cs
+++ b/Source/Engine/Core/Math/Quaternion.cs
@@ -152,15 +152,9 @@ namespace FlaxEngine
///
/// Initializes a new instance of the struct.
///
- ///
- /// The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array
- /// with four elements.
- ///
+ /// The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array with four elements.
/// Thrown when is null.
- ///
- /// Thrown when contains more or less than four
- /// elements.
- ///
+ /// Thrown when contains more or less than four elements.
public Quaternion(float[] values)
{
if (values == null)
@@ -177,9 +171,6 @@ namespace FlaxEngine
///
/// Gets a value indicating whether this instance is equivalent to the identity quaternion.
///
- ///
- /// true if this instance is an identity quaternion; otherwise, false.
- ///
public bool IsIdentity => Equals(Identity);
///
@@ -238,7 +229,6 @@ namespace FlaxEngine
///
/// Gets the angle of the quaternion.
///
- /// The angle.
public float Angle
{
get
@@ -253,7 +243,6 @@ namespace FlaxEngine
///
/// Gets the axis components of the quaternion.
///
- /// The axis components of the quaternion.
public Vector3 Axis
{
get
@@ -270,14 +259,9 @@ namespace FlaxEngine
/// Gets or sets the component at the specified index.
///
/// The value of the X, Y, Z, or W component, depending on the index.
- ///
- /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z
- /// component, and 3 for the W component.
- ///
+ /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component.
/// The value of the component at the specified index.
- ///
- /// Thrown when the is out of the range [0, 3].
- ///
+ /// Thrown when the is out of the range [0, 3].
public float this[int index]
{
get
@@ -289,10 +273,8 @@ namespace FlaxEngine
case 2: return Z;
case 3: return W;
}
-
throw new ArgumentOutOfRangeException(nameof(index), "Indices for Quaternion run from 0 to 3, inclusive.");
}
-
set
{
switch (index)
@@ -353,20 +335,14 @@ namespace FlaxEngine
/// Calculates the length of the quaternion.
///
/// The length of the quaternion.
- ///
- /// may be preferred when only the relative length is needed
- /// and speed is of the essence.
- ///
+ /// may be preferred when only the relative length is needed and speed is of the essence.
public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
///
/// Calculates the squared length of the quaternion.
///
/// The squared length of the quaternion.
- ///
- /// This method may be preferred to when only a relative length is needed
- /// and speed is of the essence.
- ///
+ /// This method may be preferred to when only a relative length is needed and speed is of the essence.
public float LengthSquared => X * X + Y * Y + Z * Z + W * W;
///
@@ -1238,8 +1214,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a pitch, yaw and roll values.
- /// Angles are in degrees.
+ /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees.
///
/// The pitch, yaw and roll angles of rotation.
/// When the method completes, contains the newly created quaternion.
@@ -1254,8 +1229,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a pitch, yaw and roll values.
- /// Angles are in degrees.
+ /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees.
///
/// The pitch, yaw and roll angles of rotation.
/// When the method completes, contains the newly created quaternion.
@@ -1269,8 +1243,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a pitch, yaw and roll values.
- /// Angles are in degrees.
+ /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees.
///
/// The pitch of rotation (in degrees).
/// The yaw of rotation (in degrees).
@@ -1287,8 +1260,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a pitch, yaw and roll values.
- /// Angles are in degrees.
+ /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees.
///
/// The pitch of rotation (in degrees).
/// The yaw of rotation (in degrees).
@@ -1304,8 +1276,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a yaw, pitch, and roll value.
- /// Angles are in radians. Use to convert degrees to radians.
+ /// Creates a quaternion given a yaw, pitch, and roll value. Angles are in radians. Use to convert degrees to radians.
///
/// The yaw of rotation (in radians).
/// The pitch of rotation (in radians).
@@ -1331,8 +1302,7 @@ namespace FlaxEngine
}
///
- /// Creates a quaternion given a yaw, pitch, and roll value.
- /// Angles are in radians.
+ /// Creates a quaternion given a yaw, pitch, and roll value. Angles are in radians.
///
/// The yaw of rotation (in radians).
/// The pitch of rotation (in radians).
@@ -1523,10 +1493,7 @@ namespace FlaxEngine
///
/// The first value to compare.
/// The second value to compare.
- ///
- /// true if has the same value as ; otherwise,
- /// false.
- ///
+ /// true if has the same value as ; otherwise, false.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Quaternion left, Quaternion right)
{
@@ -1538,10 +1505,7 @@ namespace FlaxEngine
///
/// The first value to compare.
/// The second value to compare.
- ///
- /// true if has a different value than ; otherwise,
- /// false.
- ///
+ /// true if has a different value than ; otherwise, false.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Quaternion left, Quaternion right)
{
@@ -1551,9 +1515,7 @@ namespace FlaxEngine
///
/// Returns a that represents this instance.
///
- ///
- /// A that represents this instance.
- ///
+ /// A that represents this instance.
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, _formatString, X, Y, Z, W);
@@ -1563,9 +1525,7 @@ namespace FlaxEngine
/// Returns a that represents this instance.
///
/// The format.
- ///
- /// A that represents this instance.
- ///
+ /// A that represents this instance.
public string ToString(string format)
{
if (format == null)
@@ -1579,9 +1539,7 @@ namespace FlaxEngine
/// Returns a that represents this instance.
///
/// The format provider.
- ///
- /// A that represents this instance.
- ///
+ /// A that represents this instance.
public string ToString(IFormatProvider formatProvider)
{
return string.Format(formatProvider, _formatString, X, Y, Z, W);
@@ -1592,9 +1550,7 @@ namespace FlaxEngine
///
/// The format.
/// The format provider.
- ///
- /// A that represents this instance.
- ///
+ /// A that represents this instance.
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null)
@@ -1607,9 +1563,7 @@ namespace FlaxEngine
///
/// Returns a hash code for this instance.
///
- ///
- /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
- ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode()
{
unchecked
@@ -1651,9 +1605,7 @@ namespace FlaxEngine
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
- ///
- /// true if the specified is equal to this instance; otherwise, false.
- ///
+ /// true if the specified is equal to this instance; otherwise, false.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ref Quaternion other)
{
@@ -1665,9 +1617,7 @@ namespace FlaxEngine
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
- ///
- /// true if the specified is equal to this instance; otherwise, false.
- ///
+ /// true if the specified is equal to this instance; otherwise, false.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Quaternion other)
{
@@ -1678,9 +1628,7 @@ namespace FlaxEngine
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
- ///
- /// true if the specified is equal to this instance; otherwise, false.
- ///
+ /// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object value)
{
if (!(value is Quaternion))
diff --git a/Source/Engine/Core/RandomStream.h b/Source/Engine/Core/RandomStream.h
index 729b89539..a510356ea 100644
--- a/Source/Engine/Core/RandomStream.h
+++ b/Source/Engine/Core/RandomStream.h
@@ -10,7 +10,7 @@
///
/// Very basic pseudo numbers generator.
///
-class RandomStream
+class FLAXENGINE_API RandomStream
{
private:
@@ -102,7 +102,6 @@ public:
uint32 GetUnsignedInt() const
{
MutateSeed();
-
return *(uint32*)&_seed;
}
@@ -145,6 +144,15 @@ public:
} while (l > 1.0f || l < ZeroTolerance);
return Vector3::Normalize(result);
}
+
+ ///
+ /// Gets a random with components in a range between [0;1].
+ ///
+ /// A random .
+ Vector3 GetVector3() const
+ {
+ return Vector3(GetFraction(), GetFraction(), GetFraction());
+ }
///
/// Helper function for rand implementations.
diff --git a/Source/Engine/Tests/TestMath.cpp b/Source/Engine/Tests/TestMath.cpp
new file mode 100644
index 000000000..0f1082bb4
--- /dev/null
+++ b/Source/Engine/Tests/TestMath.cpp
@@ -0,0 +1,201 @@
+// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+
+#include "Engine/Core/Math/Matrix.h"
+#include "Engine/Core/Math/Packed.h"
+#include "Engine/Core/Math/Vector3.h"
+#include "Engine/Core/Math/Vector4.h"
+#include "Engine/Core/Math/Quaternion.h"
+#include "Engine/Core/Math/Transform.h"
+#include "Engine/Core/RandomStream.h"
+#include
+
+static Quaternion RotationX(float angle)
+{
+ const float halfAngle = angle * 0.5f;
+ return Quaternion(Math::Sin(halfAngle), 0.0f, 0.0f, Math::Cos(halfAngle));
+}
+
+TEST_CASE("FloatR10G10B10A2")
+{
+ SECTION("Test Conversion")
+ {
+ CHECK(Vector4::NearEqual(Vector4::Zero, FloatR10G10B10A2(Vector4::Zero).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4::One, FloatR10G10B10A2(Vector4::One).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4(0.5004888f, 0.5004888f, 0.5004888f, 0.666667f), FloatR10G10B10A2(Vector4(0.5f)).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4(1, 0, 0, 0), FloatR10G10B10A2(Vector4(1, 0, 0, 0)).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4(0, 1, 0, 0), FloatR10G10B10A2(Vector4(0, 1, 0, 0)).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4(0, 0, 1, 0), FloatR10G10B10A2(Vector4(0, 0, 1, 0)).ToVector4()));
+ CHECK(Vector4::NearEqual(Vector4(0, 0, 0, 1), FloatR10G10B10A2(Vector4(0, 0, 0, 1)).ToVector4()));
+ }
+}
+
+TEST_CASE("FloatR11G11B10")
+{
+ SECTION("Test Conversion")
+ {
+ CHECK(Vector3::NearEqual(Vector3::Zero, FloatR11G11B10(Vector3::Zero).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3::One, FloatR11G11B10(Vector3::One).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3(0.5f, 0.5f, 0.5f), FloatR11G11B10(Vector3(0.5f)).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3(1, 0, 0), FloatR11G11B10(Vector3(1, 0, 0)).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3(0, 1, 0), FloatR11G11B10(Vector3(0, 1, 0)).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3(0, 0, 1), FloatR11G11B10(Vector3(0, 0, 1)).ToVector3()));
+ CHECK(Vector3::NearEqual(Vector3(10, 11, 12), FloatR11G11B10(Vector3(10, 11, 12)).ToVector3()));
+ }
+}
+
+TEST_CASE("Quaternion")
+{
+ SECTION("Test Euler")
+ {
+ CHECK(Quaternion::NearEqual(Quaternion::Euler(90, 0, 0), Quaternion(0.7071068f, 0, 0, 0.7071068f)));
+ CHECK(Quaternion::NearEqual(Quaternion::Euler(25, 0, 10), Quaternion(0.215616f, -0.018864f, 0.0850898f, 0.9725809f)));
+ CHECK(Vector3::NearEqual(Vector3(25, 0, 10), Quaternion::Euler(25, 0, 10).GetEuler()));
+ CHECK(Vector3::NearEqual(Vector3(25, -5, 10), Quaternion::Euler(25, -5, 10).GetEuler()));
+ }
+ SECTION("Test Multiply")
+ {
+ auto q = Quaternion::Identity;
+ auto delta = Quaternion::Euler(0, 10, 0);
+ for (int i = 0; i < 9; i++)
+ q *= delta;
+ CHECK(Quaternion::NearEqual(Quaternion::Euler(0, 90, 0), q, 0.00001f));
+ }
+}
+
+TEST_CASE("Transform")
+{
+ SECTION("Test World Matrix")
+ {
+ Transform t1(Vector3(10, 1, 10), Quaternion::Euler(45, 0, -15), Vector3(1.5f, 0.5f, 0.1f));
+
+ Matrix a1 = t1.GetWorld();
+
+ Matrix a2;
+ {
+ Matrix m1, m2;
+ Matrix::Scaling(t1.Scale, a2);
+ Matrix::RotationQuaternion(t1.Orientation, m2);
+ Matrix::Multiply(a2, m2, m1);
+ Matrix::Translation(t1.Translation, m2);
+ Matrix::Multiply(m1, m2, a2);
+ }
+
+ Matrix a3;
+ Matrix::Transformation(t1.Scale, t1.Orientation, t1.Translation, a3);
+
+ CHECK(a1 == a2);
+ CHECK(a1 == a3);
+ }
+ SECTION("Test Local To World")
+ {
+ Transform t1(Vector3(10, 1, 10), Quaternion::Euler(45, 0, -15), Vector3(1.5f, 0.5f, 0.1f));
+ Transform t2(Vector3(0, 20, 0), Quaternion::Euler(0, 0, 15), Vector3(1.0f, 2.0f, 1.0f));
+
+ Transform a1 = t1.LocalToWorld(t2);
+ Vector3 a2 = t1.LocalToWorld(t2.Translation);
+
+ Vector3 a3;
+ {
+ Vector3 result;
+ Matrix scale, rotation, scaleRotation;
+ Matrix::Scaling(t1.Scale, scale);
+ Matrix::RotationQuaternion(t1.Orientation, rotation);
+ Matrix::Multiply(scale, rotation, scaleRotation);
+ Vector3::Transform(t2.Translation, scaleRotation, result);
+ a3 = result + t1.Translation;
+ }
+
+ Vector3 a4T[1];
+ Vector3 a4Ta[1] = { t2.Translation };
+ t1.LocalToWorld(a4Ta, ARRAY_COUNT(a4Ta), a4T);
+ Vector3 a4 = a4T[0];
+
+ CHECK(Vector3::NearEqual(a1.Translation, a2));
+ CHECK(Vector3::NearEqual(a2, a3));
+ CHECK(Vector3::NearEqual(a2, a4));
+ }
+ SECTION("Test World To Local")
+ {
+ Transform t1 = Transform(Vector3(10, 1, 10), Quaternion::Euler(45, 0, -15), Vector3(1.5f, 0.5f, 0.1f));
+ Transform t2 = Transform(Vector3(0, 20, 0), Quaternion::Euler(0, 0, 15), Vector3(1.0f, 2.0f, 1.0f));
+
+ Transform a1 = t1.WorldToLocal(t2);
+ Vector3 a2 = t1.WorldToLocal(t2.Translation);
+
+ Vector3 a3;
+ {
+ Matrix scale, rotation, scaleRotation;
+ Matrix::Scaling(t1.Scale, scale);
+ Matrix::RotationQuaternion(t1.Orientation, rotation);
+ Matrix::Multiply(scale, rotation, scaleRotation);
+ Matrix::Invert(scaleRotation, scale);
+ a3 = t2.Translation - t1.Translation;
+ Vector3::Transform(a3, scale, a3);
+ }
+
+ Vector3 a4T[1];
+ Vector3 a4Ta[1] = { t2.Translation };
+ t1.WorldToLocal(a4Ta, ARRAY_COUNT(a4Ta), a4T);
+ Vector3 a4 = a4T[0];
+
+ CHECK(Vector3::NearEqual(a1.Translation, a2));
+ CHECK(Vector3::NearEqual(a2, a3, 0.0001f));
+ CHECK(Vector3::NearEqual(a2, a4));
+ }
+ SECTION("Test World Local Space")
+ {
+ Transform trans = Transform(Vector3(1, 2, 3));
+
+ CHECK(Vector3::NearEqual(Vector3(1, 2, 3), trans.LocalToWorld(Vector3(0, 0, 0))));
+ CHECK(Vector3::NearEqual(Vector3(4, 4, 4), trans.LocalToWorld(Vector3(3, 2, 1))));
+ CHECK(Vector3::NearEqual(Vector3(-1, -2, -3), trans.WorldToLocal(Vector3(0, 0, 0))));
+ CHECK(Vector3::NearEqual(Vector3(0, 0, 0), trans.WorldToLocal(Vector3(1, 2, 3))));
+
+ trans = Transform(Vector3::Zero, Quaternion::Euler(0, 90, 0));
+ CHECK(Vector3::NearEqual(Vector3(0, 2, -1), trans.LocalToWorld(Vector3(1, 2, 0))));
+
+ trans.Translation = Vector3(1, 0, 0);
+ trans.Orientation = RotationX(PI * 0.5f);
+ trans.Scale = Vector3(2, 2, 2);
+ CHECK(Vector3::NearEqual(Vector3(1, 0, 2), trans.LocalToWorld(Vector3(0, 1, 0))));
+
+ Transform t1 = trans.LocalToWorld(Transform::Identity);
+ CHECK(Vector3::NearEqual(Vector3(1.0f, 0, 0), t1.Translation));
+ CHECK(Quaternion::NearEqual(RotationX(PI * 0.5f), t1.Orientation));
+ CHECK(Vector3::NearEqual(Vector3(2.0f, 2.0f, 2.0f), t1.Scale));
+
+ Transform t2 = trans.WorldToLocal(Transform::Identity);
+ CHECK(Vector3::NearEqual(Vector3(-0.5f, 0, 0), t2.Translation));
+ CHECK(Quaternion::NearEqual(RotationX(PI * -0.5f), t2.Orientation));
+ CHECK(Vector3::NearEqual(Vector3(0.5f, 0.5f, 0.5f), t2.Scale));
+
+ RandomStream rand(10);
+ for (int32 i = 0; i < 10; i++)
+ {
+ Transform a = Transform(rand.GetVector3(), Quaternion::Euler((float)i * 10, 0, (float)i), rand.GetVector3() * 10.0f);
+ Transform b = Transform(rand.GetVector3(), Quaternion::Euler((float)i, 1, 22), rand.GetVector3() * 0.3f);
+
+ Transform ab = a.LocalToWorld(b);
+ Transform ba = a.WorldToLocal(ab);
+
+ CHECK(Transform::NearEqual(b, ba, 0.00001f));
+ }
+ }
+ SECTION("Test Add Subtract")
+ {
+ RandomStream rand(10);
+ for (int32 i = 0; i < 10; i++)
+ {
+ Transform a = Transform(rand.GetVector3(), Quaternion::Euler((float)i * 10, 0, (float)i), rand.GetVector3() * 10.0f);
+ Transform b = Transform(rand.GetVector3(), Quaternion::Euler((float)i, 1, 22), rand.GetVector3() * 0.3f);
+
+ Transform ab = a + b;
+ Transform newA = ab - b;
+ CHECK(Transform::NearEqual(a, newA, 0.00001f));
+
+ Transform ba = b + a;
+ Transform newB = ba - a;
+ CHECK(Transform::NearEqual(b, newB, 0.00001f));
+ }
+ }
+}