3
0

TransformUnitTests.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  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 <gtest/gtest.h>
  9. #include <AzCore/Math/Matrix3x3.h>
  10. #include <AzCore/UnitTest/TestTypes.h>
  11. #include <cmath>
  12. #include <Tests/Printers.h>
  13. #include <Tests/Matchers.h>
  14. #include <AzCore/Math/Vector3.h>
  15. #include <AzCore/Math/MathUtils.h>
  16. #include <AzCore/Math/Quaternion.h>
  17. #include <MCore/Source/AzCoreConversions.h>
  18. #include <EMotionFX/Source/PlayBackInfo.h>
  19. #include <EMotionFX/Source/Transform.h>
  20. #if defined(EMFX_SCALE_DISABLED)
  21. #define EMFX_SCALE false
  22. #else
  23. #define EMFX_SCALE true
  24. #endif
  25. namespace EMotionFX
  26. {
  27. static const float sqrt2 = std::sqrt(2.0f);
  28. static const float sqrt2over2 = sqrt2 / 2.0f;
  29. AZ::Matrix3x3 TensorProduct(const AZ::Vector3& u, const AZ::Vector3& v)
  30. {
  31. AZ::Matrix3x3 mat{};
  32. mat.SetElement(0, 0, u.GetX() * v.GetX());
  33. mat.SetElement(0, 1, u.GetX() * v.GetY());
  34. mat.SetElement(0, 2, u.GetX() * v.GetZ());
  35. mat.SetElement(1, 0, u.GetY() * v.GetX());
  36. mat.SetElement(1, 1, u.GetY() * v.GetY());
  37. mat.SetElement(1, 2, u.GetY() * v.GetZ());
  38. mat.SetElement(2, 0, u.GetZ() * v.GetX());
  39. mat.SetElement(2, 1, u.GetZ() * v.GetY());
  40. mat.SetElement(2, 2, u.GetZ() * v.GetZ());
  41. return mat;
  42. }
  43. TEST(TransformFixture, CreateIdentity)
  44. {
  45. const Transform transform = Transform::CreateIdentity();
  46. EXPECT_TRUE(transform.m_position.IsZero());
  47. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateIdentity());
  48. EMFX_SCALECODE
  49. (
  50. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateOne());
  51. )
  52. }
  53. TEST(TransformFixture, CreateIdentityWithZeroScale)
  54. {
  55. const Transform transform = Transform::CreateIdentityWithZeroScale();
  56. EXPECT_TRUE(transform.m_position.IsZero());
  57. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateIdentity());
  58. EMFX_SCALECODE
  59. (
  60. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateZero());
  61. )
  62. }
  63. TEST(TransformFixture, CreateZero)
  64. {
  65. const Transform transform = Transform::CreateZero();
  66. EXPECT_TRUE(transform.m_position.IsZero());
  67. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateZero());
  68. EMFX_SCALECODE
  69. (
  70. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateZero());
  71. )
  72. }
  73. TEST(TransformFixture, ConstructFromVec3Quat)
  74. {
  75. const Transform transform(AZ::Vector3(6.0f, 7.0f, 8.0f), AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi));
  76. EXPECT_EQ(transform.m_position, AZ::Vector3(6.0f, 7.0f, 8.0f));
  77. EXPECT_THAT(transform.m_rotation, IsClose(AZ::Quaternion(sqrt2over2, 0.0f, 0.0f, sqrt2over2)));
  78. EMFX_SCALECODE
  79. (
  80. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateOne());
  81. )
  82. }
  83. using TransformConstructFromVec3QuatVec3Params = ::testing::tuple<AZ::Vector3, ::testing::tuple<float, float, float>, AZ::Vector3>;
  84. class TransformConstructFromVec3QuatVec3Fixture
  85. : public ::testing::TestWithParam<TransformConstructFromVec3QuatVec3Params>
  86. {
  87. public:
  88. const AZ::Vector3& ExpectedPosition() const
  89. {
  90. return ::testing::get<0>(GetParam());
  91. }
  92. AZ::Quaternion ExpectedRotation() const
  93. {
  94. return AZ::Quaternion::CreateFromEulerRadiansZYX(AZ::Vector3(
  95. ::testing::get<0>(::testing::get<1>(GetParam())),
  96. ::testing::get<1>(::testing::get<1>(GetParam())),
  97. ::testing::get<2>(::testing::get<1>(GetParam()))
  98. ));
  99. }
  100. const AZ::Vector3& ExpectedScale() const
  101. {
  102. return ::testing::get<2>(GetParam());
  103. }
  104. bool HasNonUniformScale() const
  105. {
  106. const AZ::Vector3 scale = ExpectedScale();
  107. return
  108. !AZ::IsClose(scale.GetX(), scale.GetY(), AZ::Constants::Tolerance) ||
  109. !AZ::IsClose(scale.GetX(), scale.GetZ(), AZ::Constants::Tolerance) ||
  110. !AZ::IsClose(scale.GetY(), scale.GetZ(), AZ::Constants::Tolerance);
  111. }
  112. // Returns a transformation matrix where the position is mirrored, the
  113. // rotation axis is mirrored, and the rotation angle is negated
  114. AZ::Matrix4x4 GetMirroredTransform(const AZ::Vector3& axis) const
  115. {
  116. const AZ::Matrix3x3 mirrorMatrix = AZ::Matrix3x3::CreateIdentity() - (2.0f * TensorProduct(axis, axis));
  117. const AZ::Vector3 mirrorPosition = mirrorMatrix * ExpectedPosition();
  118. AZ::Vector3 extractedAxis;
  119. float extractedAngle;
  120. ExpectedRotation().ConvertToAxisAngle(extractedAxis, extractedAngle);
  121. const AZ::Quaternion mirrorRotation = AZ::Quaternion::CreateFromAxisAngle(
  122. mirrorMatrix * AZ::Vector3(extractedAxis.GetX(), extractedAxis.GetY(), extractedAxis.GetZ()),
  123. -extractedAngle
  124. );
  125. return AZ::Matrix4x4::CreateFromQuaternionAndTranslation(mirrorRotation, mirrorPosition)
  126. * AZ::Matrix4x4::CreateScale(ExpectedScale());
  127. }
  128. };
  129. TEST_P(TransformConstructFromVec3QuatVec3Fixture, ConstructFromVec3QuatVec3)
  130. {
  131. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  132. EXPECT_THAT(transform.m_position, IsClose(ExpectedPosition()));
  133. EXPECT_THAT(transform.m_rotation, IsClose(ExpectedRotation()));
  134. EMFX_SCALECODE
  135. (
  136. EXPECT_THAT(transform.m_scale, IsClose(ExpectedScale()));
  137. )
  138. }
  139. TEST_P(TransformConstructFromVec3QuatVec3Fixture, SetFromVec3QuatVec3)
  140. {
  141. Transform transform(AZ::Vector3(5.0f, 6.0f, 7.0f), AZ::Quaternion(0.1f, 0.2f, 0.3f, 0.4f), AZ::Vector3(8.0f, 9.0f, 10.0f));
  142. transform.Set(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  143. EXPECT_THAT(transform.m_position, IsClose(ExpectedPosition()));
  144. EXPECT_THAT(transform.m_rotation, IsClose(ExpectedRotation()));
  145. EMFX_SCALECODE
  146. (
  147. EXPECT_THAT(transform.m_scale, IsClose(ExpectedScale()));
  148. )
  149. }
  150. INSTANTIATE_TEST_CASE_P(Test, TransformConstructFromVec3QuatVec3Fixture,
  151. ::testing::Combine(
  152. ::testing::Values(
  153. AZ::Vector3::CreateZero(),
  154. AZ::Vector3(6.0f, 7.0f, 8.0f)
  155. ),
  156. ::testing::Combine(
  157. ::testing::Values(0.0f, AZ::Constants::QuarterPi, AZ::Constants::HalfPi),
  158. ::testing::Values(0.0f, AZ::Constants::QuarterPi, AZ::Constants::HalfPi),
  159. ::testing::Values(0.0f, AZ::Constants::QuarterPi, AZ::Constants::HalfPi)
  160. ),
  161. ::testing::Values(
  162. AZ::Vector3::CreateOne(),
  163. AZ::Vector3(2.0f, 2.0f, 2.0f),
  164. AZ::Vector3(2.0f, 3.0f, 4.0f)
  165. )
  166. )
  167. );
  168. TEST(TransformFixture, SetFromVec3Quat)
  169. {
  170. Transform transform(AZ::Vector3(5.0f, 6.0f, 7.0f), AZ::Quaternion(0.1f, 0.2f, 0.3f, 0.4f), AZ::Vector3(8.0f, 9.0f, 10.0f));
  171. transform.Set(AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi));
  172. EXPECT_EQ(transform.m_position, AZ::Vector3(1.0f, 2.0f, 3.0f));
  173. EXPECT_THAT(transform.m_rotation, IsClose(AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi)));
  174. EMFX_SCALECODE
  175. (
  176. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateOne());
  177. )
  178. }
  179. TEST(TransformFixture, Identity)
  180. {
  181. Transform transform(AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.1f, 0.2f, 0.3f, 0.4f), AZ::Vector3(4.0f, 5.0f, 6.0f));
  182. transform.Identity();
  183. EXPECT_EQ(transform.m_position, AZ::Vector3::CreateZero());
  184. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateIdentity());
  185. EMFX_SCALECODE
  186. (
  187. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateOne());
  188. )
  189. }
  190. TEST(TransformFixture, Zero)
  191. {
  192. Transform transform(AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.1f, 0.2f, 0.3f, 0.4f), AZ::Vector3(4.0f, 5.0f, 6.0f));
  193. transform.Zero();
  194. EXPECT_EQ(transform.m_position, AZ::Vector3::CreateZero());
  195. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateZero());
  196. EMFX_SCALECODE
  197. (
  198. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateZero());
  199. )
  200. }
  201. TEST(TransformFixture, IdentityWithZeroScale)
  202. {
  203. Transform transform(AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.1f, 0.2f, 0.3f, 0.4f), AZ::Vector3(4.0f, 5.0f, 6.0f));
  204. transform.IdentityWithZeroScale();
  205. EXPECT_EQ(transform.m_position, AZ::Vector3::CreateZero());
  206. EXPECT_EQ(transform.m_rotation, AZ::Quaternion::CreateIdentity());
  207. EMFX_SCALECODE
  208. (
  209. EXPECT_EQ(transform.m_scale, AZ::Vector3::CreateZero());
  210. )
  211. }
  212. using TransformMultiplyParams = ::testing::tuple<Transform, Transform, Transform, Transform>;
  213. using TransformMultiplyFixture = ::testing::TestWithParam<TransformMultiplyParams>;
  214. TEST_P(TransformMultiplyFixture, Multiply)
  215. {
  216. const Transform& inputA = ::testing::get<0>(GetParam());
  217. const Transform& inputB = ::testing::get<1>(GetParam());
  218. const Transform& expected = ::testing::get<2>(GetParam());
  219. Transform multiply(inputA);
  220. multiply.Multiply(inputB);
  221. EXPECT_THAT(multiply, IsClose(expected));
  222. }
  223. TEST_P(TransformMultiplyFixture, Multiplied)
  224. {
  225. const Transform& inputA = ::testing::get<0>(GetParam());
  226. const Transform& inputB = ::testing::get<1>(GetParam());
  227. const Transform& expected = ::testing::get<2>(GetParam());
  228. EXPECT_THAT(
  229. inputA.Multiplied(inputB),
  230. IsClose(expected)
  231. );
  232. EXPECT_THAT(
  233. inputA.Multiplied(Transform::CreateIdentity()),
  234. IsClose(inputA)
  235. );
  236. }
  237. TEST_P(TransformMultiplyFixture, PreMultiply)
  238. {
  239. Transform inputA = ::testing::get<0>(GetParam());
  240. const Transform& inputB = ::testing::get<1>(GetParam());
  241. const Transform& expected = ::testing::get<3>(GetParam());
  242. EXPECT_THAT(
  243. inputA.PreMultiply(inputB),
  244. IsClose(expected)
  245. );
  246. EXPECT_THAT(
  247. inputA.PreMultiply(Transform::CreateIdentity()),
  248. IsClose(inputA)
  249. );
  250. }
  251. TEST_P(TransformMultiplyFixture, MultiplyWithOutputParam)
  252. {
  253. const Transform& inputA = ::testing::get<0>(GetParam());
  254. const Transform& inputB = ::testing::get<1>(GetParam());
  255. const Transform& expected = ::testing::get<2>(GetParam());
  256. Transform output = Transform::CreateIdentity();
  257. inputA.Multiply(inputB, &output);
  258. EXPECT_THAT(output, IsClose(expected));
  259. }
  260. TEST_P(TransformMultiplyFixture, PreMultiplied)
  261. {
  262. const Transform& inputA = ::testing::get<0>(GetParam());
  263. const Transform& inputB = ::testing::get<1>(GetParam());
  264. const Transform& expected = ::testing::get<3>(GetParam());
  265. EXPECT_THAT(
  266. inputA.PreMultiplied(inputB),
  267. IsClose(expected)
  268. );
  269. EXPECT_THAT(
  270. inputA.PreMultiplied(Transform::CreateIdentity()),
  271. IsClose(inputA)
  272. );
  273. }
  274. TEST_P(TransformMultiplyFixture, PreMultiplyWithOutputParam)
  275. {
  276. const Transform& inputA = ::testing::get<0>(GetParam());
  277. const Transform& inputB = ::testing::get<1>(GetParam());
  278. const Transform& expected = ::testing::get<3>(GetParam());
  279. Transform output;
  280. inputA.PreMultiply(inputB, &output);
  281. EXPECT_THAT(
  282. output,
  283. IsClose(expected)
  284. );
  285. }
  286. TEST_P(TransformMultiplyFixture, operatorMult)
  287. {
  288. const Transform& inputA = ::testing::get<0>(GetParam());
  289. const Transform& inputB = ::testing::get<1>(GetParam());
  290. const Transform& expected = ::testing::get<2>(GetParam());
  291. const Transform& expectedPreMult = ::testing::get<3>(GetParam());
  292. EXPECT_THAT(
  293. inputA * inputB,
  294. IsClose(expected)
  295. );
  296. EXPECT_THAT(
  297. inputB * inputA,
  298. IsClose(expectedPreMult)
  299. );
  300. EXPECT_THAT(
  301. inputA * Transform::CreateIdentity(),
  302. IsClose(inputA)
  303. );
  304. EXPECT_THAT(
  305. inputB * Transform::CreateIdentity(),
  306. IsClose(inputB)
  307. );
  308. }
  309. INSTANTIATE_TEST_CASE_P(Test, TransformMultiplyFixture,
  310. ::testing::Values(
  311. TransformMultiplyParams {
  312. /* input a */{Transform::CreateIdentity()},
  313. /* input b */{Transform::CreateIdentity()},
  314. /* a * b = */{Transform::CreateIdentity()},
  315. /* b * a = */{Transform::CreateIdentity()}
  316. },
  317. // symmetric cases (where a*b == b*a) -----------------------------
  318. TransformMultiplyParams {
  319. // just translation
  320. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  321. /* input b */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  322. /* a * b = */{AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  323. /* b * a = */{AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()}
  324. },
  325. TransformMultiplyParams {
  326. // just rotation
  327. /* input a */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  328. /* input b */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  329. /* a * b = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3::CreateOne()},
  330. /* b * a = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3::CreateOne()}
  331. },
  332. TransformMultiplyParams {
  333. // just scale
  334. /* input a */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  335. /* input b */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  336. /* a * b = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(4.0f, 4.0f, 4.0f)},
  337. /* b * a = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(4.0f, 4.0f, 4.0f)}
  338. },
  339. TransformMultiplyParams {
  340. // translation and rotation
  341. /* input a */{AZ::Vector3::CreateAxisY(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  342. /* input b */{AZ::Vector3::CreateAxisY(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  343. /* a * b = */{AZ::Vector3(0.0f, 1.0f + sqrt2over2, sqrt2over2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3::CreateOne()},
  344. /* b * a = */{AZ::Vector3(0.0f, 1.0f + sqrt2over2, sqrt2over2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3::CreateOne()}
  345. },
  346. TransformMultiplyParams {
  347. // rotation and scale
  348. /* input a */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  349. /* input b */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  350. /* a * b = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(4.0f, 4.0f, 4.0f)},
  351. /* b * a = */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(4.0f, 4.0f, 4.0f)}
  352. },
  353. TransformMultiplyParams {
  354. // translation and scale
  355. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  356. /* input b */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  357. /* a * b = */{AZ::Vector3(3.0f, 3.0f, 3.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(4.0f, 4.0f, 4.0f)},
  358. /* b * a = */{AZ::Vector3(3.0f, 3.0f, 3.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(4.0f, 4.0f, 4.0f)}
  359. },
  360. TransformMultiplyParams {
  361. // translation, rotation, and scale
  362. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  363. /* input b */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  364. /* a * b = */{AZ::Vector3(3.0f, 1.0f, 1.0f + 2*sqrt2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(4.0f, 4.0f, 4.0f)},
  365. /* b * a = */{AZ::Vector3(3.0f, 1.0f, 1.0f + 2*sqrt2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(4.0f, 4.0f, 4.0f)}
  366. },
  367. // asymmetric cases (where a*b != b*a) -----------------------------
  368. TransformMultiplyParams {
  369. // translation and rotation
  370. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  371. /* input b */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  372. // translate then rotate
  373. /* a * b = */{AZ::Vector3(1.0f, 0.0f, sqrt2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  374. // rotate then translate
  375. /* b * a = */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()}
  376. },
  377. TransformMultiplyParams {
  378. // translation and scale
  379. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  380. /* input b */{AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  381. // translate then scale
  382. /* a * b = */{AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  383. // scale then translate
  384. /* b * a = */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)}
  385. },
  386. TransformMultiplyParams {
  387. // rotation and scale
  388. // rotation * scale are only asymmetric when there is a translation involved as well
  389. /* input a */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  390. /* input b */{AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  391. // rotate then scale
  392. /* a * b = */{AZ::Vector3(3.0f, 3.0f, 3.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  393. // scale then rotate
  394. /* b * a = */{AZ::Vector3(2.0f, 1.0f, 1.0f + sqrt2), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  395. }
  396. )
  397. );
  398. TEST(TransformFixture, TransformPoint)
  399. {
  400. EXPECT_THAT(
  401. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity())
  402. .TransformPoint(AZ::Vector3::CreateZero()),
  403. IsClose(AZ::Vector3(5.0f, 0.0f, 0.0f))
  404. );
  405. EXPECT_THAT(
  406. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.5f, 1.0f, 1.0f))
  407. .TransformPoint(AZ::Vector3::CreateAxisX()),
  408. IsClose(EMFX_SCALE ? AZ::Vector3(7.5f, 0.0f, 0.0f) : AZ::Vector3(6.0f, 0.0f, 0.0f))
  409. );
  410. EXPECT_THAT(
  411. Transform(AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3::CreateOne())
  412. .TransformPoint(AZ::Vector3(0.0f, 1.0f, 0.0f)),
  413. IsClose(AZ::Vector3(0.0f, sqrt2over2, sqrt2over2))
  414. );
  415. EXPECT_THAT(
  416. Transform(AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3(1.0f, 2.0f, 3.0f))
  417. .TransformPoint(AZ::Vector3::CreateOne()),
  418. IsClose(AZ::Vector3(1.0f, -sqrt2over2, sqrt2over2 * 5.0f))
  419. );
  420. EXPECT_THAT(
  421. Transform(AZ::Vector3(5.0f, 6.0f, 7.0f), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3(1.0f, 2.0f, 3.0f))
  422. .TransformPoint(AZ::Vector3::CreateOne()),
  423. IsClose(AZ::Vector3(6.0f, 6.0f - sqrt2over2, 7.0f + sqrt2over2 * 5.0f))
  424. );
  425. }
  426. TEST(TransformFixture, TransformVector)
  427. {
  428. EXPECT_THAT(
  429. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity())
  430. .TransformVector(AZ::Vector3::CreateZero()),
  431. IsClose(AZ::Vector3::CreateZero())
  432. );
  433. EXPECT_THAT(
  434. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.5f, 1.0f, 1.0f))
  435. .TransformVector(AZ::Vector3::CreateAxisX()),
  436. IsClose(EMFX_SCALE ? AZ::Vector3(2.5f, 0.0f, 0.0f) : AZ::Vector3::CreateAxisX())
  437. );
  438. EXPECT_THAT(
  439. Transform(AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3::CreateOne())
  440. .TransformVector(AZ::Vector3::CreateAxisY()),
  441. IsClose(AZ::Vector3(0.0f, sqrt2over2, sqrt2over2))
  442. );
  443. EXPECT_THAT(
  444. Transform(AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3(1.0f, 2.0f, 3.0f))
  445. .TransformVector(AZ::Vector3::CreateOne()),
  446. IsClose(AZ::Vector3(1.0f, -sqrt2over2, sqrt2over2 * 5.0f))
  447. );
  448. }
  449. TEST(TransformFixture, RotateVector)
  450. {
  451. EXPECT_THAT(
  452. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity())
  453. .RotateVector(AZ::Vector3::CreateZero()),
  454. IsClose(AZ::Vector3::CreateZero())
  455. );
  456. EXPECT_THAT(
  457. Transform(AZ::Vector3(5.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.5f, 1.0f, 1.0f))
  458. .RotateVector(AZ::Vector3::CreateAxisX()),
  459. IsClose(AZ::Vector3::CreateAxisX())
  460. );
  461. EXPECT_THAT(
  462. Transform(AZ::Vector3::CreateZero(), AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi), AZ::Vector3::CreateOne())
  463. .RotateVector(AZ::Vector3::CreateAxisY()),
  464. IsClose(AZ::Vector3(0.0f, sqrt2over2, sqrt2over2))
  465. );
  466. }
  467. TEST_P(TransformConstructFromVec3QuatVec3Fixture, Inverse)
  468. {
  469. // Inverse does not work properly when there is non-uniform scale
  470. if (HasNonUniformScale())
  471. {
  472. return;
  473. }
  474. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  475. const Transform inverse = Transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale()).Inverse();
  476. const AZ::Vector3 point(1.0f, 2.0f, 3.0f);
  477. EXPECT_THAT(
  478. inverse.TransformPoint(transform.TransformPoint(point)),
  479. IsClose(point)
  480. );
  481. }
  482. TEST_P(TransformConstructFromVec3QuatVec3Fixture, Inversed)
  483. {
  484. if (HasNonUniformScale())
  485. {
  486. return;
  487. }
  488. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  489. const Transform inverse = transform.Inversed();
  490. const AZ::Vector3 point(1.0f, 2.0f, 3.0f);
  491. EXPECT_THAT(
  492. inverse.TransformPoint(transform.TransformPoint(point)),
  493. IsClose(point)
  494. );
  495. }
  496. TEST_P(TransformConstructFromVec3QuatVec3Fixture, CalcRelativeToWithOutputParam)
  497. {
  498. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  499. const Transform someTransform(
  500. AZ::Vector3(20.0f, 30.0f, 40.0f),
  501. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.2f, 0.4f, 0.7f).GetNormalized(), 0.25f),
  502. AZ::Vector3(2.0f, 3.0f, 4.0f)
  503. );
  504. Transform relative;
  505. transform.CalcRelativeTo(someTransform, &relative);
  506. EXPECT_THAT(
  507. relative * someTransform,
  508. IsClose(transform)
  509. );
  510. }
  511. TEST_P(TransformConstructFromVec3QuatVec3Fixture, CalcRelativeTo)
  512. {
  513. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  514. const Transform someTransform(
  515. AZ::Vector3(20.0f, 30.0f, 40.0f),
  516. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.2f, 0.4f, 0.7f).GetNormalized(), 0.25f),
  517. AZ::Vector3(2.0f, 3.0f, 4.0f)
  518. );
  519. const Transform relative = transform.CalcRelativeTo(someTransform);
  520. EXPECT_THAT(
  521. relative * someTransform,
  522. IsClose(transform)
  523. );
  524. }
  525. TEST_P(TransformConstructFromVec3QuatVec3Fixture, InverseWithOutputParam)
  526. {
  527. if (HasNonUniformScale())
  528. {
  529. return;
  530. }
  531. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  532. Transform inverse;
  533. transform.Inverse(&inverse);
  534. const AZ::Vector3 point(1.0f, 2.0f, 3.0f);
  535. EXPECT_THAT(
  536. inverse.TransformPoint(transform.TransformPoint(point)),
  537. IsClose(point)
  538. );
  539. }
  540. TEST_P(TransformConstructFromVec3QuatVec3Fixture, Mirror)
  541. {
  542. const AZ::Vector3 axis = AZ::Vector3::CreateAxisX();
  543. const Transform mirrorTransform = Transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale()).Mirror(axis);
  544. const AZ::Matrix4x4 mirrorMatrix = GetMirroredTransform(axis);
  545. const AZ::Vector3 point(3.0f, 4.0f, 5.0f);
  546. EXPECT_THAT(
  547. mirrorTransform.TransformPoint(point),
  548. IsClose(mirrorMatrix * point)
  549. );
  550. }
  551. TEST(TransformFixture, MirrorWithFlags)
  552. {
  553. }
  554. TEST_P(TransformConstructFromVec3QuatVec3Fixture, Mirrored)
  555. {
  556. const AZ::Vector3 axis = AZ::Vector3::CreateAxisX();
  557. const Transform mirrorTransform = Transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale()).Mirrored(axis);
  558. const AZ::Matrix4x4 mirrorMatrix = GetMirroredTransform(axis);
  559. const AZ::Vector3 point(3.0f, 4.0f, 5.0f);
  560. EXPECT_THAT(
  561. mirrorTransform.TransformPoint(point),
  562. IsClose(mirrorMatrix * point)
  563. );
  564. }
  565. TEST_P(TransformConstructFromVec3QuatVec3Fixture, MirrorWithOutputParam)
  566. {
  567. const AZ::Vector3 axis = AZ::Vector3::CreateAxisX();
  568. Transform mirrorTransform;
  569. Transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale()).Mirror(axis, &mirrorTransform);
  570. const AZ::Matrix4x4 mirrorMatrix = GetMirroredTransform(axis);
  571. const AZ::Vector3 point(3.0f, 4.0f, 5.0f);
  572. EXPECT_THAT(
  573. mirrorTransform.TransformPoint(point),
  574. IsClose(mirrorMatrix * point)
  575. );
  576. }
  577. struct ApplyDeltaParams
  578. {
  579. const Transform m_initial;
  580. const Transform m_a;
  581. const Transform m_b;
  582. const Transform m_expected;
  583. const float m_weight;
  584. };
  585. using TransformApplyDeltaFixture = ::testing::TestWithParam<ApplyDeltaParams>;
  586. TEST_P(TransformApplyDeltaFixture, ApplyDelta)
  587. {
  588. if (GetParam().m_weight != 1.0f)
  589. {
  590. return;
  591. }
  592. Transform transform = GetParam().m_initial;
  593. transform.ApplyDelta(GetParam().m_a, GetParam().m_b);
  594. EXPECT_THAT(
  595. transform,
  596. IsClose(GetParam().m_expected)
  597. );
  598. }
  599. TEST_P(TransformApplyDeltaFixture, ApplyDeltaMirrored)
  600. {
  601. if (GetParam().m_weight != 1.0f)
  602. {
  603. return;
  604. }
  605. const AZ::Vector3 mirrorAxis = AZ::Vector3::CreateAxisX();
  606. Transform transform = GetParam().m_initial;
  607. transform.ApplyDeltaMirrored(GetParam().m_a, GetParam().m_b, mirrorAxis);
  608. EXPECT_THAT(
  609. transform,
  610. IsClose(GetParam().m_expected.Mirrored(mirrorAxis))
  611. );
  612. }
  613. TEST_P(TransformApplyDeltaFixture, ApplyDeltaWithWeight)
  614. {
  615. Transform transform = GetParam().m_initial;
  616. transform.ApplyDeltaWithWeight(GetParam().m_a, GetParam().m_b, GetParam().m_weight);
  617. EXPECT_THAT(
  618. transform,
  619. IsClose(GetParam().m_expected)
  620. );
  621. }
  622. INSTANTIATE_TEST_CASE_P(Test, TransformApplyDeltaFixture,
  623. ::testing::ValuesIn(std::vector<ApplyDeltaParams>{
  624. {
  625. {Transform::CreateIdentity()},
  626. {AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  627. {AZ::Vector3(2.0f, 3.0f, 4.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  628. {AZ::Vector3(0.5f, 0.5f, 0.5f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  629. 0.5f,
  630. },
  631. {
  632. {Transform::CreateIdentity()},
  633. {AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  634. {AZ::Vector3(2.0f, 3.0f, 4.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  635. {AZ::Vector3(1.0f, 1.0f, 1.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  636. 1.0f,
  637. },
  638. {
  639. {Transform::CreateIdentity()},
  640. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi / 2.0f), AZ::Vector3::CreateOne()},
  641. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  642. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi / 4.0f), AZ::Vector3::CreateOne()},
  643. 0.5f,
  644. },
  645. {
  646. {Transform::CreateIdentity()},
  647. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi / 2.0f), AZ::Vector3::CreateOne()},
  648. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()},
  649. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi / 2.0f), AZ::Vector3::CreateOne()},
  650. 1.0f,
  651. },
  652. {
  653. {Transform::CreateIdentity()},
  654. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  655. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  656. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(1.5f, 1.5f, 1.5f)},
  657. 0.5f,
  658. },
  659. {
  660. {Transform::CreateIdentity()},
  661. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3::CreateOne()},
  662. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  663. {AZ::Vector3::CreateZero(), AZ::Quaternion::CreateIdentity(), AZ::Vector3(2.0f, 2.0f, 2.0f)},
  664. 1.0f,
  665. },
  666. })
  667. );
  668. TEST_P(TransformConstructFromVec3QuatVec3Fixture, CheckIfHasScale)
  669. {
  670. const Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  671. EXPECT_EQ(transform.CheckIfHasScale(), !ExpectedScale().IsClose(AZ::Vector3::CreateOne()));
  672. }
  673. TEST(TransformFixture, Normalize)
  674. {
  675. const Transform transform = Transform(
  676. AZ::Vector3::CreateOne(),
  677. AZ::Quaternion(2.0f, 0.0f, 0.0f, 2.0f),
  678. AZ::Vector3::CreateOne()
  679. ).Normalize();
  680. EXPECT_FLOAT_EQ(transform.m_rotation.GetLength(), 1.0f);
  681. }
  682. TEST(TransformFixture, Normalized)
  683. {
  684. const Transform transform = Transform(
  685. AZ::Vector3::CreateOne(),
  686. AZ::Quaternion(2.0f, 0.0f, 0.0f, 2.0f),
  687. AZ::Vector3::CreateOne()
  688. ).Normalized();
  689. EXPECT_FLOAT_EQ(transform.m_rotation.GetLength(), 1.0f);
  690. }
  691. TEST(TransformFixture, BlendAdditive)
  692. {
  693. {
  694. const Transform result =
  695. Transform(AZ::Vector3(5.0f, 6.0f, 7.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3::CreateOne()).BlendAdditive(
  696. /*dest=*/Transform(AZ::Vector3(11.0f, 12.0f, 13.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi), AZ::Vector3(2.0f, 2.0f, 2.0f)),
  697. /*orgTransform=*/Transform(AZ::Vector3(8.0f, 10.0f, 12.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi), AZ::Vector3(2.0f, 3.0f, 2.0f)),
  698. 0.5f
  699. );
  700. EXPECT_THAT(
  701. result,
  702. IsClose(Transform(AZ::Vector3(6.5f, 7.0f, 7.5f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::Pi * 3.0f / 8.0f), AZ::Vector3(1.0f, 0.5f, 1.0f)))
  703. );
  704. }
  705. }
  706. class TwoTransformsFixture
  707. : public ::testing::Test
  708. {
  709. protected:
  710. const AZ::Vector3 m_translationA{5.0f, 6.0f, 7.0f};
  711. const AZ::Quaternion m_rotationA = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi);
  712. const AZ::Vector3 m_scaleA = AZ::Vector3::CreateOne();
  713. const AZ::Vector3 m_translationB{11.0f, 12.0f, 13.0f};
  714. const AZ::Quaternion m_rotationB = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi);
  715. const AZ::Vector3 m_scaleB{3.0f, 4.0f, 5.0f};
  716. };
  717. TEST_F(TwoTransformsFixture, Blend)
  718. {
  719. const Transform transformA(m_translationA, m_rotationA, m_scaleA);
  720. const Transform transformB(m_translationB, m_rotationB, m_scaleB);
  721. EXPECT_THAT(
  722. Transform(m_translationA, m_rotationA, m_scaleA).Blend(transformB, 0.0f),
  723. IsClose(transformA)
  724. );
  725. EXPECT_THAT(
  726. Transform(m_translationA, m_rotationA, m_scaleA).Blend(transformB, 0.25f),
  727. IsClose(Transform(AZ::Vector3(6.5f, 7.5f, 8.5f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::Pi * 5.0f / 16.0f), AZ::Vector3(1.5f, 1.75f, 2.0f)))
  728. );
  729. EXPECT_THAT(
  730. Transform(m_translationA, m_rotationA, m_scaleA).Blend(transformB, 0.5f),
  731. IsClose(Transform(AZ::Vector3(8.0f, 9.0f, 10.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::Pi * 3.0f / 8.0f), AZ::Vector3(2.0f, 2.5f, 3.0f)))
  732. );
  733. EXPECT_THAT(
  734. Transform(m_translationA, m_rotationA, m_scaleA).Blend(transformB, 0.75f),
  735. IsClose(Transform(AZ::Vector3(9.5f, 10.5f, 11.5f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::Pi * 7.0f / 16.0f), AZ::Vector3(2.5f, 3.25f, 4.0f)))
  736. );
  737. EXPECT_THAT(
  738. Transform(m_translationA, m_rotationA, m_scaleA).Blend(transformB, 1.0f),
  739. IsClose(transformB)
  740. );
  741. }
  742. TEST_F(TwoTransformsFixture, ApplyAdditiveTransform)
  743. {
  744. EXPECT_THAT(
  745. Transform(m_translationA, m_rotationA, m_scaleA).ApplyAdditive(Transform(m_translationB, m_rotationB, m_scaleB)),
  746. IsClose(Transform(m_translationA + m_translationB, m_rotationA * m_rotationB, m_scaleA * m_scaleB))
  747. );
  748. }
  749. TEST_F(TwoTransformsFixture, ApplyAdditiveTransformFloat)
  750. {
  751. const float factor = 0.5f;
  752. EXPECT_THAT(
  753. Transform(m_translationA, m_rotationA, m_scaleA).ApplyAdditive(Transform(m_translationB, m_rotationB, m_scaleB), factor),
  754. IsClose(Transform(m_translationA + m_translationB * factor, m_rotationA.NLerp(m_rotationA * m_rotationB, factor), m_scaleA * AZ::Vector3::CreateOne().Lerp(m_scaleB, factor)))
  755. );
  756. }
  757. TEST_F(TwoTransformsFixture, AddTransform)
  758. {
  759. EXPECT_THAT(
  760. Transform(m_translationA, m_rotationA, m_scaleA).Add(Transform(m_translationB, m_rotationB, m_scaleB)),
  761. IsClose(Transform(m_translationA + m_translationB, m_rotationA + m_rotationB, m_scaleA + m_scaleB))
  762. );
  763. }
  764. TEST_F(TwoTransformsFixture, AddTransformFloat)
  765. {
  766. const float factor = 0.5f;
  767. EXPECT_THAT(
  768. Transform(m_translationA, m_rotationA, m_scaleA).Add(Transform(m_translationB, m_rotationB, m_scaleB), factor),
  769. IsClose(Transform(m_translationA + m_translationB * factor, m_rotationA + m_rotationB * factor, m_scaleA + m_scaleB * factor))
  770. );
  771. }
  772. TEST_F(TwoTransformsFixture, Subtract)
  773. {
  774. EXPECT_THAT(
  775. Transform(m_translationA, m_rotationA, m_scaleA).Subtract(Transform(m_translationB, m_rotationB, m_scaleB)),
  776. IsClose(Transform(m_translationA - m_translationB, m_rotationA - m_rotationB, m_scaleA - m_scaleB))
  777. );
  778. }
  779. class TransformProjectedToGroundPlaneFixture
  780. : public TransformConstructFromVec3QuatVec3Fixture
  781. {
  782. public:
  783. bool ShouldSkip() const
  784. {
  785. // These tests do not meet the expectation when there is both a
  786. // pitch and a roll value
  787. // This is because the combination of pitch + roll, even when yaw
  788. // is 0, introduces a rotation around z
  789. return ::testing::get<0>(::testing::get<1>(GetParam())) != 0
  790. && ::testing::get<1>(::testing::get<1>(GetParam())) != 0;
  791. }
  792. void Expect(const Transform& transform, float zValue) const
  793. {
  794. EXPECT_THAT(
  795. transform,
  796. IsClose(Transform(
  797. AZ::Vector3(ExpectedPosition().GetX(), ExpectedPosition().GetY(), zValue),
  798. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), ::testing::get<2>(::testing::get<1>(GetParam()))),
  799. ExpectedScale()
  800. ))
  801. );
  802. }
  803. };
  804. TEST_P(TransformProjectedToGroundPlaneFixture, ApplyMotionExtractionFlags)
  805. {
  806. if (ShouldSkip())
  807. {
  808. return;
  809. }
  810. Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  811. transform.ApplyMotionExtractionFlags(EMotionExtractionFlags(0));
  812. Expect(transform, 0.0f);
  813. }
  814. TEST_P(TransformProjectedToGroundPlaneFixture, ApplyMotionExtractionFlagsCaptureZ)
  815. {
  816. if (ShouldSkip())
  817. {
  818. return;
  819. }
  820. Transform transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale());
  821. transform.ApplyMotionExtractionFlags(MOTIONEXTRACT_CAPTURE_Z);
  822. Expect(transform, ExpectedPosition().GetZ());
  823. }
  824. TEST_P(TransformProjectedToGroundPlaneFixture, ProjectedToGroundPlane)
  825. {
  826. if (ShouldSkip())
  827. {
  828. return;
  829. }
  830. Expect(Transform(ExpectedPosition(), ExpectedRotation(), ExpectedScale()).ProjectedToGroundPlane(), 0.0f);
  831. }
  832. const auto possiblePitchAndYawValues = ::testing::Values(
  833. -AZ::Constants::HalfPi,
  834. -AZ::Constants::QuarterPi,
  835. 0.0f,
  836. AZ::Constants::QuarterPi,
  837. AZ::Constants::HalfPi
  838. );
  839. INSTANTIATE_TEST_CASE_P(Test, TransformProjectedToGroundPlaneFixture,
  840. ::testing::Combine(
  841. ::testing::Values(
  842. AZ::Vector3::CreateZero(),
  843. AZ::Vector3(6.0f, 7.0f, 8.0f)
  844. ),
  845. ::testing::Combine(
  846. /* possible pitch values */ possiblePitchAndYawValues,
  847. /* possible roll values */ ::testing::Values(0.0f, AZ::Constants::QuarterPi),
  848. /* possible yaw values */ possiblePitchAndYawValues
  849. ),
  850. ::testing::Values(
  851. AZ::Vector3::CreateOne()
  852. )
  853. )
  854. );
  855. } // namespace EMotionFX