summaryrefslogtreecommitdiff
path: root/Runtime/Math/MathTests.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Math/MathTests.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Math/MathTests.cpp')
-rw-r--r--Runtime/Math/MathTests.cpp726
1 files changed, 726 insertions, 0 deletions
diff --git a/Runtime/Math/MathTests.cpp b/Runtime/Math/MathTests.cpp
new file mode 100644
index 0000000..229406a
--- /dev/null
+++ b/Runtime/Math/MathTests.cpp
@@ -0,0 +1,726 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#if UNITY_WIN && !UNITY_WINRT
+#include "External/DirectX/builds/dx9include/d3dx9.h"
+#endif
+#include <vector>
+
+
+SUITE (MathTests)
+{
+
+#if UNITY_WIN
+#pragma warning( disable : 4723 ) //required for the divide by 0 that's happening in this test.
+#endif
+
+TEST (Math_Nan)
+{
+ struct
+ {
+ inline bool IsNANNew (float A)
+ {
+ // A NAN has an exponent of 255 (shifted left 23 positions) and a non-zero mantissa.
+ int exp = *(int*)&A & 0x7F800000;
+ int mantissa = *(int*)&A & 0x007FFFFF;
+ return exp == 0x7F800000 && mantissa != 0;
+ }
+
+ inline void operator () (bool expect, float A)
+ {
+ CHECK_EQUAL (IsNANNew (A), IsNAN (A));
+ CHECK_EQUAL (expect, IsNAN (A));
+ }
+ } CheckNAN;
+
+ float f = 0.0F;
+ float f0 = 0.0F;
+
+ f = f / f0;
+
+ CheckNAN (true, f);
+
+ CheckNAN (true, std::numeric_limits<float>::quiet_NaN ());
+ CheckNAN (true, std::numeric_limits<float>::signaling_NaN ());
+ CheckNAN (false, 1.0F);
+ CheckNAN (false, 0.0F);
+}
+
+#if UNITY_WIN
+#pragma warning( default : 4723 )
+#endif
+
+TEST (Math_Matrix)
+{
+ Matrix4x4f m0, m1, m2, m6;
+
+ for (int i = 0; i < 16; ++i)
+ {
+ m0.m_Data[i] = (float)i;
+ m1.m_Data[15 - i] = (float)i;
+ }
+
+ MultiplyMatrices4x4REF (&m0, &m1, &m2);
+ MultiplyMatrices4x4 (&m0, &m1, &m6);
+ CHECK_EQUAL (0, memcmp (m2.m_Data, m6.m_Data, sizeof(Matrix4x4f)));
+
+ TransposeMatrix4x4REF(&m0, &m2);
+ TransposeMatrix4x4(&m0, &m6);
+ CHECK_EQUAL (0, memcmp (m2.m_Data, m6.m_Data, sizeof(Matrix4x4f)));
+
+ Vector3f v (2.0F, 5.0F, 2.0F);
+ Vector3f res (2.0F, 5.0F, -2.0F);
+
+ Quaternionf q;
+ Quaternionf backConvertedQ;
+ Matrix3x3f m3;
+ Vector3f tempResult;
+
+ q = AxisAngleToQuaternion (Vector3f::yAxis, kPI / 2.0F);
+ QuaternionToMatrix (q, m3);
+
+ CHECK_EQUAL (true, CompareApproximately (RotateVectorByQuat(q, v), res));
+ CHECK_EQUAL (true, CompareApproximately (m3.MultiplyPoint3 (v), res));
+
+ MatrixToQuaternion (m3, backConvertedQ);
+ CHECK_EQUAL (true, CompareApproximately (backConvertedQ, q));
+
+ Vector3f axis;
+ float roll;
+
+ QuaternionToAxisAngle (backConvertedQ, &axis, &roll);
+ CHECK_EQUAL (true, CompareApproximately (axis, Vector3f::yAxis));
+ CHECK_CLOSE (kPI / 2.0F, roll, 0.000001F);
+
+ q = Inverse (q);
+ m3.Invert ();
+ MatrixToQuaternion (m3, backConvertedQ);
+ CHECK_EQUAL (true, CompareApproximately (backConvertedQ, q));
+
+ tempResult = RotateVectorByQuat (q, res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+ tempResult = RotateVectorByQuat (backConvertedQ, res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+ tempResult = m3.MultiplyPoint3 (res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+}
+
+
+
+TEST (Math_NormalizeFastTest)
+{
+ Vector3f input[] = { Vector3f (0.0f, 0.1f, 0.0f), Vector3f (0.0f, 0.0f, 0.0f), Vector3f (-0.0f, -0.0f, -0.0f) };
+ Vector3f output[] = { Vector3f (0.0f, 1.f, 0.0f), Vector3f (0.0f, 0.0f, 0.0f), Vector3f (-0.0f, -0.0f, -0.0f) };
+
+ for (int i=0;i<3;i++)
+ {
+ Vector3f normalized = NormalizeFast(input[i]);
+ CHECK (CompareApproximately (output[i] , normalized, 0.0001f));
+ }
+}
+
+
+
+TEST (Math_MatrixQuaternionConversion)
+{
+ Rand rand (GetTimeSinceStartup ());
+ for (int i = 0; i < 500; ++i)
+ {
+ Quaternionf rot = RandomQuaternion (rand);
+ Quaternionf outq, outq2;
+ Matrix3x3f m, outm;
+ QuaternionToMatrix (rot, m);
+ Vector3f angle;
+
+ MatrixToEuler (m, angle);
+ EulerToMatrix (angle, outm);
+ outq2 = EulerToQuaternion (angle);
+
+ MatrixToQuaternion (outm, outq);
+ CHECK (CompareApproximately (m , outm, 0.1f));
+ CHECK_CLOSE (1, Abs (Dot (outq, rot)), 0.01f);
+ CHECK_CLOSE (1, Abs (Dot (outq2, rot)), 0.01f);
+ }
+}
+
+
+TEST (Math_EulerAngles)
+{
+ struct
+ {
+ void operator() (float x, float y, float z)
+ {
+ Quaternionf quat = EulerToQuaternion (Vector3f (Deg2Rad (x), Deg2Rad (y), Deg2Rad (z)));
+ Matrix3x3f quatM;
+ QuaternionToMatrix (quat, quatM);
+
+ Vector3f euler = QuaternionToEuler (quat);
+ Vector3f eulerDeg (Rad2Deg (euler.x), Rad2Deg (euler.y), Rad2Deg (euler.z));
+ Quaternionf newquat = EulerToQuaternion (euler);
+
+ CHECK_CLOSE (Abs (Dot (newquat, quat)), 1, 0.01f);
+ }
+ } TestEulerAngles;
+
+ TestEulerAngles ( 90.0f, 45.0f, 0.0f);
+ TestEulerAngles ( 90.0f, 90.0f, 0.0f);
+ TestEulerAngles (270.0f, 0.0f, 0.0f);
+ TestEulerAngles (270.0f, 40.0f, 0.0f);
+}
+
+TEST (Math_EulerAnglesMatchAxisAngle)
+{
+ Quaternionf quat = AxisAngleToQuaternion(Vector3f::yAxis, Deg2Rad(20.0F));
+ Vector3f euler = QuaternionToEuler(quat);
+ CHECK_EQUAL (true, CompareApproximately (0, euler.x));
+ CHECK_EQUAL (true, CompareApproximately (Deg2Rad(20.0F), euler.y));
+ CHECK_EQUAL (true, CompareApproximately (0, euler.z));
+}
+
+// This test fails with the current version of QuaternionToEuler. The angles
+// being close to gimbal lock will snap to 90 degree increments.
+#if 0
+
+TEST (Math_QuaternionToEulerHandlesGimbalLock)
+{
+ Quaternionf quat = EulerToQuaternion (Vector3f (Deg2Rad (269.5f), 0.f, 0.f));
+// printf( "%f, %f, %f, %f\n", quat.x, quat.y, quat.z, quat.w);
+ Vector3f euler = QuaternionToEuler (quat);
+// printf( "%f, %f, %f\n", Rad2Deg(euler.x), Rad2Deg(euler.y), Rad2Deg(euler.z));
+ Quaternionf quat1 = EulerToQuaternion (euler);
+// printf( "%f, %f, %f, %f\n", quat1.x, quat1.y, quat1.z, quat1.w);
+
+ CHECK_CLOSE (269.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (89.5f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+ CHECK_CLOSE (89.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (89.0f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+ CHECK_CLOSE (89.0f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (88.5f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+// printf( "%f, %f, %f\n", Rad2Deg(euler.x), Rad2Deg(euler.y), Rad2Deg(euler.z));
+
+ CHECK_CLOSE (88.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+}
+
+#endif
+
+TEST (Math_ColorRGBA32Lerp)
+{
+#if UNITY_LINUX
+#warning Investigate/fix ColorRGBA32 Tests!
+#else
+ ColorRGBA32 c0, c1, res;
+
+ c0 = ColorRGBA32 (100, 150, 255, 0);
+ c1 = ColorRGBA32 (200, 100, 0, 255);
+
+ res = Lerp (c0, c1, 0);
+ CHECK (ColorRGBA32 (100, 150, 255, 0) == res);
+
+ res = Lerp (c0, c1, 90);
+ CHECK (ColorRGBA32 (135, 132,165,89) == res);
+
+ res = Lerp (c0, c1, 200);
+ CHECK (ColorRGBA32 (178, 110,55,199) == res);
+
+ res = Lerp (c0, c1, 255);
+ CHECK (ColorRGBA32 (199, 100, 0, 254) == res);
+#endif
+}
+
+
+TEST (Math_ColorRGBA32Scale)
+{
+#if UNITY_LINUX
+#warning Investigate/fix ColorRGBA32 Tests!
+#else
+ ColorRGBA32 c0, res;
+
+ c0 = ColorRGBA32 (100, 150, 255, 150);
+
+ res = c0 * 0;
+ CHECK (ColorRGBA32 (0, 0, 0, 0) == res);
+
+ res = c0 * 20;
+ CHECK (ColorRGBA32 (8, 12, 20, 12) == res);
+
+ res = c0 * 150;
+ CHECK (ColorRGBA32 (58, 88, 150, 88) == res);
+
+ res = c0 * 255;
+ CHECK (ColorRGBA32 (100, 150, 255, 150) == res);
+#endif
+}
+
+void TestMultiplyColorRGBA32(const ColorRGBA32 input0, const ColorRGBA32 input1, int tolerance)
+{
+ ColorRGBA32 expected;
+ ColorRGBA32 actual;
+ expected.r = (input0.r * input1.r) / 255;
+ expected.g = (input0.g * input1.g) / 255;
+ expected.b = (input0.b * input1.b) / 255;
+ expected.a = (input0.a * input1.a) / 255;
+ actual = input0*input1;
+
+ CHECK_CLOSE((int)expected.r, (int)actual.r, tolerance);
+ CHECK_CLOSE((int)expected.g, (int)actual.g, tolerance);
+ CHECK_CLOSE((int)expected.b, (int)actual.b, tolerance);
+ CHECK_CLOSE((int)expected.a, (int)actual.a, tolerance);
+}
+
+TEST (Math_ColorRGBA32Muliply)
+{
+ for(int i = 0; i < 256; i+=4)
+ {
+ TestMultiplyColorRGBA32(ColorRGBA32(0,0,0,0), ColorRGBA32(i+0,i+1,i+2,i+3), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(i+0,i+1,i+2,i+3), ColorRGBA32(0,0,0,0), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(i+0,i+1,i+2,i+3), ColorRGBA32(0xff,0xff,0xff,0xff), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(0xff,0xff,0xff,0xff), ColorRGBA32(i+0,i+1,i+2,i+3), 0);
+ }
+
+ for(int i = 0; i < 256; i+=4)
+ for(int j = i; j < 256; j+=4)
+ TestMultiplyColorRGBA32(ColorRGBA32(j+0,j+1,j+2,j+3), ColorRGBA32(i+0,i+1,i+2,i+3), 1);
+}
+
+// Reference Implementation: D3DX; thus only test on Windows
+#if UNITY_WIN && !UNITY_WINRT
+TEST (Math_SphericalHarmonics)
+{
+ Rand r;
+
+ for (int i = 0; i < 10000; ++i)
+ {
+ float x = r.GetFloat () * 2.0f - 1.0f;
+ float y = r.GetFloat () * 2.0f - 1.0f;
+ float z = r.GetFloat () * 2.0f - 1.0f;
+ float sh[9], d3dxsh[9];
+
+ SHEvalDirection9 (x, y, z, sh);
+ D3DXSHEvalDirection (d3dxsh, 3, &D3DXVECTOR3 (x, y, z));
+ for (int j = 0; j < 9; ++j)
+ {
+ CHECK_CLOSE (sh[j], d3dxsh[j], 0.000001f);
+ }
+
+ float shR[9], shG[9], shB[9];
+ float d3dxshR[9], d3dxshG[9], d3dxshB[9];
+ SHEvalDirectionalLight9 (x, y, z, 0.1f, 0.2f, 0.3f, shR, shG, shB);
+ D3DXSHEvalDirectionalLight (3, &D3DXVECTOR3(x,y,z), 0.1f, 0.2f, 0.3f, d3dxshR, d3dxshG, d3dxshB);
+ for (int j = 0; j < 9; ++j)
+ {
+ CHECK_CLOSE (shR[j], d3dxshR[j], 0.000001f);
+ CHECK_CLOSE (shG[j], d3dxshG[j], 0.000001f);
+ CHECK_CLOSE (shB[j], d3dxshB[j], 0.000001f);
+ }
+ }
+}
+#endif
+
+
+void FabsPerformance ();
+
+TEST (Math_Repeat)
+{
+ CHECK_EQUAL (15.0F, Repeat (-5.0F, 20.0F));
+ CHECK_EQUAL ( 5.0F, Repeat (5.0F, 20.0F));
+ CHECK_EQUAL ( 5.0F, Repeat (25.0F, 20.0F));
+ CHECK_EQUAL ( 0.0F, Repeat (20.0F, 20.0F));
+ CHECK_EQUAL ( 0.0F, Repeat (0.0F, 20.0F));
+ CHECK_EQUAL (19.9F, Repeat (-0.1F, 20.0F));
+ CHECK_EQUAL (10.0F, Repeat (-10.0F, 20.0F));
+ CHECK_CLOSE (0.139999F, Repeat (0.699999F, 0.14F), 1e-5f);
+ //CHECK (Repeat (0.69999999F, 0.14F) >= 0.0f) // This fails! Revisit for the next breaking version.
+
+ // Our Repeat inverts when in negative space
+ CHECK_CLOSE ( 3.0F, Repeat ( 3.0F, 5.0F), 1e-5f);
+ CHECK_CLOSE (-2.0F, Repeat ( 3.0F, -5.0F), 1e-5f);
+ CHECK_CLOSE (-3.0F, Repeat (-3.0F, -5.0F), 1e-5f);
+ CHECK_CLOSE ( 2.0F, Repeat (-3.0F, 5.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat ( 0.0F, -1.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat ( 0.0F, 1.0F), 1e-5f);
+
+ CHECK_CLOSE ( 1.0F, Repeat (-59.0F, 30.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat (-60.0F, 30.0F), 1e-5f);
+ CHECK_CLOSE (29.0F, Repeat (-61.0F, 30.0F), 1e-5f);
+}
+
+TEST (Math_DeltaAngleRad)
+{
+ CHECK_EQUAL (0, DeltaAngleRad (12345.67890F, 12345.67890F));
+
+ CHECK_EQUAL (kPI, DeltaAngleRad (0, -kPI));
+ CHECK_EQUAL (kPI, DeltaAngleRad (0, kPI));
+ CHECK_EQUAL (kPI, DeltaAngleRad (kPI, 0));
+
+ CHECK_EQUAL (0, DeltaAngleRad (1.0F, 1.0F+2*kPI));
+ CHECK_EQUAL (0, DeltaAngleRad (1.0F+2*kPI, 1.0F));
+
+ CHECK_CLOSE ( kPI/2, DeltaAngleRad (0, 5*kPI/2), 1e-5f);
+ CHECK_CLOSE (-kPI/2, DeltaAngleRad (0, 7*kPI/2), 1e-5f);
+}
+
+/*
+TEST (Math_RoundFunctions)
+{
+ struct
+ {
+ void operator() (float t, int floor, int ceil, int round)
+ {
+ CHECK_EQUAL (floor, std::floor(t));
+ CHECK_EQUAL (ceil, std::ceil(t));
+
+ CHECK_EQUAL (round, RoundfToInt(t));
+ CHECK_EQUAL (round, Roundf(t));
+
+ CHECK_EQUAL (floor, Floorf(t));
+ CHECK_EQUAL (floor, FloorfToInt(t));
+
+ CHECK_EQUAL (ceil, Ceilf(t));
+ CHECK_EQUAL (ceil, CeilfToInt(t));
+
+ if (t >= 0.0F)
+ {
+ CHECK_EQUAL (RoundfToIntPos(t), round);
+ CHECK_EQUAL (FloorfToIntPos(t), floor);
+ CHECK_EQUAL (CeilfToIntPos(t), ceil);
+ }
+ }
+ } TestRoundFunctions;
+
+ CHECK_EQUAL (64, NextPowerOfTwo (33));
+ CHECK_EQUAL (32, NextPowerOfTwo (32));
+ CHECK_EQUAL (32, NextPowerOfTwo (31));
+
+ TestRoundFunctions (15.1F, 15, 16, 15);
+ TestRoundFunctions (0.9F, 0, 1, 1);
+
+ TestRoundFunctions (1.0F, 1, 1, 1);
+ TestRoundFunctions (2.0F, 2, 2, 2);
+
+ TestRoundFunctions (5.9F, 5, 6, 6);
+ TestRoundFunctions (7.1F, 7, 8, 7);
+ TestRoundFunctions (7.6F, 7, 8, 8);
+ TestRoundFunctions (0.49F, 0, 1, 0);
+ TestRoundFunctions (120000.51F, 120000, 120001, 120001);
+
+ TestRoundFunctions (-19.7F, -20, -19, -20);
+ TestRoundFunctions (-16.01F, -17, -16, -16);
+ TestRoundFunctions (-25.0F, -25, -25, -25);
+ TestRoundFunctions (-25.501F, -26, -25, -26);
+ TestRoundFunctions (-5.9F, -6, -5, -6);
+ TestRoundFunctions (-7.1F, -8, -7, -7);
+ TestRoundFunctions (-7.6F, -8, -7, -8);
+ TestRoundFunctions (-0.1F, -1, 0, 0);
+ TestRoundFunctions (-0.0000011F, -1, 0, 0);
+ TestRoundFunctions (-0.25F, -1, 0, 0);
+ TestRoundFunctions (-0.49F, -1, 0, 0);
+ TestRoundFunctions (-0.51F, -1, 0, -1);
+ TestRoundFunctions (-0.6F, -1, 0, -1);
+ TestRoundFunctions (-1.0F, -1, -1, -1);
+ TestRoundFunctions (-2.0F, -2, -2, -2);
+ TestRoundFunctions (-1.01F, -2, -1, -1);
+ TestRoundFunctions (-100000.49F, -100001, -100000, -100000);
+
+ CHECK_EQUAL (1, RoundfToInt(0.5F));
+ CHECK_EQUAL (2, RoundfToInt(1.5F));
+ CHECK_EQUAL (0, RoundfToInt(-0.5F));
+ CHECK_EQUAL (-1, RoundfToInt(-1.5F));
+
+ // Rounding up or down, doesn't have to match floor / ceil function. Pick fastest
+ //ErrorIf (TestFloor (120000.51F, 120000, 120000, 120000));
+
+ CHECK_EQUAL (15, FloorfToIntPos (15.1F));
+ CHECK_EQUAL (0, FloorfToIntPos (0.9F));
+
+ CHECK_EQUAL (16, CeilfToIntPos (15.1F));
+ CHECK_EQUAL (1, CeilfToIntPos (0.9F));
+
+ CHECK_EQUAL (15, RoundfToIntPos (15.1F));
+ CHECK_EQUAL (1, RoundfToIntPos (0.9F));
+}
+*/
+
+TEST (Math_TransformPoints)
+{
+ Vector3f v (1, 0, 0);
+ Matrix4x4f tr;
+ tr.SetTR (Vector3f(10,0,0), AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90)));
+
+ //Must ignore the translation, and work when input and output are the same.
+ TransformPoints3x3 (tr, &v, &v, 1);
+
+ CHECK (CompareApproximately (v, Vector3f(0, 1, 0)));
+}
+
+
+TEST (TypeSizes)
+{
+ CHECK_EQUAL (4, sizeof(SInt32));
+ CHECK_EQUAL (4, sizeof(UInt32));
+
+ CHECK_EQUAL (2, sizeof(SInt16));
+ CHECK_EQUAL (2, sizeof(UInt16));
+
+ CHECK_EQUAL (1, sizeof(UInt8));
+ CHECK_EQUAL (1, sizeof(SInt8));
+
+ CHECK_EQUAL (8, sizeof(UInt64));
+ CHECK_EQUAL (8, sizeof(SInt64));
+}
+
+
+TEST (Math_QuaternionMatrixEquivalence)
+{
+ Matrix3x3f m;
+ EulerToMatrix (Vector3f (Deg2Rad (15.0F), Deg2Rad (20.0F), Deg2Rad (64.0F)), m);
+
+ Quaternionf q;
+ MatrixToQuaternion (m, q);
+
+ Vector3f v (25.3F, 27.14F, 34.2F);
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+ Vector3f quatRes = RotateVectorByQuat (q, v);
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Matrix3x3f m2 (m);
+ m2.Scale (Vector3f (1.0F, 1.0F, -1.0F));
+
+ CHECK (CompareApproximately (m2.GetDeterminant (), -1.0F));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+ Vector3f quatRes = RotateVectorByQuat (q, Vector3f (v.x, v.y, v.z));
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+}
+
+
+TEST (Math_ColorMisc)
+{
+ {
+ ColorRGBA32 c0 (100, 150, 100, 0);
+ ColorRGBA32 c1 (200, 100, 0, 200);
+ ColorRGBA32 res = c0 + c1;
+ CHECK (res == ColorRGBA32 (255, 250, 100, 200));
+ }
+
+ {
+ ColorRGBA32 res = ColorRGBA32 (150, 150, 150, 150) + ColorRGBA32 (150, 150, 150, 150);
+ CHECK (res == ColorRGBA32 (255, 255, 255, 255));
+ }
+}
+
+
+TEST (Math_TransformAABB)
+{
+ Matrix4x4f m;
+
+ for (int i = 0; i < 16; ++i)
+ m.m_Data[i] = (float)(7-i);
+
+ AABB aabb(Vector3f(1,2,3), Vector3f(4,5,6));
+
+ AABB aabbSlow;
+ TransformAABBSlow(aabb, m, aabbSlow);
+
+ AABB aabbRef;
+ TransformAABB(aabb, m, aabbRef);
+
+ CHECK (CompareApproximately (aabbSlow.m_Center, aabbRef.m_Center));
+ CHECK (CompareApproximately (aabbSlow.m_Extent, aabbRef.m_Extent));
+}
+
+TEST (Math_BitsInMask)
+{
+ CHECK_EQUAL (0, BitsInMask(0x0));
+ CHECK_EQUAL (32, BitsInMask(0xFFFFFFFF));
+ CHECK_EQUAL (1, BitsInMask(0x1));
+ CHECK_EQUAL (1, BitsInMask(0x80000000));
+ CHECK_EQUAL (2, BitsInMask(0x5));
+ CHECK_EQUAL (3, BitsInMask(0x7));
+ CHECK_EQUAL (24, BitsInMask(0xDEADBEEF));
+ CHECK_EQUAL (19, BitsInMask(0xCAFE1337));
+}
+
+TEST (Math_BitsInMask64)
+{
+ CHECK_EQUAL (0, BitsInMask64(0x0000000000000000ULL));
+ CHECK_EQUAL (64, BitsInMask64(0xFFFFFFFFFFFFFFFFULL));
+ CHECK_EQUAL (1, BitsInMask64(0x0000000000000001ULL));
+ CHECK_EQUAL (2, BitsInMask64(0x8000000080000000ULL));
+ CHECK_EQUAL (2, BitsInMask64(0x0000000000000005ULL));
+ CHECK_EQUAL (3, BitsInMask64(0x0000000000000007ULL));
+ CHECK_EQUAL (24, BitsInMask64(0x00000000DEADBEEFULL));
+ CHECK_EQUAL (19, BitsInMask64(0x00000000CAFE1337ULL));
+ CHECK_EQUAL (43, BitsInMask64(0xCAFE1337DEADBEEFULL));
+}
+
+TEST (Math_Normalize)
+{
+ Plane p;
+ p.SetABCD(0,0,0,1);
+ p.NormalizeRobust();
+ Vector3f n = p.GetNormal();
+ CHECK (IsNormalized(n));
+
+ p.SetABCD(2.5e-5f, 3.1e-5f, 1.2e-5f, 1.f);
+ p.NormalizeRobust();
+ n = p.GetNormal();
+ CHECK (IsNormalized(n));
+
+ Vector3f normal(2.3e-5f, 2.1e-5f, 3.2e-5f);
+ float invOriginalLength;
+ normal = NormalizeRobust(normal, invOriginalLength);
+ CHECK (CompareApproximately (22394.295f, invOriginalLength));
+}
+
+}
+
+
+#endif