PoseTests.cpp 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  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/Math/Random.h>
  9. #include <Tests/SystemComponentFixture.h>
  10. #include <Tests/Matchers.h>
  11. #include <MCore/Source/MemoryObject.h>
  12. #include <EMotionFX/Source/Actor.h>
  13. #include <EMotionFX/Source/ActorInstance.h>
  14. #include <EMotionFX/Source/Mesh.h>
  15. #include <EMotionFX/Source/MeshDeformerStack.h>
  16. #include <EMotionFX/Source/MorphSetup.h>
  17. #include <EMotionFX/Source/MorphSetupInstance.h>
  18. #include <EMotionFX/Source/MorphTargetStandard.h>
  19. #include <EMotionFX/Source/Node.h>
  20. #include <EMotionFX/Source/PoseData.h>
  21. #include <EMotionFX/Source/PoseDataFactory.h>
  22. #include <EMotionFX/Source/PoseDataRagdoll.h>
  23. #include <EMotionFX/Source/Skeleton.h>
  24. #include <EMotionFX/Source/SoftSkinDeformer.h>
  25. #include <EMotionFX/Source/SoftSkinManager.h>
  26. #include <EMotionFX/Source/TransformData.h>
  27. #include <EMotionFX/Source/Transform.h>
  28. #include <Tests/TestAssetCode/SimpleActors.h>
  29. #include <Tests/TestAssetCode/ActorFactory.h>
  30. namespace EMotionFX
  31. {
  32. class PoseTests
  33. : public SystemComponentFixture
  34. {
  35. class ActorWithMorphs
  36. : public SimpleJointChainActor
  37. {
  38. public:
  39. explicit ActorWithMorphs(size_t numMorphTargets, const char* name = "Test actor")
  40. : SimpleJointChainActor(5, name)
  41. {
  42. SetMotionExtractionNodeIndex(0);
  43. MorphSetup* morphSetup = MorphSetup::Create();
  44. SetMorphSetup(0, morphSetup);
  45. for (size_t i = 0; i < numMorphTargets; ++i)
  46. {
  47. MorphTargetStandard* morphTarget = MorphTargetStandard::Create(AZStd::string::format("MT#%zu", i).c_str());
  48. morphTarget->SetRangeMin(0.0f);
  49. morphTarget->SetRangeMax(1.0f);
  50. morphSetup->AddMorphTarget(morphTarget);
  51. }
  52. }
  53. };
  54. public:
  55. void SetUp() override
  56. {
  57. SystemComponentFixture::SetUp();
  58. m_actor = ActorFactory::CreateAndInit<ActorWithMorphs>(m_numMorphTargets);
  59. m_actorInstance = ActorInstance::Create(m_actor.get());
  60. }
  61. void TearDown() override
  62. {
  63. m_actorInstance->Destroy();
  64. m_actor.reset();
  65. SystemComponentFixture::TearDown();
  66. }
  67. void CompareFlags(const Pose& pose, uint8 expectedFlags)
  68. {
  69. const size_t numTransforms = pose.GetNumTransforms();
  70. for (size_t i = 0; i < numTransforms; ++i)
  71. {
  72. EXPECT_EQ(pose.GetFlags(i), expectedFlags);
  73. }
  74. }
  75. void CompareFlags(const Pose& poseA, const Pose& poseB)
  76. {
  77. const size_t numTransforms = poseA.GetNumTransforms();
  78. EXPECT_EQ(numTransforms, poseB.GetNumTransforms());
  79. for (size_t i = 0; i < numTransforms; ++i)
  80. {
  81. EXPECT_EQ(poseA.GetFlags(i), poseB.GetFlags(i));
  82. }
  83. }
  84. void CompareMorphTargets(const Pose& poseA, const Pose& poseB)
  85. {
  86. const size_t numMorphWeights = poseA.GetNumMorphWeights();
  87. EXPECT_EQ(numMorphWeights, poseB.GetNumMorphWeights());
  88. for (size_t i = 0; i < numMorphWeights; ++i)
  89. {
  90. EXPECT_EQ(poseA.GetMorphWeight(i), poseB.GetMorphWeight(i));
  91. }
  92. }
  93. void CheckIfRotationIsNormalized(const AZ::Quaternion& rotation)
  94. {
  95. const float epsilon = 0.01f;
  96. const float length = rotation.GetLength();
  97. EXPECT_TRUE(AZ::IsClose(length, 1.0f, epsilon))
  98. << "Rotation quaternion not normalized. Length is " << length << ".";
  99. }
  100. void ComparePoseTransforms(const Pose& poseA, const Pose& poseB)
  101. {
  102. const size_t numTransforms = poseA.GetNumTransforms();
  103. EXPECT_EQ(numTransforms, poseB.GetNumTransforms());
  104. for (size_t i = 0; i < numTransforms; ++i)
  105. {
  106. const Transform& localA = poseA.GetLocalSpaceTransform(i);
  107. const Transform& localB = poseB.GetLocalSpaceTransform(i);
  108. EXPECT_EQ(localA, localB);
  109. EXPECT_THAT(poseA.GetModelSpaceTransform(i), poseB.GetModelSpaceTransform(i));
  110. }
  111. }
  112. AZ::Quaternion CreateRandomUnnormalizedQuaternion(AZ::SimpleLcgRandom& random) const
  113. {
  114. AZ::Quaternion candidate;
  115. do
  116. {
  117. candidate.Set(random.GetRandomFloat(), random.GetRandomFloat(), random.GetRandomFloat(), random.GetRandomFloat());
  118. }
  119. while (AZ::IsClose(candidate.GetLength(), 1.0f, AZ::Constants::FloatEpsilon));
  120. return candidate;
  121. }
  122. public:
  123. AZStd::unique_ptr<Actor> m_actor;
  124. ActorInstance* m_actorInstance = nullptr;
  125. const size_t m_numMorphTargets = 5;
  126. const float m_testOffset = 10.0f;
  127. };
  128. class PoseTestsBoolParam
  129. : public PoseTests
  130. , public ::testing::WithParamInterface<bool>
  131. {
  132. };
  133. INSTANTIATE_TEST_CASE_P(PoseTests, PoseTestsBoolParam, ::testing::Bool());
  134. TEST_F(PoseTests, Clear)
  135. {
  136. Pose pose;
  137. pose.LinkToActor(m_actor.get());
  138. EXPECT_EQ(pose.GetNumTransforms(), m_actor->GetNumNodes());
  139. pose.Clear();
  140. EXPECT_EQ(pose.GetNumTransforms(), 0);
  141. pose.LinkToActor(m_actor.get());
  142. EXPECT_EQ(pose.GetNumTransforms(), m_actor->GetNumNodes());
  143. pose.Clear(/*clearMem*/false);
  144. EXPECT_EQ(pose.GetNumTransforms(), 0);
  145. }
  146. TEST_F(PoseTests, ClearFlags)
  147. {
  148. Pose pose;
  149. pose.LinkToActor(m_actor.get(), 100);
  150. EXPECT_EQ(pose.GetNumTransforms(), m_actor->GetNumNodes());
  151. CompareFlags(pose, 100);
  152. pose.ClearFlags(200);
  153. CompareFlags(pose, 200);
  154. }
  155. TEST_F(PoseTests, GetSetFlags)
  156. {
  157. Pose pose;
  158. pose.LinkToActor(m_actor.get());
  159. const size_t numTransforms = pose.GetNumTransforms();
  160. for (size_t i = 0; i < numTransforms; ++i)
  161. {
  162. pose.SetFlags(i, Pose::FLAG_LOCALTRANSFORMREADY);
  163. EXPECT_EQ(pose.GetFlags(i), Pose::FLAG_LOCALTRANSFORMREADY);
  164. pose.SetFlags(i, Pose::FLAG_LOCALTRANSFORMREADY | Pose::FLAG_MODELTRANSFORMREADY);
  165. EXPECT_EQ(pose.GetFlags(i), Pose::FLAG_LOCALTRANSFORMREADY | Pose::FLAG_MODELTRANSFORMREADY);
  166. }
  167. }
  168. TEST_F(PoseTests, InitFromBindPose)
  169. {
  170. Pose pose;
  171. pose.LinkToActor(m_actor.get());
  172. pose.InitFromBindPose(m_actor.get());
  173. const Pose* bindPose = m_actor->GetBindPose();
  174. ComparePoseTransforms(pose, *bindPose);
  175. CompareFlags(pose, *bindPose);
  176. CompareMorphTargets(pose, *bindPose);
  177. }
  178. TEST_F(PoseTests, InitFromPose)
  179. {
  180. Pose poseA;
  181. poseA.LinkToActor(m_actor.get());
  182. const Pose* bindPose = m_actor->GetBindPose();
  183. poseA.InitFromPose(bindPose);
  184. Pose poseB;
  185. poseB.LinkToActor(m_actor.get());
  186. poseB.InitFromPose(&poseA);
  187. ComparePoseTransforms(poseA, poseB);
  188. CompareFlags(poseA, poseB);
  189. CompareMorphTargets(poseA, poseB);
  190. }
  191. TEST_F(PoseTests, LinkToActorInstance)
  192. {
  193. Pose pose;
  194. pose.LinkToActorInstance(m_actorInstance);
  195. EXPECT_EQ(pose.GetNumTransforms(), m_actor->GetNumNodes());
  196. EXPECT_EQ(pose.GetActor(), m_actor.get());
  197. EXPECT_EQ(pose.GetSkeleton(), m_actor->GetSkeleton());
  198. EXPECT_EQ(pose.GetActorInstance(), m_actorInstance);
  199. }
  200. TEST_F(PoseTests, LinkToActor)
  201. {
  202. Pose pose;
  203. pose.LinkToActor(m_actor.get());
  204. EXPECT_EQ(pose.GetNumTransforms(), m_actor->GetNumNodes());
  205. EXPECT_EQ(pose.GetActor(), m_actor.get());
  206. EXPECT_EQ(pose.GetSkeleton(), m_actor->GetSkeleton());
  207. }
  208. TEST_F(PoseTests, SetNumTransforms)
  209. {
  210. Pose pose;
  211. pose.SetNumTransforms(100);
  212. EXPECT_EQ(pose.GetNumTransforms(), 100);
  213. pose.SetNumTransforms(200);
  214. EXPECT_EQ(pose.GetNumTransforms(), 200);
  215. pose.SetNumTransforms(0);
  216. EXPECT_EQ(pose.GetNumTransforms(), 0);
  217. pose.SetNumTransforms(100);
  218. EXPECT_EQ(pose.GetNumTransforms(), 100);
  219. }
  220. TEST_F(PoseTests, ApplyMorphWeightsToActorInstance)
  221. {
  222. Pose pose;
  223. pose.LinkToActorInstance(m_actorInstance);
  224. EXPECT_EQ(pose.GetNumMorphWeights(), m_numMorphTargets);
  225. MorphSetupInstance* morphInstance = m_actorInstance->GetMorphSetupInstance();
  226. EXPECT_EQ(m_numMorphTargets, morphInstance->GetNumMorphTargets());
  227. AZ::SimpleLcgRandom random;
  228. random.SetSeed(875960);
  229. for (size_t i = 0; i < m_numMorphTargets; ++i)
  230. {
  231. // Zero all weights on the morph instance.
  232. morphInstance->GetMorphTarget(i)->SetWeight(0.0f);
  233. // Apply random morph target weights on the pose.
  234. const float newWeight = random.GetRandomFloat();
  235. pose.SetMorphWeight(i, newWeight);
  236. EXPECT_EQ(pose.GetMorphWeight(i), newWeight);
  237. }
  238. pose.ApplyMorphWeightsToActorInstance();
  239. // Check if all weights got correctly forwarded from the pose to the actor instance.
  240. for (size_t i = 0; i < m_numMorphTargets; ++i)
  241. {
  242. EXPECT_EQ(pose.GetMorphWeight(i), morphInstance->GetMorphTarget(i)->GetWeight());
  243. }
  244. }
  245. TEST_F(PoseTests, SetGetZeroMorphWeights)
  246. {
  247. Pose pose;
  248. pose.LinkToActor(m_actor.get());
  249. EXPECT_EQ(pose.GetNumMorphWeights(), m_numMorphTargets);
  250. // Set and get tests.
  251. for (size_t i = 0; i < m_numMorphTargets; ++i)
  252. {
  253. const float newWeight = static_cast<float>(i);
  254. pose.SetMorphWeight(i, newWeight);
  255. EXPECT_EQ(pose.GetMorphWeight(i), newWeight);
  256. }
  257. // Zero weights test.
  258. pose.ZeroMorphWeights();
  259. for (size_t i = 0; i < m_numMorphTargets; ++i)
  260. {
  261. EXPECT_EQ(pose.GetMorphWeight(i), 0.0f);
  262. }
  263. }
  264. TEST_F(PoseTests, ResizeNumMorphs)
  265. {
  266. Pose pose;
  267. pose.LinkToActor(m_actor.get());
  268. EXPECT_EQ(pose.GetNumMorphWeights(), m_numMorphTargets);
  269. pose.ResizeNumMorphs(10);
  270. EXPECT_EQ(pose.GetNumMorphWeights(), 10);
  271. }
  272. TEST_F(PoseTests, GetSetLocalSpaceTransform)
  273. {
  274. Pose pose;
  275. pose.LinkToActor(m_actor.get());
  276. const size_t jointIndex = 0;
  277. // Set the new transform.
  278. Transform newTransform(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));
  279. pose.SetLocalSpaceTransform(jointIndex, newTransform);
  280. EXPECT_TRUE(pose.GetFlags(jointIndex) & Pose::FLAG_LOCALTRANSFORMREADY);
  281. // All model space transforms should be invalidated.
  282. // The model space transform of the node doesn't get automatically updated and
  283. // all child node model transforms are invalidated along with the joint.
  284. for (size_t i = jointIndex; i < m_actor->GetNumNodes(); ++i)
  285. {
  286. EXPECT_FALSE(pose.GetFlags(i) & Pose::FLAG_MODELTRANSFORMREADY);
  287. }
  288. // Test accessor that returns the transform.
  289. EXPECT_EQ(pose.GetLocalSpaceTransform(jointIndex), newTransform);
  290. // Test accessor that writes the transform to a parameter.
  291. Transform compareTransform;
  292. pose.GetLocalSpaceTransform(jointIndex, &compareTransform);
  293. EXPECT_EQ(compareTransform, newTransform);
  294. }
  295. TEST_F(PoseTests, GetSetLocalSpaceTransformDirect)
  296. {
  297. Pose pose;
  298. pose.LinkToActor(m_actor.get());
  299. const size_t jointIndex = 0;
  300. Transform newTransform(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));
  301. pose.SetLocalSpaceTransformDirect(jointIndex, newTransform);
  302. EXPECT_TRUE(pose.GetFlags(jointIndex) & Pose::FLAG_LOCALTRANSFORMREADY);
  303. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(jointIndex), newTransform);
  304. }
  305. TEST_F(PoseTests, GetSetModelSpaceTransform)
  306. {
  307. Pose pose;
  308. pose.LinkToActor(m_actor.get());
  309. const size_t jointIndex = 0;
  310. // Set the new transform.
  311. Transform newTransform(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));
  312. // Test accessor that returns the transform.
  313. pose.SetModelSpaceTransform(jointIndex, newTransform);
  314. // The local space transform gets directly updated along with SetModelSpaceTransform,
  315. // so both, the model space as well as the local space transforms should be ready.
  316. EXPECT_TRUE(pose.GetFlags(jointIndex) & Pose::FLAG_MODELTRANSFORMREADY);
  317. EXPECT_TRUE(pose.GetFlags(jointIndex) & Pose::FLAG_LOCALTRANSFORMREADY);
  318. // All child model space transforms should be invalidated as they haven't been updated yet.
  319. for (size_t i = jointIndex + 1; i < m_actor->GetNumNodes(); ++i)
  320. {
  321. EXPECT_FALSE(pose.GetFlags(i) & Pose::FLAG_MODELTRANSFORMREADY);
  322. }
  323. EXPECT_EQ(pose.GetModelSpaceTransform(jointIndex), newTransform);
  324. // Test accessor that writes the transform to a parameter.
  325. Transform compareTransform;
  326. pose.GetModelSpaceTransform(jointIndex, &compareTransform);
  327. EXPECT_EQ(compareTransform, newTransform);
  328. }
  329. TEST_F(PoseTests, GetSetModelSpaceTransformDirect)
  330. {
  331. Pose pose;
  332. pose.LinkToActor(m_actor.get());
  333. const size_t jointIndex = 0;
  334. Transform newTransform(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));
  335. pose.SetModelSpaceTransformDirect(jointIndex, newTransform);
  336. EXPECT_TRUE(pose.GetFlags(jointIndex) & Pose::FLAG_MODELTRANSFORMREADY);
  337. EXPECT_EQ(pose.GetModelSpaceTransformDirect(jointIndex), newTransform);
  338. }
  339. TEST_F(PoseTests, SetLocalGetModelSpaceTransform)
  340. {
  341. Pose pose;
  342. pose.LinkToActor(m_actor.get());
  343. pose.InitFromBindPose(m_actor.get());
  344. const Transform newTransform(AZ::Vector3(1.0f, 1.0f, 1.0f), AZ::Quaternion::CreateIdentity());
  345. // Iterate through the joints, adjust their local space transforms and check if the model space transform adjusts automatically, accordingly.
  346. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  347. {
  348. pose.SetLocalSpaceTransform(i, newTransform);
  349. EXPECT_EQ(pose.GetLocalSpaceTransform(i), newTransform);
  350. const float floatI = static_cast<float>(i + 1);
  351. EXPECT_EQ(pose.GetModelSpaceTransform(i), Transform(AZ::Vector3(floatI, floatI, floatI), AZ::Quaternion::CreateIdentity()));
  352. }
  353. }
  354. TEST_F(PoseTests, SetLocalDirectGetModelSpaceTransform)
  355. {
  356. Pose pose;
  357. pose.LinkToActor(m_actor.get());
  358. pose.InitFromBindPose(m_actor.get());
  359. const Transform newTransform(AZ::Vector3(1.0f, 1.0f, 1.0f), AZ::Quaternion::CreateIdentity());
  360. // Same as the previous test, but this time we use the direct call which does not automatically invalidate the model space transform.
  361. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  362. {
  363. const Transform oldModelSpaceTransform = pose.GetModelSpaceTransform(i);
  364. // Set the local space transform without invalidating the model space transform.
  365. pose.SetLocalSpaceTransformDirect(i, newTransform);
  366. EXPECT_EQ(pose.GetLocalSpaceTransform(i), newTransform);
  367. // As we used the direct call, the model space tranform did not get invalidated and updated.
  368. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), oldModelSpaceTransform);
  369. // Manually invalidate the model space transform and check the result.
  370. pose.InvalidateModelSpaceTransform(i);
  371. const float floatI = static_cast<float>(i + 1);
  372. EXPECT_EQ(pose.GetModelSpaceTransform(i), Transform(AZ::Vector3(floatI, floatI, floatI), AZ::Quaternion::CreateIdentity()));
  373. }
  374. }
  375. TEST_F(PoseTests, SetModelDirectGetLocalSpaceTransform)
  376. {
  377. Pose pose;
  378. pose.LinkToActor(m_actor.get());
  379. pose.InitFromBindPose(m_actor.get());
  380. // Similar to previous test, model space and local space operations are switched.
  381. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  382. {
  383. const Transform oldLocalSpaceTransform = pose.GetLocalSpaceTransform(i);
  384. const Transform newTransform(Transform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 1) * m_testOffset)), AZ::Quaternion::CreateIdentity()));
  385. // Set the model space transform without invalidating the local space transform.
  386. pose.SetModelSpaceTransformDirect(i, newTransform);
  387. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), newTransform);
  388. // As we used the direct call, the local space tranform did not get invalidated and updated.
  389. EXPECT_EQ(pose.GetLocalSpaceTransform(i), oldLocalSpaceTransform);
  390. // Manually invalidate the local space transform and check the result.
  391. pose.InvalidateLocalSpaceTransform(i);
  392. EXPECT_THAT(pose.GetLocalSpaceTransform(i), IsClose(Transform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity())));
  393. }
  394. }
  395. TEST_P(PoseTestsBoolParam, UpdateLocalSpaceTranforms)
  396. {
  397. Pose pose;
  398. pose.LinkToActor(m_actor.get());
  399. pose.InitFromBindPose(m_actor.get());
  400. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  401. {
  402. const Transform oldLocalSpaceTransform = pose.GetLocalSpaceTransform(i);
  403. const Transform newTransform(Transform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 1) * m_testOffset)), AZ::Quaternion::CreateIdentity()));
  404. // Set the model space transform directly, so that it won't automatically be updated.
  405. pose.SetModelSpaceTransformDirect(i, newTransform);
  406. #if AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  407. EXPECT_THAT(pose.GetModelSpaceTransformDirect(i), IsClose(newTransform));
  408. EXPECT_THAT(pose.GetLocalSpaceTransformDirect(i), IsClose(oldLocalSpaceTransform));
  409. #else
  410. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), newTransform);
  411. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), oldLocalSpaceTransform);
  412. #endif // AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  413. }
  414. // We have to manually update the local space transforms as we directly set them.
  415. pose.InvalidateAllLocalSpaceTransforms();
  416. // Update all invalidated local space transforms.
  417. if (GetParam())
  418. {
  419. pose.UpdateAllLocalSpaceTranforms();
  420. }
  421. else
  422. {
  423. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  424. {
  425. pose.UpdateLocalSpaceTransform(i);
  426. }
  427. }
  428. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  429. {
  430. // Get the local space transform without auto-updating them, to see if update call worked.
  431. #if AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  432. EXPECT_THAT(pose.GetLocalSpaceTransformDirect(i), IsClose(Transform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity())));
  433. #else
  434. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), Transform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity()));
  435. #endif // AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  436. }
  437. }
  438. TEST_F(PoseTests, ForceUpdateFullLocalSpacePose)
  439. {
  440. Pose pose;
  441. pose.LinkToActor(m_actor.get());
  442. pose.InitFromBindPose(m_actor.get());
  443. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  444. {
  445. const Transform oldLocalSpaceTransform = pose.GetLocalSpaceTransform(i);
  446. const Transform newTransform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 1) * m_testOffset)), AZ::Quaternion::CreateIdentity());
  447. // Set the local space without invalidating the model space transform.
  448. pose.SetModelSpaceTransformDirect(i, newTransform);
  449. #if AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  450. EXPECT_THAT(pose.GetModelSpaceTransformDirect(i), IsClose(newTransform));
  451. EXPECT_THAT(pose.GetLocalSpaceTransformDirect(i), IsClose(oldLocalSpaceTransform));
  452. #else
  453. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), newTransform);
  454. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), oldLocalSpaceTransform);
  455. #endif // AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  456. }
  457. // Update all local space transforms regardless of the invalidate flag.
  458. pose.ForceUpdateFullLocalSpacePose();
  459. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  460. {
  461. // Get the local space transform without auto-updating them, to see if update call worked.
  462. #if AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  463. EXPECT_THAT(pose.GetLocalSpaceTransformDirect(i), IsClose(Transform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity())));
  464. #else
  465. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), Transform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity()));
  466. #endif // AZ_TRAIT_USE_PLATFORM_SIMD_NEON
  467. }
  468. }
  469. TEST_P(PoseTestsBoolParam, UpdateModelSpaceTranforms)
  470. {
  471. Pose pose;
  472. pose.LinkToActor(m_actor.get());
  473. pose.InitFromBindPose(m_actor.get());
  474. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  475. {
  476. const Transform oldModelSpaceTransform = pose.GetModelSpaceTransform(i);
  477. const Transform newTransform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity());
  478. // Set the local space and invalidate the model space transform.
  479. pose.SetLocalSpaceTransform(i, newTransform);
  480. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), newTransform);
  481. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), oldModelSpaceTransform);
  482. }
  483. // Update all invalidated model space transforms.
  484. if (GetParam())
  485. {
  486. pose.UpdateAllModelSpaceTranforms();
  487. }
  488. else
  489. {
  490. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  491. {
  492. pose.UpdateModelSpaceTransform(i);
  493. }
  494. }
  495. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  496. {
  497. // Get the model space transform without auto-updating them, to see if the update call worked.
  498. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i),
  499. Transform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 1) * m_testOffset)), AZ::Quaternion::CreateIdentity()));
  500. }
  501. }
  502. TEST_F(PoseTests, ForceUpdateAllModelSpaceTranforms)
  503. {
  504. Pose pose;
  505. pose.LinkToActor(m_actor.get());
  506. pose.InitFromBindPose(m_actor.get());
  507. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  508. {
  509. const Transform oldModelSpaceTransform = pose.GetModelSpaceTransform(i);
  510. const Transform newTransform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity());
  511. // Set the local space without invalidating the model space transform.
  512. pose.SetLocalSpaceTransformDirect(i, newTransform);
  513. EXPECT_EQ(pose.GetLocalSpaceTransformDirect(i), newTransform);
  514. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), oldModelSpaceTransform);
  515. }
  516. // Update all model space transforms regardless of the invalidate flag.
  517. pose.ForceUpdateFullModelSpacePose();
  518. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  519. {
  520. // Get the model space transform without auto-updating them, to see if the ForceUpdateFullModelSpacePose() worked.
  521. EXPECT_EQ(pose.GetModelSpaceTransformDirect(i), Transform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 1) * m_testOffset)), AZ::Quaternion::CreateIdentity()));
  522. }
  523. }
  524. TEST_P(PoseTestsBoolParam, GetWorldSpaceTransform)
  525. {
  526. Pose pose;
  527. pose.LinkToActorInstance(m_actorInstance);
  528. pose.InitFromBindPose(m_actor.get());
  529. const Transform offsetTransform(AZ::Vector3(0.0f, 0.0f, m_testOffset), AZ::Quaternion::CreateIdentity());
  530. m_actorInstance->SetLocalSpaceTransform(offsetTransform);
  531. m_actorInstance->UpdateWorldTransform();
  532. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  533. {
  534. pose.SetLocalSpaceTransform(i, offsetTransform);
  535. const Transform expectedWorldTransform(AZ::Vector3(0.0f, 0.0f, static_cast<float>((i + 2) * m_testOffset)), AZ::Quaternion::CreateIdentity());
  536. if (GetParam())
  537. {
  538. EXPECT_EQ(pose.GetWorldSpaceTransform(i), expectedWorldTransform);
  539. }
  540. else
  541. {
  542. Transform worldTransform;
  543. pose.GetWorldSpaceTransform(i, &worldTransform);
  544. EXPECT_EQ(worldTransform, expectedWorldTransform);
  545. }
  546. }
  547. }
  548. TEST_F(PoseTests, GetMeshNodeWorldSpaceTransform)
  549. {
  550. const size_t lodLevel = 0;
  551. const size_t jointIndex = 0;
  552. Pose pose;
  553. // If there is no actor instance linked, expect the identity transform.
  554. EXPECT_EQ(pose.GetMeshNodeWorldSpaceTransform(lodLevel, jointIndex), Transform::CreateIdentity());
  555. // Link the actor instance and move it so that the model and world space transforms differ.
  556. pose.LinkToActorInstance(m_actorInstance);
  557. pose.InitFromBindPose(m_actor.get());
  558. const Transform offsetTransform(AZ::Vector3(0.0f, 0.0f, m_testOffset),
  559. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), m_testOffset));
  560. m_actorInstance->SetLocalSpaceTransform(offsetTransform);
  561. pose.SetLocalSpaceTransform(jointIndex, offsetTransform);
  562. m_actorInstance->UpdateWorldTransform();
  563. EXPECT_THAT(m_actorInstance->GetWorldSpaceTransform(), IsClose(offsetTransform));
  564. EXPECT_THAT(pose.GetLocalSpaceTransform(jointIndex), IsClose(offsetTransform));
  565. EXPECT_THAT(pose.GetModelSpaceTransform(jointIndex), IsClose(offsetTransform));
  566. const Transform expextedWorldSpaceTransform = pose.GetModelSpaceTransform(jointIndex).Multiplied(m_actorInstance->GetWorldSpaceTransform());
  567. EXPECT_THAT(pose.GetWorldSpaceTransform(jointIndex), IsClose(expextedWorldSpaceTransform));
  568. // Create a mesh and mesh defomer stack (should equal the world space transform of the joint for non-skinned meshes).
  569. Mesh* mesh = Mesh::Create();
  570. m_actor->SetMesh(lodLevel, jointIndex, mesh);
  571. EXPECT_EQ(pose.GetMeshNodeWorldSpaceTransform(lodLevel, jointIndex), pose.GetWorldSpaceTransform(jointIndex));
  572. MeshDeformerStack* meshDeformerStack = MeshDeformerStack::Create(mesh);
  573. m_actor->SetMeshDeformerStack(lodLevel, jointIndex, meshDeformerStack);
  574. EXPECT_EQ(pose.GetMeshNodeWorldSpaceTransform(lodLevel, jointIndex), pose.GetWorldSpaceTransform(jointIndex));
  575. // Add a skinning deformer and make sure they equal the actor instance's world space transform afterwards.
  576. meshDeformerStack->AddDeformer(GetSoftSkinManager().CreateDeformer(mesh));
  577. EXPECT_EQ(pose.GetMeshNodeWorldSpaceTransform(lodLevel, jointIndex), m_actorInstance->GetWorldSpaceTransform());
  578. }
  579. TEST_P(PoseTestsBoolParam, CompensateForMotionExtraction)
  580. {
  581. const size_t motionExtractionJointIndex = m_actor->GetMotionExtractionNodeIndex();
  582. ASSERT_NE(motionExtractionJointIndex, InvalidIndex)
  583. << "Motion extraction joint not set for the test actor.";
  584. Pose pose;
  585. pose.LinkToActorInstance(m_actorInstance);
  586. pose.InitFromBindPose(m_actor.get());
  587. const TransformData* transformData = m_actorInstance->GetTransformData();
  588. // Adjust the default bind pose transform for the motion extraction node in order to see if the compensation
  589. // for motion extraction actually works.
  590. Pose* bindPose = transformData->GetBindPose();
  591. const Transform bindPoseTransform(AZ::Vector3(1.0f, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity());
  592. bindPose->SetLocalSpaceTransform(motionExtractionJointIndex, bindPoseTransform);
  593. const Transform preTransform(AZ::Vector3(0.0f, 0.0f, 1.0f),
  594. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), m_testOffset));
  595. pose.SetLocalSpaceTransform(motionExtractionJointIndex, preTransform);
  596. if (GetParam())
  597. {
  598. pose.CompensateForMotionExtraction();
  599. }
  600. else
  601. {
  602. pose.CompensateForMotionExtractionDirect();
  603. }
  604. const Transform transformResult = pose.GetLocalSpaceTransform(motionExtractionJointIndex);
  605. Transform expectedResult = preTransform;
  606. ActorInstance::MotionExtractionCompensate(expectedResult, bindPoseTransform);
  607. EXPECT_THAT(transformResult, IsClose(expectedResult));
  608. }
  609. TEST_F(PoseTests, CalcTrajectoryTransform)
  610. {
  611. const size_t motionExtractionJointIndex = m_actor->GetMotionExtractionNodeIndex();
  612. ASSERT_NE(motionExtractionJointIndex, InvalidIndex)
  613. << "Motion extraction joint not set for the test actor.";
  614. Pose pose;
  615. pose.LinkToActorInstance(m_actorInstance);
  616. pose.InitFromBindPose(m_actor.get());
  617. pose.SetLocalSpaceTransform(motionExtractionJointIndex, Transform(AZ::Vector3(1.0f, 1.0f, 1.0f),
  618. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), m_testOffset)));
  619. const Transform transformResult = pose.CalcTrajectoryTransform();
  620. const Transform expectedResult = pose.GetWorldSpaceTransform(motionExtractionJointIndex).ProjectedToGroundPlane();
  621. EXPECT_THAT(transformResult, IsClose(expectedResult));
  622. EXPECT_EQ(transformResult.m_position, AZ::Vector3(1.0f, 1.0f, 0.0f));
  623. }
  624. ///////////////////////////////////////////////////////////////////////////
  625. TEST_F(PoseTests, Scaling)
  626. {
  627. EMFX_SCALECODE
  628. (
  629. Pose pose;
  630. pose.LinkToActorInstance(m_actorInstance);
  631. pose.InitFromBindPose(m_actor.get());
  632. size_t jointIndex = InvalidIndex;
  633. Node* joint = m_actor->GetSkeleton()->FindNodeAndIndexByName("joint4", jointIndex);
  634. ASSERT_NE(joint, nullptr) << "Can't find the joint named 'joint4'.";
  635. const Transform jointTransform = pose.GetWorldSpaceTransform(jointIndex);
  636. EXPECT_THAT(jointTransform.m_scale, IsClose(AZ::Vector3::CreateOne()));
  637. AZ::Vector3 scale(2.0f);
  638. m_actorInstance->SetLocalSpaceScale(scale);
  639. m_actorInstance->UpdateWorldTransform();
  640. const Transform jointTransform2 = pose.GetWorldSpaceTransform(jointIndex);
  641. EXPECT_THAT(jointTransform2.m_scale, IsClose(scale));
  642. const float distToOrigin = jointTransform.m_position.GetLength();
  643. const float distToOrigin2= jointTransform2.m_position.GetLength();
  644. EXPECT_FLOAT_EQ(distToOrigin2 / distToOrigin, 2.0f) << "Expecting the scaled joint to be twice as far from the origin as the unscaled joint.";
  645. )
  646. }
  647. ///////////////////////////////////////////////////////////////////////////
  648. class PoseTestsBlendWeightParam
  649. : public PoseTests
  650. , public ::testing::WithParamInterface<float>
  651. {
  652. };
  653. INSTANTIATE_TEST_CASE_P(PoseTests, PoseTestsBlendWeightParam, ::testing::ValuesIn({0.0f, 0.1f, 0.25f, 0.33f, 0.5f, 0.77f, 1.0f}));
  654. TEST_P(PoseTestsBlendWeightParam, Blend)
  655. {
  656. const float blendWeight = GetParam();
  657. const Pose* sourcePose = m_actorInstance->GetTransformData()->GetBindPose();
  658. // Create a destination pose and adjust the transforms.
  659. Pose destPose;
  660. destPose.LinkToActorInstance(m_actorInstance);
  661. destPose.InitFromBindPose(m_actor.get());
  662. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  663. {
  664. const float floatI = static_cast<float>(i);
  665. Transform transform(AZ::Vector3(0.0f, 0.0f, -floatI),
  666. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), floatI));
  667. EMFX_SCALECODE
  668. (
  669. transform.m_scale = AZ::Vector3(floatI, floatI, floatI);
  670. )
  671. destPose.SetLocalSpaceTransform(i, transform);
  672. }
  673. // Blend between the bind and the destination pose.
  674. Pose blendedPose;
  675. blendedPose.LinkToActorInstance(m_actorInstance);
  676. blendedPose.InitFromBindPose(m_actor.get());
  677. blendedPose.Blend(&destPose, blendWeight);
  678. // Check the blended result.
  679. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  680. {
  681. const Transform& sourceTransform = sourcePose->GetLocalSpaceTransform(i);
  682. const Transform& destTransform = destPose.GetLocalSpaceTransform(i);
  683. const Transform& transformResult = blendedPose.GetLocalSpaceTransform(i);
  684. Transform expectedResult = sourceTransform;
  685. expectedResult.Blend(destTransform, blendWeight);
  686. EXPECT_THAT(transformResult, IsClose(expectedResult));
  687. CheckIfRotationIsNormalized(destTransform.m_rotation);
  688. }
  689. }
  690. TEST_P(PoseTestsBlendWeightParam, BlendAdditiveUsingBindPose)
  691. {
  692. const float blendWeight = GetParam();
  693. const Pose* bindPose = m_actorInstance->GetTransformData()->GetBindPose();
  694. // Create a source pose and adjust the transforms.
  695. Pose sourcePose;
  696. sourcePose.LinkToActorInstance(m_actorInstance);
  697. sourcePose.InitFromBindPose(m_actor.get());
  698. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  699. {
  700. const float floatI = static_cast<float>(i);
  701. Transform transform(AZ::Vector3(floatI, 0.0f, 0.0f),
  702. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), floatI));
  703. EMFX_SCALECODE
  704. (
  705. transform.m_scale = AZ::Vector3(floatI, floatI, floatI);
  706. )
  707. sourcePose.SetLocalSpaceTransform(i, transform);
  708. }
  709. // Create a destination pose and adjust the transforms.
  710. Pose destPose;
  711. destPose.LinkToActorInstance(m_actorInstance);
  712. destPose.InitFromBindPose(m_actor.get());
  713. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  714. {
  715. const float floatI = static_cast<float>(i);
  716. Transform transform(AZ::Vector3(0.0f, 0.0f, -floatI),
  717. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(1.0f, 0.0f, 0.0f), floatI));
  718. EMFX_SCALECODE
  719. (
  720. transform.m_scale = AZ::Vector3(floatI, floatI, floatI);
  721. )
  722. destPose.SetLocalSpaceTransform(i, transform);
  723. }
  724. // Initialize our resulting pose from the source pose and additively blend it with the destination pose.
  725. Pose blendedPose;
  726. blendedPose.LinkToActorInstance(m_actorInstance);
  727. blendedPose.InitFromPose(&sourcePose);
  728. blendedPose.BlendAdditiveUsingBindPose(&destPose, blendWeight);
  729. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  730. {
  731. const Transform& bindPoseTransform = bindPose->GetLocalSpaceTransform(i);
  732. const Transform& sourceTransform = sourcePose.GetLocalSpaceTransform(i);
  733. const Transform& destTransform = destPose.GetLocalSpaceTransform(i);
  734. const Transform& transformResult = blendedPose.GetLocalSpaceTransform(i);
  735. Transform expectedResult = sourceTransform;
  736. expectedResult.BlendAdditive(destTransform, bindPoseTransform, blendWeight);
  737. EXPECT_THAT(transformResult, IsClose(expectedResult));
  738. CheckIfRotationIsNormalized(destTransform.m_rotation);
  739. }
  740. }
  741. ///////////////////////////////////////////////////////////////////////////
  742. enum PoseTestsMultiplyFunction
  743. {
  744. PreMultiply,
  745. Multiply,
  746. MultiplyInverse
  747. };
  748. class PoseTestsMultiply
  749. : public PoseTests
  750. , public ::testing::WithParamInterface<PoseTestsMultiplyFunction>
  751. {
  752. };
  753. INSTANTIATE_TEST_CASE_P(PoseTests, PoseTestsMultiply, ::testing::ValuesIn({
  754. PreMultiply, Multiply, MultiplyInverse}));
  755. TEST_P(PoseTestsMultiply, Multiply)
  756. {
  757. Pose poseA;
  758. poseA.LinkToActorInstance(m_actorInstance);
  759. poseA.InitFromBindPose(m_actor.get());
  760. Pose poseB;
  761. poseB.LinkToActorInstance(m_actorInstance);
  762. poseB.InitFromBindPose(m_actor.get());
  763. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  764. {
  765. const float floatI = static_cast<float>(i);
  766. const Transform transformA(AZ::Vector3(floatI, 0.0f, 0.0f),
  767. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(1.0f, 0.0f, 0.0f), floatI));
  768. const Transform transformB(AZ::Vector3(floatI, floatI, 0.0f),
  769. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), floatI));
  770. poseA.SetLocalSpaceTransform(i, transformA);
  771. poseB.SetLocalSpaceTransform(i, transformB);
  772. }
  773. Pose poseResult;
  774. poseResult.LinkToActorInstance(m_actorInstance);
  775. poseResult.InitFromPose(&poseA);
  776. switch (GetParam())
  777. {
  778. case PreMultiply: { poseResult.PreMultiply(poseB); break; }
  779. case Multiply: { poseResult.Multiply(poseB); break; }
  780. case MultiplyInverse: { poseResult.MultiplyInverse(poseB); break; }
  781. default: { ASSERT_TRUE(false) << "Case not handled."; }
  782. }
  783. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  784. {
  785. const Transform& transformA = poseA.GetLocalSpaceTransform(i);
  786. const Transform& transformB = poseB.GetLocalSpaceTransform(i);
  787. const Transform& transformResult = poseResult.GetLocalSpaceTransform(i);
  788. Transform expectedResult = Transform::CreateIdentity();
  789. switch (GetParam())
  790. {
  791. case PreMultiply: { expectedResult = transformA.PreMultiplied(transformB); break; }
  792. case Multiply: { expectedResult = transformA.Multiplied(transformB); break; }
  793. case MultiplyInverse: { expectedResult = transformA.PreMultiplied(transformB.Inversed()); break; }
  794. default: { ASSERT_TRUE(false) << "Case not handled."; }
  795. }
  796. EXPECT_THAT(transformResult, IsClose(expectedResult));
  797. }
  798. }
  799. ///////////////////////////////////////////////////////////////////////////
  800. class PoseTestsSum
  801. : public PoseTests
  802. , public ::testing::WithParamInterface<float>
  803. {
  804. };
  805. INSTANTIATE_TEST_CASE_P(PoseTests, PoseTestsSum, ::testing::ValuesIn({0.0f, 0.1f, 0.25f, 0.33f, 0.5f, 0.77f, 1.0f}));
  806. TEST_P(PoseTestsSum, Sum)
  807. {
  808. const float weight = GetParam();
  809. Pose poseA;
  810. poseA.LinkToActorInstance(m_actorInstance);
  811. poseA.InitFromBindPose(m_actor.get());
  812. Pose poseB;
  813. poseB.LinkToActorInstance(m_actorInstance);
  814. poseB.InitFromBindPose(m_actor.get());
  815. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  816. {
  817. const float floatI = static_cast<float>(i);
  818. const Transform transformA(AZ::Vector3(floatI, 0.0f, 0.0f), AZ::Quaternion::CreateIdentity());
  819. const Transform transformB(AZ::Vector3(floatI, floatI, 0.0f), AZ::Quaternion::CreateIdentity());
  820. poseA.SetLocalSpaceTransform(i, transformA);
  821. poseB.SetLocalSpaceTransform(i, transformB);
  822. }
  823. const size_t numMorphWeights = poseA.GetNumMorphWeights();
  824. for (size_t i = 0; i < numMorphWeights; ++i)
  825. {
  826. const float floatI = static_cast<float>(i);
  827. poseA.SetMorphWeight(i, floatI);
  828. poseB.SetMorphWeight(i, floatI);
  829. }
  830. Pose poseSum;
  831. poseSum.LinkToActorInstance(m_actorInstance);
  832. poseSum.InitFromPose(&poseA);
  833. poseSum.Sum(&poseB, weight);
  834. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  835. {
  836. const Transform& transformA = poseA.GetLocalSpaceTransform(i);
  837. const Transform& transformB = poseB.GetLocalSpaceTransform(i);
  838. const Transform& transformResult = poseSum.GetLocalSpaceTransform(i);
  839. Transform expectedResult = transformA;
  840. expectedResult.Add(transformB, weight);
  841. EXPECT_THAT(transformResult, IsClose(expectedResult));
  842. }
  843. for (size_t i = 0; i < numMorphWeights; ++i)
  844. {
  845. EXPECT_FLOAT_EQ(poseSum.GetMorphWeight(i),
  846. poseA.GetMorphWeight(i) + poseB.GetMorphWeight(i) * weight);
  847. }
  848. }
  849. ///////////////////////////////////////////////////////////////////////////
  850. TEST_F(PoseTests, MakeRelativeTo)
  851. {
  852. Pose poseA;
  853. poseA.LinkToActorInstance(m_actorInstance);
  854. poseA.InitFromBindPose(m_actor.get());
  855. Pose poseB;
  856. poseB.LinkToActorInstance(m_actorInstance);
  857. poseB.InitFromBindPose(m_actor.get());
  858. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  859. {
  860. const float floatI = static_cast<float>(i);
  861. const Transform transformA(AZ::Vector3(floatI, floatI, floatI), AZ::Quaternion::CreateIdentity());
  862. const Transform transformB(AZ::Vector3(floatI, floatI, floatI) - AZ::Vector3::CreateOne(), AZ::Quaternion::CreateIdentity());
  863. poseA.SetLocalSpaceTransform(i, transformA);
  864. poseB.SetLocalSpaceTransform(i, transformB);
  865. }
  866. Pose poseRel;
  867. poseRel.LinkToActorInstance(m_actorInstance);
  868. poseRel.InitFromPose(&poseA);
  869. poseRel.MakeRelativeTo(poseB);
  870. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  871. {
  872. const Transform& transformRel = poseRel.GetLocalSpaceTransform(i);
  873. const AZ::Vector3& result = transformRel.m_position;
  874. EXPECT_TRUE(result.IsClose(AZ::Vector3::CreateOne()));
  875. }
  876. }
  877. ///////////////////////////////////////////////////////////////////////////
  878. enum PoseTestAdditiveFunction
  879. {
  880. MakeAdditive,
  881. ApplyAdditive,
  882. ApplyAdditiveWeight
  883. };
  884. struct PoseTestAdditiveParam
  885. {
  886. bool m_linkToActorInstance;
  887. PoseTestAdditiveFunction m_additiveFunction;
  888. float m_weight;
  889. };
  890. class PoseTestsAdditive
  891. : public PoseTests
  892. , public ::testing::WithParamInterface<PoseTestAdditiveParam>
  893. {
  894. };
  895. static const std::vector<PoseTestAdditiveParam> poseTestsAdditiveData
  896. {
  897. {true, MakeAdditive, 0.0f}, {true, ApplyAdditive, 0.0f},
  898. {false, MakeAdditive, 0.0f}, {false, ApplyAdditive, 0.0f},
  899. {false, ApplyAdditiveWeight, 0.0f}, {false, ApplyAdditiveWeight, 0.25f}, {false, ApplyAdditiveWeight, 0.5f}, {false, ApplyAdditiveWeight, 1.0f},
  900. {true, ApplyAdditiveWeight, 0.0f}, {true, ApplyAdditiveWeight, 0.25f}, {true, ApplyAdditiveWeight, 0.5f}, {true, ApplyAdditiveWeight, 1.0f}
  901. };
  902. INSTANTIATE_TEST_CASE_P(PoseTests, PoseTestsAdditive, ::testing::ValuesIn(poseTestsAdditiveData));
  903. TEST_P(PoseTestsAdditive, Additive)
  904. {
  905. const bool linkToActorInstance = GetParam().m_linkToActorInstance;
  906. const int additiveFunction = GetParam().m_additiveFunction;
  907. const float weight = GetParam().m_weight;
  908. Pose poseA;
  909. if (linkToActorInstance)
  910. {
  911. poseA.LinkToActorInstance(m_actorInstance);
  912. }
  913. else
  914. {
  915. poseA.LinkToActor(m_actor.get());
  916. }
  917. poseA.InitFromBindPose(m_actor.get());
  918. Pose poseB;
  919. if (linkToActorInstance)
  920. {
  921. poseB.LinkToActorInstance(m_actorInstance);
  922. }
  923. else
  924. {
  925. poseB.LinkToActor(m_actor.get());
  926. }
  927. poseB.InitFromBindPose(m_actor.get());
  928. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  929. {
  930. const float floatI = static_cast<float>(i);
  931. const Transform transformA(AZ::Vector3(floatI, 0.0f, 0.0f),
  932. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(1.0f, 0.0f, 0.0f), floatI));
  933. const Transform transformB(AZ::Vector3(floatI, floatI, 0.0f),
  934. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(0.0f, 1.0f, 0.0f), floatI));
  935. poseA.SetLocalSpaceTransform(i, transformA);
  936. poseB.SetLocalSpaceTransform(i, transformB);
  937. }
  938. const size_t numMorphWeights = poseA.GetNumMorphWeights();
  939. for (size_t i = 0; i < numMorphWeights; ++i)
  940. {
  941. const float floatI = static_cast<float>(i);
  942. poseA.SetMorphWeight(i, floatI);
  943. poseB.SetMorphWeight(i, floatI);
  944. }
  945. Pose poseResult;
  946. if (linkToActorInstance)
  947. {
  948. poseResult.LinkToActorInstance(m_actorInstance);
  949. }
  950. else
  951. {
  952. poseResult.LinkToActor(m_actor.get());
  953. }
  954. poseResult.InitFromPose(&poseA);
  955. switch (additiveFunction)
  956. {
  957. case MakeAdditive: { poseResult.MakeAdditive(poseB); break; }
  958. case ApplyAdditive: { poseResult.ApplyAdditive(poseB); break; }
  959. case ApplyAdditiveWeight: { poseResult.ApplyAdditive(poseB, weight); break; }
  960. default: { ASSERT_TRUE(false) << "Case not handled."; }
  961. }
  962. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  963. {
  964. const Transform& transformA = poseA.GetLocalSpaceTransform(i);
  965. const Transform& transformB = poseB.GetLocalSpaceTransform(i);
  966. const Transform& transformResult = poseResult.GetLocalSpaceTransform(i);
  967. Transform expectedResult = Transform::CreateIdentity();
  968. if (additiveFunction == MakeAdditive)
  969. {
  970. expectedResult.m_position = transformA.m_position - transformB.m_position;
  971. expectedResult.m_rotation = transformB.m_rotation.GetConjugate() * transformA.m_rotation;
  972. EMFX_SCALECODE
  973. (
  974. expectedResult.m_scale = transformA.m_scale * transformB.m_scale;
  975. )
  976. }
  977. else if (additiveFunction == ApplyAdditive || weight > 1.0f - MCore::Math::epsilon)
  978. {
  979. expectedResult.m_position = transformA.m_position + transformB.m_position;
  980. expectedResult.m_rotation = transformA.m_rotation * transformB.m_rotation;
  981. expectedResult.m_rotation.Normalize();
  982. EMFX_SCALECODE
  983. (
  984. expectedResult.m_scale = transformA.m_scale * transformB.m_scale;
  985. )
  986. }
  987. else if (weight < MCore::Math::epsilon )
  988. {
  989. expectedResult = transformA;
  990. }
  991. else
  992. {
  993. expectedResult.m_position = transformA.m_position + transformB.m_position * weight;
  994. expectedResult.m_rotation = transformA.m_rotation.NLerp(transformB.m_rotation * transformA.m_rotation, weight);
  995. expectedResult.m_rotation.Normalize();
  996. EMFX_SCALECODE
  997. (
  998. expectedResult.m_scale = transformA.m_scale * AZ::Vector3::CreateOne().Lerp(transformB.m_scale, weight);
  999. )
  1000. }
  1001. EXPECT_THAT(transformResult, IsClose(expectedResult));
  1002. }
  1003. switch (additiveFunction)
  1004. {
  1005. case 0:
  1006. {
  1007. for (size_t i = 0; i < numMorphWeights; ++i)
  1008. {
  1009. EXPECT_FLOAT_EQ(poseResult.GetMorphWeight(i),
  1010. poseA.GetMorphWeight(i) - poseB.GetMorphWeight(i));
  1011. }
  1012. break;
  1013. }
  1014. case 1:
  1015. {
  1016. for (size_t i = 0; i < numMorphWeights; ++i)
  1017. {
  1018. EXPECT_FLOAT_EQ(poseResult.GetMorphWeight(i),
  1019. poseA.GetMorphWeight(i) + poseB.GetMorphWeight(i));
  1020. }
  1021. break;
  1022. }
  1023. case 2:
  1024. {
  1025. for (size_t i = 0; i < numMorphWeights; ++i)
  1026. {
  1027. EXPECT_FLOAT_EQ(poseResult.GetMorphWeight(i),
  1028. poseA.GetMorphWeight(i) + poseB.GetMorphWeight(i) * weight);
  1029. }
  1030. break;
  1031. }
  1032. default: { ASSERT_TRUE(false) << "Case not handled."; }
  1033. }
  1034. }
  1035. ///////////////////////////////////////////////////////////////////////////
  1036. TEST_F(PoseTests, Zero)
  1037. {
  1038. Pose pose;
  1039. pose.LinkToActor(m_actor.get());
  1040. pose.InitFromBindPose(m_actor.get());
  1041. pose.Zero();
  1042. // Check if local space transforms are correctly zeroed.
  1043. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  1044. {
  1045. EXPECT_EQ(pose.GetLocalSpaceTransform(i), Transform::CreateZero());
  1046. }
  1047. // Check if morph target weights are all zero.
  1048. const size_t numMorphWeights = pose.GetNumMorphWeights();
  1049. for (size_t i = 0; i < numMorphWeights; ++i)
  1050. {
  1051. EXPECT_EQ(pose.GetMorphWeight(i), 0.0f);
  1052. }
  1053. }
  1054. TEST_F(PoseTests, NormalizeQuaternions)
  1055. {
  1056. Pose pose;
  1057. pose.LinkToActor(m_actor.get());
  1058. pose.InitFromBindPose(m_actor.get());
  1059. AZ::SimpleLcgRandom random;
  1060. random.SetSeed(875960);
  1061. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  1062. {
  1063. Transform transformRandomRot(AZ::Vector3::CreateZero(),
  1064. CreateRandomUnnormalizedQuaternion(random));
  1065. pose.SetLocalSpaceTransform(i, transformRandomRot);
  1066. EXPECT_EQ(pose.GetLocalSpaceTransform(i), transformRandomRot);
  1067. }
  1068. pose.NormalizeQuaternions();
  1069. for (size_t i = 0; i < m_actor->GetSkeleton()->GetNumNodes(); ++i)
  1070. {
  1071. CheckIfRotationIsNormalized(pose.GetLocalSpaceTransform(i).m_rotation);
  1072. }
  1073. }
  1074. TEST_F(PoseTests, AssignmentOperator)
  1075. {
  1076. Pose pose;
  1077. pose.LinkToActor(m_actor.get());
  1078. pose.InitFromBindPose(m_actor.get());
  1079. Pose poseCopy;
  1080. poseCopy.LinkToActor(m_actor.get());
  1081. poseCopy = pose;
  1082. const Pose* bindPose = m_actor->GetBindPose();
  1083. ComparePoseTransforms(poseCopy, *bindPose);
  1084. CompareFlags(poseCopy, *bindPose);
  1085. CompareMorphTargets(poseCopy, *bindPose);
  1086. }
  1087. TEST_F(PoseTests, GetAndPreparePoseDataType)
  1088. {
  1089. Pose pose;
  1090. pose.LinkToActor(m_actor.get());
  1091. PoseData* poseData = pose.GetAndPreparePoseData(azrtti_typeid<PoseDataRagdoll>(), m_actorInstance);
  1092. EXPECT_NE(poseData, nullptr);
  1093. EXPECT_EQ(pose.GetPoseDatas().size(), 1);
  1094. EXPECT_EQ(poseData->RTTI_GetType(), azrtti_typeid<PoseDataRagdoll>());
  1095. EXPECT_EQ(poseData->IsUsed(), true);
  1096. }
  1097. TEST_F(PoseTests, GetAndPreparePoseDataTemplate)
  1098. {
  1099. Pose pose;
  1100. pose.LinkToActor(m_actor.get());
  1101. PoseData* poseData = pose.GetAndPreparePoseData<PoseDataRagdoll>(m_actorInstance);
  1102. EXPECT_NE(poseData, nullptr);
  1103. EXPECT_EQ(pose.GetPoseDatas().size(), 1);
  1104. EXPECT_EQ(poseData->RTTI_GetType(), azrtti_typeid<PoseDataRagdoll>());
  1105. EXPECT_EQ(poseData->IsUsed(), true);
  1106. }
  1107. TEST_F(PoseTests, GetHasPoseData)
  1108. {
  1109. Pose pose;
  1110. pose.LinkToActor(m_actor.get());
  1111. PoseData* poseData = pose.GetAndPreparePoseData(azrtti_typeid<PoseDataRagdoll>(), m_actorInstance);
  1112. EXPECT_NE(poseData, nullptr);
  1113. EXPECT_EQ(pose.GetPoseDatas().size(), 1);
  1114. EXPECT_EQ(pose.HasPoseData(azrtti_typeid<PoseDataRagdoll>()), true);
  1115. EXPECT_EQ(pose.GetPoseDataByType(azrtti_typeid<PoseDataRagdoll>()), poseData);
  1116. EXPECT_EQ(pose.GetPoseData<PoseDataRagdoll>(), poseData);
  1117. }
  1118. TEST_F(PoseTests, AddPoseData)
  1119. {
  1120. Pose pose;
  1121. pose.LinkToActor(m_actor.get());
  1122. PoseData* poseData = PoseDataFactory::Create(&pose, azrtti_typeid<PoseDataRagdoll>());
  1123. pose.AddPoseData(poseData);
  1124. EXPECT_NE(poseData, nullptr);
  1125. EXPECT_EQ(pose.GetPoseDatas().size(), 1);
  1126. EXPECT_EQ(pose.HasPoseData(azrtti_typeid<PoseDataRagdoll>()), true);
  1127. EXPECT_EQ(pose.GetPoseDataByType(azrtti_typeid<PoseDataRagdoll>()), poseData);
  1128. EXPECT_EQ(pose.GetPoseData<PoseDataRagdoll>(), poseData);
  1129. }
  1130. TEST_F(PoseTests, ClearPoseDatas)
  1131. {
  1132. Pose pose;
  1133. pose.LinkToActor(m_actor.get());
  1134. PoseData* poseData = PoseDataFactory::Create(&pose, azrtti_typeid<PoseDataRagdoll>());
  1135. pose.AddPoseData(poseData);
  1136. EXPECT_NE(poseData, nullptr);
  1137. EXPECT_EQ(pose.GetPoseDatas().size(), 1);
  1138. pose.ClearPoseDatas();
  1139. EXPECT_TRUE(pose.GetPoseDatas().empty());
  1140. }
  1141. } // namespace EMotionFX