diff --git a/test/Makefile.am b/test/Makefile.am
index 3fed4aee3c..e3721425f2 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -27,5 +27,6 @@ drtest_LDADD = $(top_builddir)/libs/scene/libscenegraph.la \
drtest_SOURCES = Camera.cpp \
CSG.cpp \
HeadlessOpenGLContext.cpp \
+ Math.cpp \
ModelScale.cpp \
SelectionAlgorithm.cpp
\ No newline at end of file
diff --git a/test/Math.cpp b/test/Math.cpp
new file mode 100644
index 0000000000..b0303ff69d
--- /dev/null
+++ b/test/Math.cpp
@@ -0,0 +1,560 @@
+#include "RadiantTest.h"
+
+#include "math/Matrix4.h"
+#include "math/Quaternion.h"
+
+namespace test
+{
+
+namespace
+{
+ double angle = 30.0;
+
+ double cosAngle = cos(degrees_to_radians(angle));
+ double sinAngle = sin(degrees_to_radians(angle));
+ double EPSILON = 0.00001f;
+}
+
+TEST(Matrix4, getIdentity)
+{
+ const Matrix4 identity = Matrix4::getIdentity();
+
+ EXPECT_TRUE(identity.xx() == 1) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.xy() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.xz() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.xw() == 0) << "Wrong values in identity matrix";
+
+ EXPECT_TRUE(identity.yx() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.yy() == 1) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.yz() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.yw() == 0) << "Wrong values in identity matrix";
+
+ EXPECT_TRUE(identity.zx() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.zy() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.zz() == 1) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.zw() == 0) << "Wrong values in identity matrix";
+
+ EXPECT_TRUE(identity.tx() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.ty() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.tz() == 0) << "Wrong values in identity matrix";
+ EXPECT_TRUE(identity.tw() == 1) << "Wrong values in identity matrix";
+
+ Matrix4 identity2;
+
+ identity2.xx() = 1;
+ identity2.xy() = 0;
+ identity2.xz() = 0;
+ identity2.xw() = 0;
+
+ identity2.yx() = 0;
+ identity2.yy() = 1;
+ identity2.yz() = 0;
+ identity2.yw() = 0;
+
+ identity2.zx() = 0;
+ identity2.zy() = 0;
+ identity2.zz() = 1;
+ identity2.zw() = 0;
+
+ identity2.tx() = 0;
+ identity2.ty() = 0;
+ identity2.tz() = 0;
+ identity2.tw() = 1;
+
+ EXPECT_TRUE(identity == identity2) << "Explicitly constructed identity not equal to Matrix4::getIdentity()";
+}
+
+TEST(Matrix4, getRotationAboutXDegrees)
+{
+ double angle = 30.0;
+ double cosAngle = cos(degrees_to_radians(angle));
+ double sinAngle = sin(degrees_to_radians(angle));
+
+ // Test X rotation
+ auto xRot = Matrix4::getRotationAboutXDegrees(angle);
+
+ EXPECT_DOUBLE_EQ(xRot.xx(), 1), "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.xy(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.xz(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.xw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(xRot.yx(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.yy(), cosAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.yz(), sinAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.yw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(xRot.zx(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.zy(), -sinAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.zz(), cosAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.zw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(xRot.tx(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.ty(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.tz(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(xRot.tw(), 1) << "Matrix rotation constructor failed";
+}
+
+TEST(Matrix4, getRotationAboutYDegrees)
+{
+ double angle = 30.0;
+ double cosAngle = cos(degrees_to_radians(angle));
+ double sinAngle = sin(degrees_to_radians(angle));
+
+ // Test Y rotation
+ auto yRot = Matrix4::getRotationAboutYDegrees(angle);
+
+ EXPECT_DOUBLE_EQ(yRot.xx(), cosAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.xy(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.xz(), -sinAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.xw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(yRot.yx(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.yy(), 1) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.yz(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.yw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(yRot.zx(), sinAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.zy(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.zz(), cosAngle) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.zw(), 0) << "Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(yRot.tx(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.ty(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.tz(), 0) << "Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(yRot.tw(), 1) << "Matrix rotation constructor failed";
+}
+
+TEST(Matrix4, getRotationAboutZDegrees)
+{
+ double angle = 30.0;
+ double cosAngle = cos(degrees_to_radians(angle));
+ double sinAngle = sin(degrees_to_radians(angle));
+
+ // Test Z rotation
+ auto zRot = Matrix4::getRotationAboutZDegrees(angle);
+
+ EXPECT_DOUBLE_EQ(zRot.xx(), cosAngle) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.xy(), sinAngle) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.xz(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.xw(), 0) <<"Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(zRot.yx(), -sinAngle) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.yy(), cosAngle) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.yz(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.yw(), 0) <<"Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(zRot.zx(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.zy(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.zz(), 1) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.zw(), 0) <<"Matrix rotation constructor failed";
+
+ EXPECT_DOUBLE_EQ(zRot.tx(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.ty(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.tz(), 0) <<"Matrix rotation constructor failed";
+ EXPECT_DOUBLE_EQ(zRot.tw(), 1) <<"Matrix rotation constructor failed";
+}
+
+TEST(Matrix4, getRotationForEulerXYZDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerXYZ = Matrix4::getRotationForEulerXYZDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerXYZ.xx(), cy * cz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.xy(), cy * sz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.xz(), -sy) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.xw(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXYZ.yx(), sx * sy * cz - cx * sz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.yy(), sx * sy * sz + cx * cz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.yz(), sx * cy) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.yw(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXYZ.zx(), cx * sy * cz + sx * sz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.zy(), cx * sy * sz - sx * cz) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.zz(), cx * cy) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.zw(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXYZ.tx(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.ty(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.tz(), 0) <<"Matrix getRotationForEulerXYZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXYZ.tw(), 1) <<"Matrix getRotationForEulerXYZDegrees failed";
+
+ // Test Euler Angle retrieval (XYZ)
+ Vector3 testEuler = eulerXYZ.getEulerAnglesXYZDegrees();
+
+ EXPECT_DOUBLE_EQ(testEuler.x(), euler.x()) << "getEulerAnglesXYZDegrees fault at x()";
+ EXPECT_DOUBLE_EQ(testEuler.y(), euler.y()) << "getEulerAnglesXYZDegrees fault at y()";
+ EXPECT_DOUBLE_EQ(testEuler.z(), euler.z()) << "getEulerAnglesXYZDegrees fault at z()";
+}
+
+TEST(Matrix4, getRotationForEulerYZXDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerYZX = Matrix4::getRotationForEulerYZXDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerYZX.xx(), cy * cz) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.xy(), cx * cy * sz + sx * sy) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.xz(), sx * cy * sz - cx * sy) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.xw(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYZX.yx(), -sz) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.yy(), cx * cz) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.yz(), sx * cz) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.yw(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYZX.zx(), sy * cz) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.zy(), cx * sy * sz - sx * cy) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.zz(), sx * sy * sz + cx * cy) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.zw(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYZX.tx(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.ty(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.tz(), 0) << "Matrix getRotationForEulerYZXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYZX.tw(), 1) << "Matrix getRotationForEulerYZXDegrees failed";
+}
+
+TEST(Matrix4, getRotationForEulerXZYDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerXZY = Matrix4::getRotationForEulerXZYDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerXZY.xx(), cy * cz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.xy(), sz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.xz(), -sy * cz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.xw(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXZY.yx(), sx * sy - cx * cy * sz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.yy(), cx * cz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.yz(), cx * sy * sz + sx * cy) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.yw(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXZY.zx(), sx * cy * sz + cx * sy) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.zy(), -sx * cz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.zz(), cx * cy - sx * sy * sz) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.zw(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerXZY.tx(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.ty(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.tz(), 0) << "Matrix getRotationForEulerXZYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerXZY.tw(), 1) << "Matrix getRotationForEulerXZYDegrees failed";
+}
+
+TEST(Matrix4, getRotationForEulerYXZDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerYXZ = Matrix4::getRotationForEulerYXZDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerYXZ.xx(), cy * cz - sx * sy * sz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.xy(), cy * sz + sx * sy * cz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.xz(), -cx * sy) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.xw(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYXZ.yx(), -cx * sz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.yy(), cx * cz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.yz(), sx) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.yw(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYXZ.zx(), sy * cz + sx * cy * sz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.zy(), sy * sz - sx * cy * cz) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.zz(), cx * cy) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.zw(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerYXZ.tx(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.ty(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.tz(), 0) << "Matrix getRotationForEulerYXZDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerYXZ.tw(), 1) << "Matrix getRotationForEulerYXZDegrees failed";
+
+ // Test Euler Angle retrieval (YXZ)
+ Vector3 testEuler = eulerYXZ.getEulerAnglesYXZDegrees();
+
+ EXPECT_DOUBLE_EQ(testEuler.x(), euler.x()) << "getEulerAnglesYXZDegrees fault at x()";
+ EXPECT_DOUBLE_EQ(testEuler.y(), euler.y()) << "getEulerAnglesYXZDegrees fault at y()";
+ EXPECT_DOUBLE_EQ(testEuler.z(), euler.z()) << "getEulerAnglesYXZDegrees fault at z()";
+}
+
+TEST(Matrix4, getRotationForEulerZXYDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerZXY = Matrix4::getRotationForEulerZXYDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerZXY.xx(), cy * cz + sx * sy * sz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.xy(), cx * sz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.xz(), sx * cy * sz - sy * cz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.xw(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZXY.yx(), sx * sy * cz - cy * sz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.yy(), cx * cz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.yz(), sx * cy * cz + sy * sz) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.yw(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZXY.zx(), cx * sy) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.zy(), -sx) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.zz(), cx * cy) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.zw(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZXY.tx(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.ty(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.tz(), 0) << "Matrix getRotationForEulerZXYDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZXY.tw(), 1) << "Matrix getRotationForEulerZXYDegrees failed";
+
+ // Test Euler Angle retrieval (ZXY)
+ Vector3 testEuler = eulerZXY.getEulerAnglesZXYDegrees();
+
+ EXPECT_DOUBLE_EQ(testEuler.x(), euler.x()) << "getEulerAnglesZXYDegrees fault at x()";
+ EXPECT_DOUBLE_EQ(testEuler.y(), euler.y()) << "getEulerAnglesZXYDegrees fault at y()";
+ EXPECT_DOUBLE_EQ(testEuler.z(), euler.z()) << "getEulerAnglesZXYDegrees fault at z()";
+}
+
+TEST(Matrix4, getRotationForEulerZYXDegrees)
+{
+ // Test euler angle constructors
+ Vector3 euler(30, -55, 75);
+
+ // Convert degrees to radians
+ double pi = 3.141592653589793238462643383f;
+ double cx = cos(euler[0] * c_pi / 180.0f);
+ double sx = sin(euler[0] * c_pi / 180.0f);
+ double cy = cos(euler[1] * c_pi / 180.0f);
+ double sy = sin(euler[1] * c_pi / 180.0f);
+ double cz = cos(euler[2] * c_pi / 180.0f);
+ double sz = sin(euler[2] * c_pi / 180.0f);
+
+ Matrix4 eulerZYX = Matrix4::getRotationForEulerZYXDegrees(euler);
+
+ EXPECT_DOUBLE_EQ(eulerZYX.xx(), cy * cz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.xy(), cx * sz + sx * sy * cz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.xz(), sx * sz - cx * sy * cz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.xw(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZYX.yx(), -cy * sz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.yy(), cx * cz - sx * sy * sz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.yz(), sx * cz + cx * sy * sz) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.yw(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZYX.zx(), sy) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.zy(), -sx * cy) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.zz(), cx * cy) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.zw(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+
+ EXPECT_DOUBLE_EQ(eulerZYX.tx(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.ty(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.tz(), 0) << "Matrix getRotationForEulerZYXDegrees failed";
+ EXPECT_DOUBLE_EQ(eulerZYX.tw(), 1) << "Matrix getRotationForEulerZYXDegrees failed";
+
+ // Test Euler Angle retrieval (ZYX)
+ Vector3 testEuler = eulerZYX.getEulerAnglesZYXDegrees();
+
+ EXPECT_DOUBLE_EQ(testEuler.x(), euler.x()) << "getEulerAnglesZYXDegrees fault at x()";
+ EXPECT_DOUBLE_EQ(testEuler.y(), euler.y()) << "getEulerAnglesZYXDegrees fault at y()";
+ EXPECT_DOUBLE_EQ(testEuler.z(), euler.z()) << "getEulerAnglesZYXDegrees fault at z()";
+}
+
+TEST(Matrix4, Multiplication)
+{
+ auto a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59);
+ auto b = Matrix4::byColumns(61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137);
+
+ auto c = a.getMultipliedBy(b);
+
+ EXPECT_TRUE(c.xx() == 6252) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.xy() == 7076) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.xz() == 8196) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.xw() == 9430) << "Matrix multiplication failed";
+
+ EXPECT_TRUE(c.yx() == 8068) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.yy() == 9124) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.yz() == 10564) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.yw() == 12150) << "Matrix multiplication failed";
+
+ EXPECT_TRUE(c.zx() == 9432) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.zy() == 10696) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.zz() == 12400) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.zw() == 14298) << "Matrix multiplication failed";
+
+ EXPECT_TRUE(c.tx() == 11680) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.ty() == 13224) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.tz() == 15312) << "Matrix multiplication failed";
+ EXPECT_TRUE(c.tw() == 17618) << "Matrix multiplication failed";
+
+ // Test Pre-Multiplication
+ EXPECT_TRUE(b.getMultipliedBy(a) == a.getPremultipliedBy(b)) << "Matrix pre-multiplication mismatch";
+
+ // Create an affine matrix
+ Matrix4 affineA = a;
+
+ affineA.xw() = 0;
+ affineA.yw() = 0;
+ affineA.zw() = 0;
+ affineA.tw() = 1;
+
+ EXPECT_TRUE(affineA.isAffine()) << "Affine check failed";
+}
+
+TEST(Matrix4, Transformation)
+{
+ auto a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59);
+
+ {
+ Vector3 v(61, 67, 71);
+
+ Vector3 transformed = a.transformPoint(v);
+
+ EXPECT_TRUE(transformed.x() == 3156) << "Vector3 transformation failed";
+ EXPECT_TRUE(transformed.y() == 3692) << "Vector3 transformation failed";
+ EXPECT_TRUE(transformed.z() == 4380) << "Vector3 transformation failed";
+
+ Vector3 transformedDir = a.transformDirection(v);
+
+ EXPECT_TRUE(transformedDir.x() == 3113) << "Vector3 direction transformation failed";
+ EXPECT_TRUE(transformedDir.y() == 3645) << "Vector3 direction transformation failed";
+ EXPECT_TRUE(transformedDir.z() == 4327) << "Vector3 direction transformation failed";
+ }
+
+ {
+ Vector4 vector(83, 89, 97, 101);
+ Vector4 transformed = a.transform(vector);
+
+ EXPECT_TRUE(transformed.x() == 8562) << "Vector4 transformation failed";
+ EXPECT_TRUE(transformed.y() == 9682) << "Vector4 transformation failed";
+ EXPECT_TRUE(transformed.z() == 11214) << "Vector4 transformation failed";
+ EXPECT_TRUE(transformed.w() == 12896) << "Vector4 transformation failed";
+ }
+
+ EXPECT_TRUE(a.t().x() == 43) << "Matrix4::t failed";
+ EXPECT_TRUE(a.t().y() == 47) << "Matrix4::t failed";
+ EXPECT_TRUE(a.t().z() == 53) << "Matrix4::t failed";
+}
+
+TEST(Matrix4, getDeterminant)
+{
+ Matrix4 a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59);
+
+ EXPECT_TRUE(a.getDeterminant() == -448) << "Matrix determinant calculation failed";
+}
+
+TEST(Matrix4, getFullInverse)
+{
+ auto a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59);
+
+ auto inv = a.getFullInverse();
+
+ EXPECT_DOUBLE_EQ(inv.xx(), 0.39285714285714285) << "Matrix inversion failed on xx";
+ EXPECT_DOUBLE_EQ(inv.xy(), -0.71428571428571419) << "Matrix inversion failed on xy";
+ EXPECT_DOUBLE_EQ(inv.xz(), -0.3214285714285714) << "Matrix inversion failed on xz";
+ EXPECT_DOUBLE_EQ(inv.xw(), 0.42857142857142855) << "Matrix inversion failed on xw";
+
+ EXPECT_DOUBLE_EQ(inv.yx(), -0.27678571428571425) << "Matrix inversion failed on yx";
+ EXPECT_DOUBLE_EQ(inv.yy(), 0.4464285714285714) << "Matrix inversion failed on yy";
+ EXPECT_DOUBLE_EQ(inv.yz(), -0.33035714285714285) << "Matrix inversion failed on yz";
+ EXPECT_DOUBLE_EQ(inv.yw(), 0.10714285714285714) << "Matrix inversion failed on yw";
+
+ EXPECT_DOUBLE_EQ(inv.zx(), -0.6696428571428571) << "Matrix inversion failed on zx";
+ EXPECT_DOUBLE_EQ(inv.zy(), 0.6607142857142857) << "Matrix inversion failed on zy";
+ EXPECT_DOUBLE_EQ(inv.zz(), 0.99107142857142849) << "Matrix inversion failed on zz";
+ EXPECT_DOUBLE_EQ(inv.zw(), -0.8214285714285714) << "Matrix inversion failed on zw";
+
+ EXPECT_DOUBLE_EQ(inv.tx(), 0.5357142857142857) << "Matrix inversion failed on tx";
+ EXPECT_DOUBLE_EQ(inv.ty(), -0.42857142857142855) << "Matrix inversion failed on ty";
+ EXPECT_DOUBLE_EQ(inv.tz(), -0.39285714285714285) << "Matrix inversion failed on tz";
+ EXPECT_DOUBLE_EQ(inv.tw(), 0.3571428571428571) << "Matrix inversion failed on tw";
+}
+
+TEST(Quaternion, Multiplication)
+{
+ Quaternion q1(3, 5, 7, 11);
+ Quaternion q2(13, 17, 19, 23);
+
+ Quaternion product = q1.getMultipliedBy(q2);
+
+ EXPECT_TRUE(product.x() == 188) << "Quaternion multiplication failed on x";
+ EXPECT_TRUE(product.y() == 336) << "Quaternion multiplication failed on y";
+ EXPECT_TRUE(product.z() == 356) << "Quaternion multiplication failed on z";
+ EXPECT_TRUE(product.w() == -4) << "Quaternion multiplication failed on w";
+
+ Quaternion q1multiplied = q1;
+ q1multiplied.multiplyBy(q2);
+
+ EXPECT_TRUE(q1multiplied.x() == 188) << "Quaternion in-place multiplication failed on x";
+ EXPECT_TRUE(q1multiplied.y() == 336) << "Quaternion in-place multiplication failed on y";
+ EXPECT_TRUE(q1multiplied.z() == 356) << "Quaternion in-place multiplication failed on z";
+ EXPECT_TRUE(q1multiplied.w() == -4) << "Quaternion in-place multiplication failed on w";
+
+ Quaternion q1inverted = q1.getInverse();
+
+ EXPECT_TRUE(q1inverted.x() == -3) << "Quaternion inversion failed on x";
+ EXPECT_TRUE(q1inverted.y() == -5) << "Quaternion inversion failed on y";
+ EXPECT_TRUE(q1inverted.z() == -7) << "Quaternion inversion failed on z";
+ EXPECT_TRUE(q1inverted.w() == 11) << "Quaternion inversion failed on w";
+
+ Quaternion normalised = q1.getNormalised();
+
+ EXPECT_DOUBLE_EQ(normalised.x(), 0.2100420126042014) << "Quaternion normalisation failed on x";
+ EXPECT_DOUBLE_EQ(normalised.y(), 0.3500700210070024) << "Quaternion normalisation failed on y";
+ EXPECT_DOUBLE_EQ(normalised.z(), 0.4900980294098034) << "Quaternion normalisation failed on z";
+ EXPECT_DOUBLE_EQ(normalised.w(), 0.7701540462154052) << "Quaternion normalisation failed on w";
+
+ Vector3 point(13, 17, 19);
+
+ Vector3 transformed = q1.transformPoint(point);
+
+ EXPECT_TRUE(transformed.x() == q1.w() * q1.w() * point.x() + 2 * q1.y() * q1.w() * point.z() - 2 * q1.z() * q1.w() * point.y() + q1.x() * q1.x() * point.x() + 2 * q1.y() * q1.x() * point.y() + 2 * q1.z() * q1.x() * point.z() - q1.z() * q1.z() * point.x() - q1.y() * q1.y() * point.x(), "Quaternion point transformation failed on x");
+ EXPECT_TRUE(transformed.y() == 2 * q1.x() * q1.y() * point.x() + q1.y() * q1.y() * point.y() + 2 * q1.z() * q1.y() * point.z() + 2 * q1.w() * q1.z() * point.x() - q1.z() * q1.z() * point.y() + q1.w() * q1.w() * point.y() - 2 * q1.x() * q1.w() * point.z() - q1.x() * q1.x() * point.y(), "Quaternion point transformation failed on y");
+ EXPECT_TRUE(transformed.z() == 2 * q1.x() * q1.z() * point.x() + 2 * q1.y() * q1.z() * point.y() + q1.z() * q1.z() * point.z() - 2 * q1.w() * q1.y() * point.x() - q1.y() * q1.y() * point.z() + 2 * q1.w() * q1.x() * point.y() - q1.x() * q1.x() * point.z() + q1.w() * q1.w() * point.z(), "Quaternion point transformation failed on z");
+}
+
+}
diff --git a/tools/msvc/Tests/Tests.vcxproj b/tools/msvc/Tests/Tests.vcxproj
index 612656e97d..c31c8a2227 100644
--- a/tools/msvc/Tests/Tests.vcxproj
+++ b/tools/msvc/Tests/Tests.vcxproj
@@ -64,6 +64,7 @@
+
diff --git a/tools/msvc/Tests/Tests.vcxproj.filters b/tools/msvc/Tests/Tests.vcxproj.filters
index fda1485559..25163fec1d 100644
--- a/tools/msvc/Tests/Tests.vcxproj.filters
+++ b/tools/msvc/Tests/Tests.vcxproj.filters
@@ -6,6 +6,7 @@
+