Matrix3x4Tests.cpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/UnitTest/TestTypes.h>
  9. #include <AzCore/Math/Matrix3x4.h>
  10. #include <AzCore/Math/Matrix3x3.h>
  11. #include <AzCore/Math/Quaternion.h>
  12. #include <AZTestShared/Math/MathTestHelpers.h>
  13. #include <AzCore/Math/Vector4.h>
  14. #include "MathTestData.h"
  15. namespace UnitTest
  16. {
  17. using Matrix3x4CreateFixture = ::testing::TestWithParam<AZ::Vector3>;
  18. TEST_P(Matrix3x4CreateFixture, CreateIdentity)
  19. {
  20. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
  21. const AZ::Vector3 vector = GetParam();
  22. EXPECT_THAT(matrix * vector, IsClose(vector));
  23. }
  24. TEST_P(Matrix3x4CreateFixture, Identity)
  25. {
  26. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::Identity();
  27. const AZ::Vector3 vector = GetParam();
  28. EXPECT_THAT(matrix * vector, IsClose(vector));
  29. }
  30. TEST_P(Matrix3x4CreateFixture, CreateZero)
  31. {
  32. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateZero();
  33. const AZ::Vector3 vector = GetParam();
  34. EXPECT_THAT(matrix * vector, IsClose(AZ::Vector3::CreateZero()));
  35. }
  36. TEST_P(Matrix3x4CreateFixture, CreateTranslation)
  37. {
  38. const AZ::Vector3 translation(0.8f, 2.3f, -1.9f);
  39. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateTranslation(translation);
  40. const AZ::Vector3 vector = GetParam();
  41. EXPECT_THAT(matrix * vector, IsClose(vector + translation));
  42. }
  43. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateFixture, ::testing::ValuesIn(MathTestData::Vector3s));
  44. TEST(MATH_Matrix3x4, CreateFromValue)
  45. {
  46. const float value = 2.3f;
  47. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromValue(value);
  48. for (int row = 0; row < 3; ++row)
  49. {
  50. for (int col = 0; col < 4; ++col)
  51. {
  52. EXPECT_NEAR(matrix.GetElement(row, col), value, 1e-3f);
  53. }
  54. }
  55. }
  56. TEST(MATH_Matrix3x4, CreateFromRowMajorFloat12)
  57. {
  58. const float values[12] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f };
  59. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRowMajorFloat12(values);
  60. float storedValues[12];
  61. matrix.StoreToRowMajorFloat12(storedValues);
  62. EXPECT_THAT(storedValues, ::testing::Pointwise(::testing::FloatNear(1e-5f), values));
  63. EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 5.0f, 9.0f)));
  64. EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(2.0f, 6.0f, 10.0f)));
  65. EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(3.0f, 7.0f, 11.0f)));
  66. EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(4.0f, 8.0f, 12.0f)));
  67. }
  68. TEST(MATH_Matrix3x4, CreateFromColumnMajorFloat12)
  69. {
  70. const float values[12] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f };
  71. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumnMajorFloat12(values);
  72. float storedValues[12];
  73. matrix.StoreToColumnMajorFloat12(storedValues);
  74. EXPECT_THAT(storedValues, ::testing::Pointwise(::testing::FloatNear(1e-5f), values));
  75. EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 2.0f, 3.0f)));
  76. EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(4.0f, 5.0f, 6.0f)));
  77. EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(7.0f, 8.0f, 9.0f)));
  78. EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(10.0f, 11.0f, 12.0f)));
  79. }
  80. TEST(MATH_Matrix3x4, CreateFromColumnMajorFloat16)
  81. {
  82. const float values[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f };
  83. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumnMajorFloat16(values);
  84. float storedValues[16];
  85. matrix.StoreToColumnMajorFloat16(storedValues);
  86. for (int row = 0; row < 3; ++row)
  87. {
  88. for (int col = 0; col < 4; ++col)
  89. {
  90. EXPECT_THAT(storedValues[4 * col + row], ::testing::FloatNear(values[4 * col + row], 1e-5f));
  91. }
  92. }
  93. EXPECT_THAT(matrix.GetColumn(0), IsClose(AZ::Vector3(1.0f, 2.0f, 3.0f)));
  94. EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(5.0f, 6.0f, 7.0f)));
  95. EXPECT_THAT(matrix.GetColumn(2), IsClose(AZ::Vector3(9.0f, 10.0f, 11.0f)));
  96. EXPECT_THAT(matrix.GetColumn(3), IsClose(AZ::Vector3(13.0f, 14.0f, 15.0f)));
  97. }
  98. using Matrix3x4CreateRotationFixture = ::testing::TestWithParam<float>;
  99. TEST_P(Matrix3x4CreateRotationFixture, CreateRotationX)
  100. {
  101. const float angle = GetParam();
  102. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationX(angle);
  103. EXPECT_TRUE(matrix.IsOrthogonal());
  104. const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
  105. const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
  106. // rotating a vector should not affect its length
  107. EXPECT_NEAR(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance);
  108. // rotating about the X axis should not affect the X component
  109. EXPECT_NEAR(rotatedVector.GetX(), vector.GetX(), AZ::Constants::Tolerance);
  110. // when projected into the Y-Z plane, the angle between the rotated vector and the original vector
  111. // should wrap to the same as the input angle parameter
  112. const float xSquared = vector.GetX() * vector.GetX();
  113. const float projectedDotProduct = rotatedVector.Dot(vector) - xSquared;
  114. const float projectedMagnitudeSq = vector.Dot(vector) - xSquared;
  115. EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
  116. }
  117. TEST_P(Matrix3x4CreateRotationFixture, CreateRotationY)
  118. {
  119. const float angle = GetParam();
  120. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationY(angle);
  121. EXPECT_TRUE(matrix.IsOrthogonal());
  122. const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
  123. const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
  124. // rotating a vector should not affect its length
  125. EXPECT_NEAR(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance);
  126. // rotating about the Y axis should not affect the Y component
  127. EXPECT_NEAR(rotatedVector.GetY(), vector.GetY(), AZ::Constants::Tolerance);
  128. // when projected into the X-Z plane, the angle between the rotated vector and the original vector
  129. // should wrap to the same as the input angle parameter
  130. const float ySquared = vector.GetY() * vector.GetY();
  131. const float projectedDotProduct = rotatedVector.Dot(vector) - ySquared;
  132. const float projectedMagnitudeSq = vector.Dot(vector) - ySquared;
  133. EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
  134. }
  135. TEST_P(Matrix3x4CreateRotationFixture, CreateRotationZ)
  136. {
  137. const float angle = GetParam();
  138. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateRotationZ(angle);
  139. EXPECT_TRUE(matrix.IsOrthogonal());
  140. const AZ::Vector3 vector(1.5f, -0.2f, 2.7f);
  141. const AZ::Vector3 rotatedVector = matrix.TransformVector(vector);
  142. // rotating a vector should not affect its length
  143. EXPECT_NEAR(rotatedVector.GetLengthSq(), vector.GetLengthSq(), AZ::Constants::Tolerance);
  144. // rotating about the Z axis should not affect the Z component
  145. EXPECT_NEAR(rotatedVector.GetZ(), vector.GetZ(), AZ::Constants::Tolerance);
  146. // when projected into the X-Y plane, the angle between the rotated vector and the original vector
  147. // should wrap to the same as the input angle parameter
  148. const float zSquared = vector.GetZ() * vector.GetZ();
  149. const float projectedDotProduct = rotatedVector.Dot(vector) - zSquared;
  150. const float projectedMagnitudeSq = vector.Dot(vector) - zSquared;
  151. EXPECT_NEAR(projectedDotProduct, projectedMagnitudeSq * cosf(angle), 1e-3f);
  152. }
  153. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateRotationFixture, ::testing::ValuesIn(MathTestData::Angles));
  154. TEST(MATH_Matrix3x4, CreateFromRows)
  155. {
  156. const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
  157. const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
  158. const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
  159. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
  160. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  161. const AZ::Vector3 transformedVector = matrix * vector;
  162. const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
  163. EXPECT_THAT(transformedVector, IsClose(expected));
  164. }
  165. TEST(MATH_Matrix3x4, GetSetRows)
  166. {
  167. const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
  168. const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
  169. const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
  170. AZ::Matrix3x4 matrix;
  171. matrix.SetRows(row0, row1, row2);
  172. AZ::Vector4 rows[3];
  173. matrix.GetRows(&rows[0], &rows[1], &rows[2]);
  174. EXPECT_THAT(matrix.GetRow(0), IsClose(rows[0]));
  175. EXPECT_THAT(matrix.GetRow(1), IsClose(rows[1]));
  176. EXPECT_THAT(matrix.GetRow(2), IsClose(rows[2]));
  177. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  178. const AZ::Vector3 transformedVector = matrix * vector;
  179. const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
  180. EXPECT_THAT(transformedVector, IsClose(expected));
  181. }
  182. TEST(MATH_Matrix3x4, GetSetRow)
  183. {
  184. AZ::Matrix3x4 matrix;
  185. const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
  186. matrix.SetRow(0, row0);
  187. const AZ::Vector3 row1(0.384f, -1.92f, 0.428f);
  188. const float w1 = -1.6f;
  189. matrix.SetRow(1, row1, w1);
  190. const float x2 = 1.28f;
  191. const float y2 = -2.4f;
  192. const float z2 = -0.24f;
  193. const float w2 = 3.7f;
  194. matrix.SetRow(2, x2, y2, z2, w2);
  195. EXPECT_THAT(matrix.GetRow(0), IsClose(row0));
  196. EXPECT_THAT(matrix.GetRow(2), IsClose(AZ::Vector4(x2, y2, z2, w2)));
  197. EXPECT_THAT(matrix.GetRowAsVector3(0), IsClose(row0.GetAsVector3()));
  198. EXPECT_THAT(matrix.GetRowAsVector3(1), IsClose(row1));
  199. EXPECT_THAT(matrix.GetRowAsVector3(2), IsClose(AZ::Vector3(x2, y2, z2)));
  200. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  201. const AZ::Vector3 transformedVector = matrix * vector;
  202. const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
  203. EXPECT_THAT(transformedVector, IsClose(expected));
  204. }
  205. TEST(MATH_Matrix3x4, CreateFromColumns)
  206. {
  207. const AZ::Vector3 col0(1.488f, 0.384f, 1.28f);
  208. const AZ::Vector3 col1(2.56f, -1.92f, -2.4f);
  209. const AZ::Vector3 col2(0.096f, 0.428f, -0.24f);
  210. const AZ::Vector3 col3(2.3f, -1.6f, 3.7f);
  211. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumns(col0, col1, col2, col3);
  212. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  213. const AZ::Vector3 transformedVector = matrix * vector;
  214. const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
  215. EXPECT_THAT(transformedVector, IsClose(expected));
  216. }
  217. TEST(MATH_Matrix3x4, GetSetColumns)
  218. {
  219. const AZ::Vector3 inputCol0(2.2f, -0.4f, -1.2f);
  220. const AZ::Vector3 inputCol1(-0.3f, 1.2f, 0.2f);
  221. const AZ::Vector3 inputCol2(0.6f, -0.2f, 1.7f);
  222. const AZ::Vector3 inputCol3(3.2f, 1.7f, -0.9f);
  223. AZ::Matrix3x4 matrix;
  224. matrix.SetColumns(inputCol0, inputCol1, inputCol2, inputCol3);
  225. AZ::Vector3 col0, col1, col2, col3;
  226. matrix.GetColumns(&col0, &col1, &col2, &col3);
  227. EXPECT_THAT(col0, IsClose(inputCol0));
  228. EXPECT_THAT(col1, IsClose(inputCol1));
  229. EXPECT_THAT(col2, IsClose(inputCol2));
  230. EXPECT_THAT(col3, IsClose(inputCol3));
  231. }
  232. TEST(MATH_Matrix3x4, GetSetColumn)
  233. {
  234. const AZ::Vector3 inputCol0(1.3f, 1.4f, -0.2f);
  235. const float x = -0.7f;
  236. const float y = 1.2f;
  237. const float z = -0.4f;
  238. AZ::Matrix3x4 matrix;
  239. matrix.SetColumn(0, inputCol0);
  240. matrix.SetColumn(1, x, y, z);
  241. EXPECT_THAT(matrix.GetColumn(0), IsClose(inputCol0));
  242. EXPECT_THAT(matrix.GetColumn(1), IsClose(AZ::Vector3(x, y, z)));
  243. }
  244. TEST(MATH_Matrix3x4, GetSetBasisAndTranslation)
  245. {
  246. const AZ::Vector3 inputBasisX(-1.9f, -0.2f, 2.3f);
  247. const AZ::Vector3 inputBasisY(1.4f, 0.1f, -1.9f);
  248. const AZ::Vector3 inputBasisZ(2.1f, 0.6f, 1.1f);
  249. const AZ::Vector3 inputTranslation(-0.4f, -0.9f, -1.3f);
  250. AZ::Matrix3x4 matrix;
  251. matrix.SetBasisAndTranslation(inputBasisX, inputBasisY, inputBasisZ, inputTranslation);
  252. AZ::Vector3 basisX, basisY, basisZ, translation;
  253. matrix.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
  254. EXPECT_THAT(basisX, IsClose(inputBasisX));
  255. EXPECT_THAT(basisY, IsClose(inputBasisY));
  256. EXPECT_THAT(basisZ, IsClose(inputBasisZ));
  257. EXPECT_THAT(translation, IsClose(inputTranslation));
  258. }
  259. TEST(MATH_Matrix3x4, GetSetBasisX)
  260. {
  261. const AZ::Vector3 inputBasisX(1.6f, 1.3f, -0.8f);
  262. const float x = 0.2f;
  263. const float y = 1.3f;
  264. const float z = -3.4f;
  265. AZ::Matrix3x4 matrix;
  266. matrix.SetBasisX(inputBasisX);
  267. EXPECT_THAT(matrix.GetBasisX(), IsClose(inputBasisX));
  268. matrix.SetBasisX(x, y, z);
  269. EXPECT_THAT(matrix.GetBasisX(), IsClose(AZ::Vector3(x, y, z)));
  270. }
  271. TEST(MATH_Matrix3x4, GetSetBasisY)
  272. {
  273. const AZ::Vector3 inputBasisY(-0.7f, 0.9f, 2.7f);
  274. const float x = -0.5f;
  275. const float y = -0.3f;
  276. const float z = -0.6f;
  277. AZ::Matrix3x4 matrix;
  278. matrix.SetBasisY(inputBasisY);
  279. EXPECT_THAT(matrix.GetBasisY(), IsClose(inputBasisY));
  280. matrix.SetBasisY(x, y, z);
  281. EXPECT_THAT(matrix.GetBasisY(), IsClose(AZ::Vector3(x, y, z)));
  282. }
  283. TEST(MATH_Matrix3x4, GetSetBasisZ)
  284. {
  285. const AZ::Vector3 inputBasisZ(2.8f, 0.9f, -2.9f);
  286. const float x = 0.1f;
  287. const float y = 0.6f;
  288. const float z = 0.9f;
  289. AZ::Matrix3x4 matrix;
  290. matrix.SetBasisZ(inputBasisZ);
  291. EXPECT_THAT(matrix.GetBasisZ(), IsClose(inputBasisZ));
  292. matrix.SetBasisZ(x, y, z);
  293. EXPECT_THAT(matrix.GetBasisZ(), IsClose(AZ::Vector3(x, y, z)));
  294. }
  295. TEST(MATH_Matrix3x4, GetSetTranslation)
  296. {
  297. const AZ::Vector3 inputTranslation(-1.2f, -0.2f, 1.9f);
  298. const float x = -2.7f;
  299. const float y = -0.7f;
  300. const float z = -1.2f;
  301. AZ::Matrix3x4 matrix;
  302. matrix.SetTranslation(inputTranslation);
  303. EXPECT_THAT(matrix.GetTranslation(), IsClose(inputTranslation));
  304. matrix.SetTranslation(x, y, z);
  305. EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3(x, y, z)));
  306. }
  307. using Matrix3x4CreateFromQuaternionFixture = ::testing::TestWithParam<AZ::Quaternion>;
  308. TEST_P(Matrix3x4CreateFromQuaternionFixture, CreateFromQuaternion)
  309. {
  310. const AZ::Quaternion quaternion = GetParam();
  311. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternion(quaternion);
  312. EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  313. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  314. EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector)));
  315. }
  316. TEST_P(Matrix3x4CreateFromQuaternionFixture, CreateFromQuaternionAndTranslation)
  317. {
  318. const AZ::Quaternion quaternion = GetParam();
  319. const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
  320. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(quaternion, translation);
  321. EXPECT_THAT(matrix.GetTranslation(), IsClose(translation));
  322. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  323. EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector) + translation));
  324. }
  325. TEST_P(Matrix3x4CreateFromQuaternionFixture, SetRotationPartFromQuaternion)
  326. {
  327. const AZ::Quaternion quaternion = GetParam();
  328. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
  329. matrix.SetRotationPartFromQuaternion(quaternion);
  330. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  331. EXPECT_THAT(matrix * vector, IsClose(quaternion.TransformVector(vector)));
  332. }
  333. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateFromQuaternionFixture, ::testing::ValuesIn(MathTestData::UnitQuaternions));
  334. using Matrix3x4CreateFromMatrix3x3Fixture = ::testing::TestWithParam<AZ::Matrix3x3>;
  335. TEST_P(Matrix3x4CreateFromMatrix3x3Fixture, CreateFromMatrix3x3)
  336. {
  337. const AZ::Matrix3x3 matrix3x3 = GetParam();
  338. const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromMatrix3x3(matrix3x3);
  339. EXPECT_THAT(matrix3x4.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  340. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  341. EXPECT_THAT(matrix3x4.TransformVector(vector), IsClose(matrix3x3 * vector));
  342. }
  343. TEST_P(Matrix3x4CreateFromMatrix3x3Fixture, CreateFromMatrix3x3AndTranslation)
  344. {
  345. const AZ::Matrix3x3 matrix3x3 = GetParam();
  346. const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f);
  347. const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(matrix3x3, translation);
  348. EXPECT_THAT(matrix3x4.GetTranslation(), IsClose(translation));
  349. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  350. EXPECT_THAT(matrix3x4 * vector, IsClose(matrix3x3 * vector + translation));
  351. }
  352. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateFromMatrix3x3Fixture, ::testing::ValuesIn(MathTestData::Matrix3x3s));
  353. using Matrix3x4CreateFromMatrix4x4Fixture = ::testing::TestWithParam<AZ::Matrix4x4>;
  354. TEST_P(Matrix3x4CreateFromMatrix4x4Fixture, UnsafeCreateFromMatrix4x4)
  355. {
  356. const AZ::Matrix4x4 matrix4x4 = GetParam();
  357. const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::UnsafeCreateFromMatrix4x4(matrix4x4);
  358. EXPECT_THAT(matrix3x4.GetTranslation(), IsClose(matrix4x4.GetTranslation()));
  359. const AZ::Vector3 vector(2.3f, -0.6f, 1.8f);
  360. EXPECT_THAT(matrix3x4.TransformVector(vector), IsClose((matrix4x4 * AZ::Vector4(vector, 0.0f)).GetAsVector3()));
  361. const AZ::Vector3 point(12.3f, -5.6f, 7.3f);
  362. EXPECT_THAT(matrix3x4.TransformPoint(point), IsClose((matrix4x4 * AZ::Vector4(point, 1.0f)).GetAsVector3()));
  363. }
  364. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateFromMatrix4x4Fixture, ::testing::ValuesIn(MathTestData::Matrix4x4s));
  365. TEST(MATH_Matrix3x4, TransformPoint)
  366. {
  367. const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  368. AZ::Matrix3x3::CreateRotationY(AZ::DegToRad(90.0f)), AZ::Vector3(5.0f, 0.0f, 0.0f));
  369. const AZ::Vector3 result = matrix3x4.TransformPoint(AZ::Vector3(1.0f, 0.0f, 0.0f));
  370. const AZ::Vector3 expected = AZ::Vector3(5.0f, 0.0f, -1.0f);
  371. EXPECT_THAT(result, IsClose(expected));
  372. }
  373. TEST(MATH_Matrix3x4, CreateScale)
  374. {
  375. const AZ::Vector3 scale(1.7f, 0.3f, 2.4f);
  376. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateScale(scale);
  377. const AZ::Vector3 vector(0.2f, -1.6f, 0.4f);
  378. EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  379. const AZ::Vector3 transformedVector = matrix * vector;
  380. const AZ::Vector3 expected(0.34f, -0.48f, 0.96f);
  381. EXPECT_THAT(transformedVector, IsClose(expected));
  382. }
  383. TEST(MATH_Matrix3x4, CreateDiagonal)
  384. {
  385. const AZ::Vector3 diagonal(0.6f, -1.4f, -0.7f);
  386. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateDiagonal(diagonal);
  387. const AZ::Vector3 vector(-0.3f, -0.6f, 0.2f);
  388. EXPECT_THAT(matrix.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  389. const AZ::Vector3 transformedVector = matrix * vector;
  390. const AZ::Vector3 expected(-0.18f, 0.84f, -0.14f);
  391. EXPECT_THAT(transformedVector, IsClose(expected));
  392. }
  393. TEST(MATH_Matrix3x4, GetSetElement)
  394. {
  395. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateIdentity();
  396. EXPECT_NEAR(matrix(1, 1), 1.0f, 1e-3f);
  397. matrix.SetElement(1, 1, -2.3f);
  398. EXPECT_NEAR(matrix(1, 1), -2.3f, 1e-3f);
  399. }
  400. using Matrix3x4CreateLookAtFixture = ::testing::TestWithParam<MathTestData::AxisPair>;
  401. TEST_P(Matrix3x4CreateLookAtFixture, CreateLookAt)
  402. {
  403. const AZ::Matrix3x4::Axis axis = GetParam().first;
  404. const AZ::Vector3 axisDirection = GetParam().second;
  405. const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
  406. const AZ::Vector3 to(1.3f, 0.5f, 3.2f);
  407. const AZ::Vector3 expectedForward = to - from;
  408. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateLookAt(from, to, axis);
  409. EXPECT_TRUE(matrix.IsOrthogonal());
  410. EXPECT_THAT(matrix.GetColumn(0).Cross(matrix.GetColumn(1)), IsClose(matrix.GetColumn(2)));
  411. EXPECT_THAT(matrix.GetTranslation(), IsClose(from));
  412. // the column of the matrix corresponding to the axis direction should be parallel to the expected forward direction
  413. const AZ::Vector3 forward = matrix.Multiply3x3(axisDirection);
  414. EXPECT_THAT(forward, IsClose(expectedForward.GetNormalized()));
  415. }
  416. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4CreateLookAtFixture, ::testing::ValuesIn(MathTestData::Axes));
  417. TEST(MATH_Matrix3x4, CreateLookAtDegenerateCases)
  418. {
  419. const AZ::Vector3 from(2.5f, 0.2f, 3.6f);
  420. // to and from are the same, should generate an error
  421. AZ_TEST_START_TRACE_SUPPRESSION;
  422. EXPECT_THAT(AZ::Matrix3x4::CreateLookAt(from, from), IsClose(AZ::Matrix3x4::Identity()));
  423. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  424. // to - from is parallel to usual up direction
  425. const AZ::Vector3 to(2.5f, 0.2f, 5.2f);
  426. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateLookAt(from, to);
  427. EXPECT_TRUE(matrix.IsOrthogonal());
  428. EXPECT_THAT(matrix.GetTranslation(), IsClose(from));
  429. // the default is for the Y basis of the look at matrix to be the forward direction
  430. const AZ::Vector3 axisDirection = AZ::Vector3::CreateAxisY();
  431. const AZ::Vector3 forwardDirection = (to - from).GetNormalized();
  432. EXPECT_THAT(matrix.Multiply3x3(axisDirection), IsClose(forwardDirection));
  433. }
  434. TEST(MATH_Matrix3x4, TestMatrixMultiplication)
  435. {
  436. AZ::Matrix3x4 m1;
  437. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  438. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  439. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  440. AZ::Matrix3x4 m2;
  441. m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
  442. m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
  443. m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
  444. AZ::Matrix3x4 m3 = m1 * m2;
  445. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(74.0f, 80.0f, 86.0f, 96.0f)));
  446. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(206.0f, 224.0f, 242.0f, 268.0f)));
  447. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(338.0f, 368.0f, 398.0f, 440.0f)));
  448. AZ::Matrix3x4 m4 = m1;
  449. m4 *= m2;
  450. EXPECT_THAT(m4.GetRow(0), IsClose(AZ::Vector4(74.0f, 80.0f, 86.0f, 96.0f)));
  451. EXPECT_THAT(m4.GetRow(1), IsClose(AZ::Vector4(206.0f, 224.0f, 242.0f, 268.0f)));
  452. EXPECT_THAT(m4.GetRow(2), IsClose(AZ::Vector4(338.0f, 368.0f, 398.0f, 440.0f)));
  453. }
  454. TEST(MATH_Matrix3x4, TestSum)
  455. {
  456. AZ::Matrix3x4 m1;
  457. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  458. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  459. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  460. AZ::Matrix3x4 m2;
  461. m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
  462. m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
  463. m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
  464. AZ::Matrix3x4 m3 = m1 + m2;
  465. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
  466. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
  467. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
  468. m3 = m1;
  469. m3 += m2;
  470. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(8.0f, 10.0f, 12.0f, 14.0f)));
  471. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(16.0f, 18.0f, 20.0f, 22.0f)));
  472. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(24.0f, 26.0f, 28.0f, 30.0f)));
  473. }
  474. TEST(MATH_Matrix3x4, TestDifference)
  475. {
  476. AZ::Matrix3x4 m1;
  477. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  478. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  479. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  480. AZ::Matrix3x4 m2;
  481. m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
  482. m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
  483. m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
  484. AZ::Matrix3x4 m3 = m1 - m2;
  485. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  486. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  487. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  488. m3 = m1;
  489. m3 -= m2;
  490. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  491. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  492. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(-6.0f, -6.0f, -6.0f, -6.0f)));
  493. }
  494. TEST(MATH_Matrix3x4, TestScalarMultiplication)
  495. {
  496. AZ::Matrix3x4 m1;
  497. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  498. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  499. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  500. AZ::Matrix3x4 m2;
  501. m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
  502. m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
  503. m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
  504. AZ::Matrix3x4 m3 = m1 * 2.0f;
  505. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
  506. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
  507. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
  508. m3 = m1;
  509. m3 *= 2.0f;
  510. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
  511. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
  512. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
  513. m3 = 2.0f * m1;
  514. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
  515. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
  516. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
  517. }
  518. TEST(MATH_Matrix3x4, TestScalarDivision)
  519. {
  520. AZ::Matrix3x4 m1;
  521. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  522. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  523. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  524. AZ::Matrix3x4 m2;
  525. m2.SetRow(0, 7.0f, 8.0f, 9.0f, 10.0f);
  526. m2.SetRow(1, 11.0f, 12.0f, 13.0f, 14.0f);
  527. m2.SetRow(2, 15.0f, 16.0f, 17.0f, 18.0f);
  528. AZ::Matrix3x4 m3 = m1 / 0.5f;
  529. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
  530. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
  531. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
  532. m3 = m1;
  533. m3 /= 0.5f;
  534. EXPECT_THAT(m3.GetRow(0), IsClose(AZ::Vector4(2.0f, 4.0f, 6.0f, 8.0f)));
  535. EXPECT_THAT(m3.GetRow(1), IsClose(AZ::Vector4(10.0f, 12.0f, 14.0f, 16.0f)));
  536. EXPECT_THAT(m3.GetRow(2), IsClose(AZ::Vector4(18.0f, 20.0f, 22.0f, 24.0f)));
  537. }
  538. TEST(MATH_Matrix3x4, TestNegation)
  539. {
  540. AZ::Matrix3x4 m1;
  541. m1.SetRow(0, 1.0f, 2.0f, 3.0f, 4.0f);
  542. m1.SetRow(1, 5.0f, 6.0f, 7.0f, 8.0f);
  543. m1.SetRow(2, 9.0f, 10.0f, 11.0f, 12.0f);
  544. EXPECT_THAT(-(-m1), IsClose(m1));
  545. EXPECT_THAT(-AZ::Matrix3x4::CreateZero(), IsClose(AZ::Matrix3x4::CreateZero()));
  546. AZ::Matrix3x4 m2 = -m1;
  547. EXPECT_THAT(m2.GetRow(0), IsClose(AZ::Vector4(-1.0f, -2.0f, -3.0f, -4.0f)));
  548. EXPECT_THAT(m2.GetRow(1), IsClose(AZ::Vector4(-5.0f, -6.0f, -7.0f, -8.0f)));
  549. EXPECT_THAT(m2.GetRow(2), IsClose(AZ::Vector4(-9.0f, -10.0f, -11.0f, -12.0f)));
  550. AZ::Matrix3x4 m3 = m1 + (-m1);
  551. EXPECT_THAT(m3, IsClose(AZ::Matrix3x4::CreateZero()));
  552. }
  553. TEST(MATH_Matrix3x4, MultiplyByVector3)
  554. {
  555. const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
  556. const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
  557. const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
  558. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
  559. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  560. const AZ::Vector3 expected(2.8248f, -1.8436f, 3.788f);
  561. EXPECT_THAT(matrix * vector, IsClose(expected));
  562. }
  563. TEST(MATH_Matrix3x4, Multiply3x3)
  564. {
  565. const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
  566. const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
  567. const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
  568. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
  569. const AZ::Vector3 vector(0.2f, 0.1f, -0.3f);
  570. const AZ::Vector3 expected(0.5248f, -0.2436f, 0.088f);
  571. EXPECT_THAT(matrix.Multiply3x3(vector), IsClose(expected));
  572. matrix.SetTranslation(AZ::Vector3(0.9f, 2.6f, -2.2f));
  573. EXPECT_THAT(matrix.Multiply3x3(vector), IsClose(expected));
  574. }
  575. TEST(MATH_Matrix3x4, MultiplyByVector4)
  576. {
  577. const AZ::Vector4 row0(-0.4f, 0.5f, 0.4f, -0.5f);
  578. const AZ::Vector4 row1(0.9f, -1.0f, 0.6f, 0.4f);
  579. const AZ::Vector4 row2(0.4f, 0.3f, 0.3f, -0.8f);
  580. const AZ::Vector4 vector(0.4f, -1.0f, -0.2f, 0.3f);
  581. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
  582. const AZ::Vector4 product = matrix * vector;
  583. const AZ::Vector4 expected(-0.89f, 1.36f, -0.44f, 0.3f);
  584. EXPECT_THAT(product, IsClose(expected));
  585. }
  586. using Matrix3x4TransposeFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  587. TEST_P(Matrix3x4TransposeFixture, GetTranspose)
  588. {
  589. const AZ::Matrix3x4 matrix = GetParam();
  590. const AZ::Matrix3x4 transpose = matrix.GetTranspose();
  591. EXPECT_THAT(transpose.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  592. EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
  593. EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
  594. EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
  595. }
  596. TEST_P(Matrix3x4TransposeFixture, Transpose)
  597. {
  598. const AZ::Matrix3x4 matrix = GetParam();
  599. AZ::Matrix3x4 transpose = matrix;
  600. transpose.Transpose();
  601. EXPECT_THAT(transpose.GetTranslation(), IsClose(AZ::Vector3::CreateZero()));
  602. EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
  603. EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
  604. EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
  605. }
  606. TEST_P(Matrix3x4TransposeFixture, GetTranspose3x3)
  607. {
  608. const AZ::Matrix3x4 matrix = GetParam();
  609. const AZ::Matrix3x4 transpose = matrix.GetTranspose3x3();
  610. EXPECT_THAT(transpose.GetTranslation(), IsClose(matrix.GetTranslation()));
  611. EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
  612. EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
  613. EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
  614. }
  615. TEST_P(Matrix3x4TransposeFixture, Transpose3x3)
  616. {
  617. const AZ::Matrix3x4 matrix = GetParam();
  618. AZ::Matrix3x4 transpose = matrix;
  619. transpose.Transpose3x3();
  620. EXPECT_THAT(transpose.GetTranslation(), IsClose(matrix.GetTranslation()));
  621. EXPECT_THAT(transpose.GetColumn(0), IsClose(matrix.GetRowAsVector3(0)));
  622. EXPECT_THAT(transpose.GetColumn(1), IsClose(matrix.GetRowAsVector3(1)));
  623. EXPECT_THAT(transpose.GetColumn(2), IsClose(matrix.GetRowAsVector3(2)));
  624. }
  625. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4TransposeFixture, ::testing::ValuesIn(MathTestData::NonOrthogonalMatrix3x4s));
  626. using Matrix3x4InvertFullFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  627. TEST_P(Matrix3x4InvertFullFixture, GetInverseFull)
  628. {
  629. const AZ::Matrix3x4 matrix = GetParam();
  630. const AZ::Matrix3x4 inverse = matrix.GetInverseFull();
  631. const AZ::Vector3 vector(0.9f, 3.2f, -1.4f);
  632. EXPECT_THAT((inverse * matrix) * vector, IsClose(vector));
  633. EXPECT_THAT((matrix * inverse) * vector, IsClose(vector));
  634. EXPECT_THAT((inverse * matrix), IsClose(AZ::Matrix3x4::Identity()));
  635. }
  636. TEST_P(Matrix3x4InvertFullFixture, InvertFull)
  637. {
  638. const AZ::Matrix3x4 matrix = GetParam();
  639. AZ::Matrix3x4 inverse = matrix;
  640. inverse.InvertFull();
  641. const AZ::Vector3 vector(2.8f, -1.3f, 2.6f);
  642. EXPECT_THAT((inverse * matrix) * vector, IsClose(vector));
  643. EXPECT_THAT((matrix * inverse) * vector, IsClose(vector));
  644. EXPECT_THAT((inverse * matrix), IsClose(AZ::Matrix3x4::Identity()));
  645. }
  646. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4InvertFullFixture, ::testing::ValuesIn(MathTestData::NonOrthogonalMatrix3x4s));
  647. TEST(MATH_Matrix3x4, GetInverseFullSingularMatrix)
  648. {
  649. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromValue(1.4f);
  650. const AZ::Matrix3x4 inverse = matrix.GetInverseFull();
  651. EXPECT_THAT(inverse.GetBasisX(), IsClose(AZ::Vector3::CreateAxisX()));
  652. EXPECT_THAT(inverse.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
  653. EXPECT_THAT(inverse.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
  654. EXPECT_THAT(inverse.GetTranslation(), IsClose(AZ::Vector3(-1.4f)));
  655. }
  656. TEST(MATH_Matrix3x4, GetInverseFullSmallDeterminant)
  657. {
  658. // This is a regression test for a specific case that was broken by some changes to Matrix3x4::GetInverseFull()
  659. const AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(
  660. AZ::Vector4(-0.0162572227f, 6.21248771e-17f, 1.42125156e-09f, 0.0f),
  661. AZ::Vector4(1.42125156e-09f, 7.10625780e-10f, 0.0162572227f, 0.0f),
  662. AZ::Vector4(0.0f, 0.0162572227f, -7.10625780e-10f, 0.0f)
  663. );
  664. const AZ::Matrix3x4 inverse = matrix.GetInverseFull();
  665. EXPECT_THAT(inverse.GetRow(0), IsClose(AZ::Vector4(-61.5111237f, 5.37747292e-06f, 0.0f, 0.0f)));
  666. EXPECT_THAT(inverse.GetRow(1), IsClose(AZ::Vector4(2.35056820e-13f, 2.68873646e-06f, 61.5111237f, 0.0f)));
  667. EXPECT_THAT(inverse.GetRow(2), IsClose(AZ::Vector4(5.37747292e-06f, 61.5111237f, -2.68873646e-06f, 0.0f)));
  668. }
  669. using Matrix3x4InvertFastFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  670. TEST_P(Matrix3x4InvertFastFixture, GetInverseFast)
  671. {
  672. const AZ::Matrix3x4 matrix = GetParam();
  673. const AZ::Matrix3x4 inverseFast = matrix.GetInverseFast();
  674. const AZ::Matrix3x4 inverseFull = matrix.GetInverseFull();
  675. const AZ::Vector3 vector(0.9f, 3.2f, -1.4f);
  676. EXPECT_THAT((inverseFast * matrix) * vector, IsClose(vector));
  677. EXPECT_THAT((matrix * inverseFast) * vector, IsClose(vector));
  678. EXPECT_THAT((inverseFast * matrix), IsClose(AZ::Matrix3x4::Identity()));
  679. EXPECT_THAT(inverseFast, IsClose(inverseFull));
  680. }
  681. TEST_P(Matrix3x4InvertFastFixture, InvertFast)
  682. {
  683. const AZ::Matrix3x4 matrix = GetParam();
  684. AZ::Matrix3x4 inverseFast = matrix;
  685. inverseFast.InvertFast();
  686. AZ::Matrix3x4 inverseFull = matrix;
  687. inverseFull.InvertFull();
  688. const AZ::Vector3 vector(2.8f, -1.3f, 2.6f);
  689. EXPECT_THAT((inverseFast * matrix) * vector, IsClose(vector));
  690. EXPECT_THAT((matrix * inverseFast) * vector, IsClose(vector));
  691. EXPECT_THAT((inverseFast * matrix), IsClose(AZ::Matrix3x4::Identity()));
  692. EXPECT_THAT(inverseFast, IsClose(inverseFull));
  693. }
  694. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4InvertFastFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
  695. using Matrix3x4ScaleFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  696. TEST_P(Matrix3x4ScaleFixture, Scale)
  697. {
  698. const AZ::Matrix3x4 orthogonalMatrix = GetParam();
  699. EXPECT_THAT(orthogonalMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
  700. AZ::Matrix3x4 unscaledMatrix = orthogonalMatrix;
  701. unscaledMatrix.ExtractScale();
  702. EXPECT_THAT(unscaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
  703. const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
  704. AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
  705. scaledMatrix.MultiplyByScale(scale);
  706. EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(scale));
  707. scaledMatrix.ExtractScale();
  708. EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
  709. }
  710. TEST_P(Matrix3x4ScaleFixture, ScaleSq)
  711. {
  712. const AZ::Matrix3x4 orthogonalMatrix = GetParam();
  713. EXPECT_THAT(orthogonalMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
  714. AZ::Matrix3x4 unscaledMatrix = orthogonalMatrix;
  715. unscaledMatrix.ExtractScale();
  716. EXPECT_THAT(unscaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
  717. const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
  718. AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
  719. scaledMatrix.MultiplyByScale(scale);
  720. EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scale * scale));
  721. EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scaledMatrix.RetrieveScale() * scaledMatrix.RetrieveScale()));
  722. scaledMatrix.ExtractScale();
  723. EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
  724. }
  725. TEST_P(Matrix3x4ScaleFixture, GetReciprocalScaled)
  726. {
  727. const AZ::Matrix3x4 orthogonalMatrix = GetParam();
  728. EXPECT_THAT(orthogonalMatrix.GetReciprocalScaled(), IsClose(orthogonalMatrix));
  729. const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
  730. AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
  731. scaledMatrix.MultiplyByScale(scale);
  732. AZ::Matrix3x4 reciprocalScaledMatrix = orthogonalMatrix;
  733. reciprocalScaledMatrix.MultiplyByScale(scale.GetReciprocal());
  734. EXPECT_THAT(scaledMatrix.GetReciprocalScaled(), IsClose(reciprocalScaledMatrix));
  735. }
  736. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4ScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
  737. TEST(MATH_Matrix3x4, IsOrthogonal)
  738. {
  739. EXPECT_TRUE(AZ::Matrix3x4::CreateIdentity().IsOrthogonal());
  740. EXPECT_TRUE(AZ::Matrix3x4::CreateRotationZ(0.3f).IsOrthogonal());
  741. EXPECT_FALSE(AZ::Matrix3x4::CreateFromValue(1.0f).IsOrthogonal());
  742. EXPECT_FALSE(AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(0.8f, 0.3f, 1.2f)).IsOrthogonal());
  743. EXPECT_TRUE(AZ::Matrix3x4::CreateFromQuaternion(AZ::Quaternion(-0.52f, -0.08f, 0.56f, 0.64f)).IsOrthogonal());
  744. AZ::Matrix3x4 matrix3x4;
  745. matrix3x4.SetFromEulerRadians(AZ::Vector3(0.2f, 0.4f, 0.1f));
  746. EXPECT_TRUE(matrix3x4.IsOrthogonal());
  747. // want to test each possible way the matrix could fail to be orthogonal, which we can do by testing for one
  748. // axis, then using a rotation which cycles the axes
  749. const AZ::Matrix3x4 axisCycle = AZ::Matrix3x4::CreateFromQuaternion(AZ::Quaternion(0.5f, 0.5f, 0.5f, 0.5f));
  750. // a matrix which is normalized in 2 axes, but not the third
  751. AZ::Matrix3x4 nonOrthogonalMatrix1 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.0f, 1.0f, 2.0f));
  752. // a matrix which is normalized in all 3 axes, and 2 pairs of axes are perpendicular, but not the third pair
  753. AZ::Matrix3x4 nonOrthogonalMatrix2 = AZ::Matrix3x4::Identity();
  754. nonOrthogonalMatrix2.SetRow(2, AZ::Vector3(0.0f, 0.8f, 0.6f), 0.0f);
  755. for (int i = 0; i < 3; i++)
  756. {
  757. EXPECT_FALSE(nonOrthogonalMatrix1.IsOrthogonal());
  758. EXPECT_FALSE(nonOrthogonalMatrix2.IsOrthogonal());
  759. nonOrthogonalMatrix1 = axisCycle * nonOrthogonalMatrix1;
  760. nonOrthogonalMatrix2 = axisCycle * nonOrthogonalMatrix2;
  761. }
  762. }
  763. TEST(MATH_Matrix3x4, GetOrthogonalized)
  764. {
  765. // a matrix which is already orthogonal should be unchanged
  766. const AZ::Matrix3x4 orthogonalMatrix = AZ::Matrix3x4::CreateRotationZ(0.7f);
  767. EXPECT_TRUE(orthogonalMatrix.IsOrthogonal());
  768. EXPECT_THAT(orthogonalMatrix.GetOrthogonalized(), IsClose(orthogonalMatrix));
  769. // a matrix which isn't already orthogonal should be made orthogonal
  770. const AZ::Matrix3x4 nonOrthogonalMatrix = AZ::Matrix3x4::CreateScale(AZ::Vector3(3.0f, 4.0f, 5.0f));
  771. EXPECT_FALSE(nonOrthogonalMatrix.IsOrthogonal());
  772. EXPECT_TRUE(nonOrthogonalMatrix.GetOrthogonalized().IsOrthogonal());
  773. }
  774. TEST(MATH_Matrix3x4, Orthogonalize)
  775. {
  776. // a matrix which is already orthogonal should be unchanged
  777. AZ::Matrix3x4 orthogonalMatrix = AZ::Matrix3x4::CreateRotationY(-0.2f);
  778. EXPECT_TRUE(orthogonalMatrix.IsOrthogonal());
  779. orthogonalMatrix.Orthogonalize();
  780. EXPECT_THAT(orthogonalMatrix, IsClose(AZ::Matrix3x4::CreateRotationY(-0.2f)));
  781. // a matrix which isn't already orthogonal should be made orthogonal
  782. AZ::Matrix3x4 nonOrthogonalMatrix = AZ::Matrix3x4::CreateScale(AZ::Vector3(0.7f, 0.7f, 0.2f));
  783. EXPECT_FALSE(nonOrthogonalMatrix.IsOrthogonal());
  784. nonOrthogonalMatrix.Orthogonalize();
  785. EXPECT_TRUE(nonOrthogonalMatrix.IsOrthogonal());
  786. }
  787. TEST(MATH_Matrix3x4, IsClose)
  788. {
  789. const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
  790. AZ::Quaternion(0.12f, 0.24f, -0.72f, 0.64f),
  791. AZ::Vector3(0.3f, 0.2f, -0.7f)
  792. );
  793. AZ::Matrix3x4 matrix2 = matrix1;
  794. EXPECT_THAT(matrix2, IsCloseTolerance(matrix1, 1e-6f));
  795. matrix2.SetElement(0, 2, matrix2(0, 2) + 1e-2f);
  796. matrix2.SetElement(2, 3, matrix2(2, 3) + 1e-4f);
  797. matrix2.SetElement(1, 1, matrix2(1, 1) - 1e-6f);
  798. EXPECT_THAT(matrix2, IsCloseTolerance(matrix1, 1e-1f));
  799. EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-3f));
  800. EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-5f));
  801. EXPECT_FALSE(matrix2.IsClose(matrix1, 1e-7f));
  802. }
  803. TEST(MATH_Matrix3x4, Equality)
  804. {
  805. const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
  806. AZ::Quaternion(0.12f, 0.24f, -0.72f, 0.64f),
  807. AZ::Vector3(0.3f, 0.2f, -0.7f)
  808. );
  809. AZ::Matrix3x4 matrix2 = matrix1;
  810. EXPECT_TRUE(matrix2 == matrix1);
  811. EXPECT_FALSE(matrix2 != matrix1);
  812. for (int row = 0; row < 3; row++)
  813. {
  814. for (int col = 0; col < 3; col++)
  815. {
  816. matrix2 = matrix1;
  817. matrix2.SetElement(row, col, matrix2(row, col) + 1e-4f);
  818. EXPECT_FALSE(matrix2 == matrix1);
  819. EXPECT_TRUE(matrix2 != matrix1);
  820. }
  821. }
  822. }
  823. using Matrix3x4SetFromEulerDegreesFixture = ::testing::TestWithParam<AZ::Vector3>;
  824. TEST_P(Matrix3x4SetFromEulerDegreesFixture, SetFromEulerDegrees)
  825. {
  826. const AZ::Vector3 eulerDegrees = GetParam();
  827. AZ::Matrix3x4 matrix;
  828. matrix.SetFromEulerDegrees(eulerDegrees);
  829. const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(eulerDegrees);
  830. const AZ::Matrix3x4 rotX = AZ::Matrix3x4::CreateRotationX(eulerRadians.GetX());
  831. const AZ::Matrix3x4 rotY = AZ::Matrix3x4::CreateRotationY(eulerRadians.GetY());
  832. const AZ::Matrix3x4 rotZ = AZ::Matrix3x4::CreateRotationZ(eulerRadians.GetZ());
  833. EXPECT_THAT(matrix, IsClose(rotX * rotY * rotZ));
  834. }
  835. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4SetFromEulerDegreesFixture, ::testing::ValuesIn(MathTestData::EulerAnglesDegrees));
  836. using Matrix3x4SetFromEulerRadiansFixture = ::testing::TestWithParam<AZ::Vector3>;
  837. TEST_P(Matrix3x4SetFromEulerRadiansFixture, SetFromEulerRadians)
  838. {
  839. const AZ::Vector3 eulerRadians = GetParam();
  840. AZ::Matrix3x4 matrix;
  841. matrix.SetFromEulerRadians(eulerRadians);
  842. const AZ::Matrix3x4 rotX = AZ::Matrix3x4::CreateRotationX(eulerRadians.GetX());
  843. const AZ::Matrix3x4 rotY = AZ::Matrix3x4::CreateRotationY(eulerRadians.GetY());
  844. const AZ::Matrix3x4 rotZ = AZ::Matrix3x4::CreateRotationZ(eulerRadians.GetZ());
  845. EXPECT_THAT(matrix, IsClose(rotX * rotY * rotZ));
  846. }
  847. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4SetFromEulerRadiansFixture, ::testing::ValuesIn(MathTestData::EulerAnglesRadians));
  848. using Matrix3x4GetEulerFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  849. TEST_P(Matrix3x4GetEulerFixture, GetEuler)
  850. {
  851. // there isn't a one to one mapping between matrices and Euler angles, so testing for a particular set of Euler
  852. // angles to be returned would be fragile, but getting the Euler angles and creating a new matrix from them
  853. // should return the original matrix
  854. AZ::Matrix3x4 matrix = GetParam();
  855. matrix.SetTranslation(AZ::Vector3::CreateZero());
  856. const AZ::Vector3 eulerDegrees = matrix.GetEulerDegrees();
  857. AZ::Matrix3x4 eulerMatrix;
  858. eulerMatrix.SetFromEulerDegrees(eulerDegrees);
  859. EXPECT_THAT(eulerMatrix, IsClose(matrix));
  860. const AZ::Vector3 eulerRadians = matrix.GetEulerRadians();
  861. eulerMatrix = AZ::Matrix3x4::Identity();
  862. eulerMatrix.SetFromEulerRadians(eulerRadians);
  863. EXPECT_THAT(eulerMatrix, IsClose(matrix));
  864. }
  865. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4GetEulerFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
  866. using Matrix3x4GetDeterminantFixture = ::testing::TestWithParam<AZ::Matrix3x4>;
  867. TEST_P(Matrix3x4GetDeterminantFixture, GetDeterminantOfOrthogonalMatrices)
  868. {
  869. const AZ::Matrix3x4 matrix = GetParam();
  870. EXPECT_NEAR(matrix.GetDeterminant3x3(), 1.0f, 1e-3f);
  871. }
  872. INSTANTIATE_TEST_SUITE_P(MATH_Matrix3x4, Matrix3x4GetDeterminantFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
  873. TEST(MATH_Matrix3x4, GetDeterminantOfArbitraryMatrices)
  874. {
  875. const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(0.8f);
  876. const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(0.2f, 1.5f, 0.6f));
  877. const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateRotationY(0.2f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(2.0f, 0.5f, 1.2f));
  878. const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateFromRows(matrix3.GetRow(0), matrix3.GetRow(2), matrix3.GetRow(1));
  879. const float expected1 = 0.0f;
  880. const float expected2 = 0.18f;
  881. const float expected3 = 1.2f;
  882. const float expected4 = -expected3;
  883. EXPECT_NEAR(matrix1.GetDeterminant3x3(), expected1, 1e-3f);
  884. EXPECT_NEAR(matrix2.GetDeterminant3x3(), expected2, 1e-3f);
  885. EXPECT_NEAR(matrix3.GetDeterminant3x3(), expected3, 1e-3f);
  886. EXPECT_NEAR(matrix4.GetDeterminant3x3(), expected4, 1e-3f);
  887. EXPECT_NEAR((matrix2 * matrix3).GetDeterminant3x3(), expected2 * expected3, 1e-3f);
  888. EXPECT_NEAR(matrix2.GetTranspose3x3().GetDeterminant3x3(), expected2, 1e-3f);
  889. }
  890. // use of infinity with fast math is simply not supported
  891. #if !defined(O3DE_USING_FAST_MATH)
  892. // Use of INFINITY in newer Windows SDKs trigger a math overflow warning because it redefines INFINITY
  893. // as (huge number * huge number) instead of the previous definition of just (huge number). The multiplication
  894. // operation triggers the overflow warning.
  895. // We still want that warning globally in the code but we don't want it in this test, specifically, which uses it
  896. // to validate that we can detect infinite matrices that come about from runtime operations.
  897. AZ_PUSH_DISABLE_WARNING(4756, "-Wunknown-warning-option") //warning C4756: overflow in constant arithmetic
  898. TEST(MATH_Matrix3x4, IsFinite)
  899. {
  900. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
  901. AZ::Quaternion(-0.42f, -0.46f, 0.66f, 0.42f),
  902. AZ::Vector3(0.8f, -2.3f, 2.2f)
  903. );
  904. EXPECT_TRUE(matrix.IsFinite());
  905. const float f32NaN = NAN;
  906. EXPECT_TRUE(AZStd::isnan(f32NaN));
  907. EXPECT_FALSE(AZ::IsFiniteFloat(f32NaN));
  908. const float f32Inf = INFINITY;
  909. EXPECT_TRUE(AZStd::isinf(f32Inf));
  910. EXPECT_FALSE(AZ::IsFiniteFloat(f32Inf));
  911. for (int row = 0; row < 3; row++)
  912. {
  913. for (int col = 0; col < 3; col++)
  914. {
  915. const float value = matrix.GetElement(row, col);
  916. matrix.SetElement(row, col, f32NaN);
  917. EXPECT_FALSE(matrix.IsFinite());
  918. matrix.SetElement(row, col, f32Inf);
  919. EXPECT_FALSE(matrix.IsFinite());
  920. matrix.SetElement(row, col, value);
  921. EXPECT_TRUE(matrix.IsFinite());
  922. }
  923. }
  924. }
  925. AZ_POP_DISABLE_WARNING
  926. #endif
  927. } // namespace UnitTest