Add math unit tests for c++

This commit is contained in:
Wojciech Figat
2022-01-14 19:08:04 +01:00
parent 2df3997c8b
commit cfa5aa6a61
5 changed files with 291 additions and 118 deletions

View File

@@ -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

View File

@@ -13,7 +13,7 @@ typedef Half Float16;
/// <summary>
/// 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].
/// </summary>
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;
};

View File

@@ -152,15 +152,9 @@ namespace FlaxEngine
/// <summary>
/// Initializes a new instance of the <see cref="Quaternion" /> struct.
/// </summary>
/// <param name="values">
/// The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array
/// with four elements.
/// </param>
/// <param name="values">The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array with four elements.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="values" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when <paramref name="values" /> contains more or less than four
/// elements.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="values" /> contains more or less than four elements.</exception>
public Quaternion(float[] values)
{
if (values == null)
@@ -177,9 +171,6 @@ namespace FlaxEngine
/// <summary>
/// Gets a value indicating whether this instance is equivalent to the identity quaternion.
/// </summary>
/// <value>
/// <c>true</c> if this instance is an identity quaternion; otherwise, <c>false</c>.
/// </value>
public bool IsIdentity => Equals(Identity);
/// <summary>
@@ -238,7 +229,6 @@ namespace FlaxEngine
/// <summary>
/// Gets the angle of the quaternion.
/// </summary>
/// <value>The angle.</value>
public float Angle
{
get
@@ -253,7 +243,6 @@ namespace FlaxEngine
/// <summary>
/// Gets the axis components of the quaternion.
/// </summary>
/// <value>The axis components of the quaternion.</value>
public Vector3 Axis
{
get
@@ -270,14 +259,9 @@ namespace FlaxEngine
/// Gets or sets the component at the specified index.
/// </summary>
/// <value>The value of the X, Y, Z, or W component, depending on the index.</value>
/// <param name="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.
/// </param>
/// <param name="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.</param>
/// <returns>The value of the component at the specified index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the <paramref name="index" /> is out of the range [0, 3].
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when the <paramref name="index" /> is out of the range [0, 3].</exception>
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.
/// </summary>
/// <returns>The length of the quaternion.</returns>
/// <remarks>
/// <see cref="Quaternion.LengthSquared" /> may be preferred when only the relative length is needed
/// and speed is of the essence.
/// </remarks>
/// <remarks><see cref="Quaternion.LengthSquared" /> may be preferred when only the relative length is needed and speed is of the essence.</remarks>
public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
/// <summary>
/// Calculates the squared length of the quaternion.
/// </summary>
/// <returns>The squared length of the quaternion.</returns>
/// <remarks>
/// This method may be preferred to <see cref="Quaternion.Length" /> when only a relative length is needed
/// and speed is of the essence.
/// </remarks>
/// <remarks>This method may be preferred to <see cref="Quaternion.Length" /> when only a relative length is needed and speed is of the essence.</remarks>
public float LengthSquared => X * X + Y * Y + Z * Z + W * W;
/// <summary>
@@ -1238,8 +1214,7 @@ namespace FlaxEngine
}
/// <summary>
/// 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.
/// </summary>
/// <param name="eulerAngles">The pitch, yaw and roll angles of rotation.</param>
/// <returns>When the method completes, contains the newly created quaternion.</returns>
@@ -1254,8 +1229,7 @@ namespace FlaxEngine
}
/// <summary>
/// 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.
/// </summary>
/// <param name="eulerAngles">The pitch, yaw and roll angles of rotation.</param>
/// <param name="result">When the method completes, contains the newly created quaternion.</param>
@@ -1269,8 +1243,7 @@ namespace FlaxEngine
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">The pitch of rotation (in degrees).</param>
/// <param name="y">The yaw of rotation (in degrees).</param>
@@ -1287,8 +1260,7 @@ namespace FlaxEngine
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">The pitch of rotation (in degrees).</param>
/// <param name="y">The yaw of rotation (in degrees).</param>
@@ -1304,8 +1276,7 @@ namespace FlaxEngine
}
/// <summary>
/// Creates a quaternion given a yaw, pitch, and roll value.
/// Angles are in radians. Use <see cref="Mathf.RadiansToDegrees"/> to convert degrees to radians.
/// Creates a quaternion given a yaw, pitch, and roll value. Angles are in radians. Use <see cref="Mathf.RadiansToDegrees"/> to convert degrees to radians.
/// </summary>
/// <param name="yaw">The yaw of rotation (in radians).</param>
/// <param name="pitch">The pitch of rotation (in radians).</param>
@@ -1331,8 +1302,7 @@ namespace FlaxEngine
}
/// <summary>
/// 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.
/// </summary>
/// <param name="yaw">The yaw of rotation (in radians).</param>
/// <param name="pitch">The pitch of rotation (in radians).</param>
@@ -1523,10 +1493,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Quaternion left, Quaternion right)
{
@@ -1538,10 +1505,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Quaternion left, Quaternion right)
{
@@ -1551,9 +1515,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, _formatString, X, Y, Z, W);
@@ -1563,9 +1525,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="format">The format.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format)
{
if (format == null)
@@ -1579,9 +1539,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(IFormatProvider formatProvider)
{
return string.Format(formatProvider, _formatString, X, Y, Z, W);
@@ -1592,9 +1550,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="format">The format.</param>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null)
@@ -1607,9 +1563,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode()
{
unchecked
@@ -1651,9 +1605,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Quaternion" /> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="Quaternion" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ref Quaternion other)
{
@@ -1665,9 +1617,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Quaternion" /> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="Quaternion" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Quaternion other)
{
@@ -1678,9 +1628,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object value)
{
if (!(value is Quaternion))

View File

@@ -10,7 +10,7 @@
/// <summary>
/// Very basic pseudo numbers generator.
/// </summary>
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);
}
/// <summary>
/// Gets a random <see cref="Vector3"/> with components in a range between [0;1].
/// </summary>
/// <returns>A random <see cref="Vector3"/>.</returns>
Vector3 GetVector3() const
{
return Vector3(GetFraction(), GetFraction(), GetFraction());
}
/// <summary>
/// Helper function for rand implementations.

View File

@@ -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 <ThirdParty/catch2/catch.hpp>
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));
}
}
}