NonUniformMotionDataTests.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/UnitTest/UnitTest.h>
  9. #include <AzCore/std/string/string.h>
  10. #include <AzCore/std/tuple.h>
  11. #include <AzCore/std/smart_ptr/unique_ptr.h>
  12. #include <AzCore/Math/Vector3.h>
  13. #include <AzCore/Math/Quaternion.h>
  14. #include <EMotionFX/Source/Actor.h>
  15. #include <EMotionFX/Source/ActorInstance.h>
  16. #include <EMotionFX/Source/MotionData/NonUniformMotionData.h>
  17. #include <EMotionFX/Source/Node.h>
  18. #include <EMotionFX/Source/Skeleton.h>
  19. #include <EMotionFX/Source/TransformData.h>
  20. #include <Tests/ActorFixture.h>
  21. #include <Tests/Matchers.h>
  22. namespace EMotionFX
  23. {
  24. class NonUniformMotionDataTests
  25. : public ActorFixture
  26. , public UnitTest::TraceBusRedirector
  27. {
  28. public:
  29. void SetUp()
  30. {
  31. UnitTest::TraceBusRedirector::BusConnect();
  32. ActorFixture::SetUp();
  33. }
  34. void TearDown()
  35. {
  36. ActorFixture::TearDown();
  37. UnitTest::TraceBusRedirector::BusDisconnect();
  38. }
  39. };
  40. TEST_F(NonUniformMotionDataTests, ZeroInit)
  41. {
  42. NonUniformMotionData motionData;
  43. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  44. EXPECT_EQ(motionData.GetNumJoints(), 0);
  45. EXPECT_EQ(motionData.GetNumMorphs(), 0);
  46. EXPECT_EQ(motionData.GetNumFloats(), 0);
  47. EXPECT_TRUE(motionData.VerifyIntegrity());
  48. }
  49. TEST_F(NonUniformMotionDataTests, InitAndDuration)
  50. {
  51. NonUniformMotionData motionData;
  52. motionData.Resize(3, 4, 5);
  53. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  54. EXPECT_EQ(motionData.GetNumJoints(), 3);
  55. EXPECT_EQ(motionData.GetNumMorphs(), 4);
  56. EXPECT_EQ(motionData.GetNumFloats(), 5);
  57. motionData.AllocateJointPositionSamples(0, 11);
  58. const size_t numSamples = motionData.GetNumJointPositionSamples(0);
  59. EXPECT_EQ(numSamples, 11);
  60. for (size_t i = 0; i < numSamples; ++i)
  61. {
  62. NonUniformMotionData::Vector3Key key;
  63. key.m_time = static_cast<float>(i);
  64. key.m_value = AZ::Vector3(static_cast<float>(i), 0.0f, 0.0f);
  65. motionData.SetJointPositionSample(0, i, key);
  66. }
  67. motionData.UpdateDuration();
  68. EXPECT_TRUE(motionData.VerifyIntegrity());
  69. EXPECT_FLOAT_EQ(motionData.GetDuration(), 10.0f);
  70. }
  71. TEST_F(NonUniformMotionDataTests, Clear)
  72. {
  73. NonUniformMotionData motionData;
  74. motionData.Resize(3, 4, 5);
  75. EXPECT_EQ(motionData.GetNumJoints(), 3);
  76. EXPECT_EQ(motionData.GetNumMorphs(), 4);
  77. EXPECT_EQ(motionData.GetNumFloats(), 5);
  78. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  79. motionData.AllocateJointPositionSamples(0, 11);
  80. const size_t numSamples = motionData.GetNumJointPositionSamples(0);
  81. EXPECT_EQ(numSamples, 11);
  82. for (size_t i = 0; i < numSamples; ++i)
  83. {
  84. NonUniformMotionData::Vector3Key key;
  85. key.m_time = static_cast<float>(i);
  86. key.m_value = AZ::Vector3(static_cast<float>(i), 0.0f, 0.0f);
  87. motionData.SetJointPositionSample(0, i, key);
  88. }
  89. motionData.UpdateDuration();
  90. EXPECT_FLOAT_EQ(motionData.GetDuration(), 10.0f);
  91. motionData.Clear();
  92. EXPECT_EQ(motionData.GetNumJoints(), 0);
  93. EXPECT_EQ(motionData.GetNumMorphs(), 0);
  94. EXPECT_EQ(motionData.GetNumFloats(), 0);
  95. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  96. }
  97. TEST_F(NonUniformMotionDataTests, FindMotionLinkData)
  98. {
  99. NonUniformMotionData motionData;
  100. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  101. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 0);
  102. AZStd::unique_ptr<Actor> clonedActor(m_actor->Clone());
  103. const MotionLinkData* linkDataA = motionData.FindMotionLinkData(m_actor.get());
  104. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 1);
  105. const MotionLinkData* linkDataB = motionData.FindMotionLinkData(m_actor.get());
  106. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 1);
  107. const MotionLinkData* linkDataC = motionData.FindMotionLinkData(const_cast<Actor*>(clonedActor.get()));
  108. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 2);
  109. const MotionLinkData* linkDataD = motionData.FindMotionLinkData(const_cast<Actor*>(clonedActor.get()));
  110. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 2);
  111. EXPECT_NE(linkDataA, nullptr);
  112. EXPECT_NE(linkDataB, nullptr);
  113. EXPECT_NE(linkDataC, nullptr);
  114. EXPECT_NE(linkDataD, nullptr);
  115. EXPECT_EQ(linkDataA, linkDataB);
  116. EXPECT_NE(linkDataA, linkDataC);
  117. EXPECT_EQ(linkDataC, linkDataD);
  118. clonedActor.reset(nullptr);
  119. EXPECT_EQ(motionData.GetNumMotionLinkCacheEntries(), 1);
  120. }
  121. TEST_F(NonUniformMotionDataTests, RemoveItems)
  122. {
  123. NonUniformMotionData motionData;
  124. motionData.Resize(4, 4, 4);
  125. ASSERT_EQ(motionData.GetNumJoints(), 4);
  126. ASSERT_EQ(motionData.GetNumMorphs(), 4);
  127. ASSERT_EQ(motionData.GetNumFloats(), 4);
  128. motionData.SetJointName(0, "Joint1");
  129. motionData.SetJointName(1, "Joint2");
  130. motionData.SetJointName(2, "Joint3");
  131. motionData.SetJointName(3, "Joint4");
  132. motionData.SetMorphName(0, "Morph1");
  133. motionData.SetMorphName(1, "Morph2");
  134. motionData.SetMorphName(2, "Morph3");
  135. motionData.SetMorphName(3, "Morph4");
  136. motionData.SetFloatName(0, "Float1");
  137. motionData.SetFloatName(1, "Float2");
  138. motionData.SetFloatName(2, "Float3");
  139. motionData.SetFloatName(3, "Float4");
  140. motionData.RemoveJoint(0);
  141. motionData.RemoveMorph(1);
  142. motionData.RemoveFloat(2);
  143. ASSERT_EQ(motionData.GetNumJoints(), 3);
  144. ASSERT_EQ(motionData.GetNumMorphs(), 3);
  145. ASSERT_EQ(motionData.GetNumFloats(), 3);
  146. EXPECT_STREQ(motionData.GetJointName(0).c_str(), "Joint2");
  147. EXPECT_STREQ(motionData.GetJointName(1).c_str(), "Joint3");
  148. EXPECT_STREQ(motionData.GetJointName(2).c_str(), "Joint4");
  149. EXPECT_STREQ(motionData.GetMorphName(0).c_str(), "Morph1");
  150. EXPECT_STREQ(motionData.GetMorphName(1).c_str(), "Morph3");
  151. EXPECT_STREQ(motionData.GetMorphName(2).c_str(), "Morph4");
  152. EXPECT_STREQ(motionData.GetFloatName(0).c_str(), "Float1");
  153. EXPECT_STREQ(motionData.GetFloatName(1).c_str(), "Float2");
  154. EXPECT_STREQ(motionData.GetFloatName(2).c_str(), "Float4");
  155. }
  156. TEST_F(NonUniformMotionDataTests, FindByName)
  157. {
  158. NonUniformMotionData motionData;
  159. motionData.Resize(3, 3, 3);
  160. ASSERT_EQ(motionData.GetNumJoints(), 3);
  161. ASSERT_EQ(motionData.GetNumMorphs(), 3);
  162. ASSERT_EQ(motionData.GetNumFloats(), 3);
  163. motionData.SetJointName(0, "Joint1");
  164. motionData.SetJointName(1, "Joint2");
  165. motionData.SetJointName(2, "Joint3");
  166. motionData.SetMorphName(0, "Morph1");
  167. motionData.SetMorphName(1, "Morph2");
  168. motionData.SetMorphName(2, "Morph3");
  169. motionData.SetFloatName(0, "Float1");
  170. motionData.SetFloatName(1, "Float2");
  171. motionData.SetFloatName(2, "Float3");
  172. EXPECT_FALSE(motionData.FindJointIndexByName("Blah").IsSuccess());
  173. EXPECT_FALSE(motionData.FindMorphIndexByName("Blah").IsSuccess());
  174. EXPECT_FALSE(motionData.FindFloatIndexByName("Blah").IsSuccess());
  175. ASSERT_TRUE(motionData.FindJointIndexByName("Joint1").IsSuccess());
  176. ASSERT_TRUE(motionData.FindJointIndexByName("Joint2").IsSuccess());
  177. ASSERT_TRUE(motionData.FindJointIndexByName("Joint3").IsSuccess());
  178. ASSERT_TRUE(motionData.FindMorphIndexByName("Morph1").IsSuccess());
  179. ASSERT_TRUE(motionData.FindMorphIndexByName("Morph2").IsSuccess());
  180. ASSERT_TRUE(motionData.FindMorphIndexByName("Morph3").IsSuccess());
  181. ASSERT_TRUE(motionData.FindFloatIndexByName("Float1").IsSuccess());
  182. ASSERT_TRUE(motionData.FindFloatIndexByName("Float2").IsSuccess());
  183. ASSERT_TRUE(motionData.FindFloatIndexByName("Float3").IsSuccess());
  184. EXPECT_EQ(motionData.FindJointIndexByName("Joint1").GetValue(), 0);
  185. EXPECT_EQ(motionData.FindJointIndexByName("Joint2").GetValue(), 1);
  186. EXPECT_EQ(motionData.FindJointIndexByName("Joint3").GetValue(), 2);
  187. EXPECT_EQ(motionData.FindMorphIndexByName("Morph1").GetValue(), 0);
  188. EXPECT_EQ(motionData.FindMorphIndexByName("Morph2").GetValue(), 1);
  189. EXPECT_EQ(motionData.FindMorphIndexByName("Morph3").GetValue(), 2);
  190. EXPECT_EQ(motionData.FindFloatIndexByName("Float1").GetValue(), 0);
  191. EXPECT_EQ(motionData.FindFloatIndexByName("Float2").GetValue(), 1);
  192. EXPECT_EQ(motionData.FindFloatIndexByName("Float3").GetValue(), 2);
  193. }
  194. TEST_F(NonUniformMotionDataTests, VerifyIntegrity)
  195. {
  196. NonUniformMotionData motionData;
  197. motionData.Resize(3, 4, 5);
  198. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  199. EXPECT_EQ(motionData.GetNumJoints(), 3);
  200. EXPECT_EQ(motionData.GetNumMorphs(), 4);
  201. EXPECT_EQ(motionData.GetNumFloats(), 5);
  202. // Fill a key track for the first joint, with a duration of 10 seconds.
  203. motionData.AllocateJointPositionSamples(0, 11);
  204. const size_t numSamples = motionData.GetNumJointPositionSamples(0);
  205. EXPECT_EQ(numSamples, 11);
  206. for (size_t i = 0; i < numSamples; ++i)
  207. {
  208. NonUniformMotionData::Vector3Key key;
  209. key.m_time = static_cast<float>(i);
  210. key.m_value = AZ::Vector3(static_cast<float>(i), 0.0f, 0.0f);
  211. motionData.SetJointPositionSample(0, i, key);
  212. }
  213. motionData.UpdateDuration();
  214. EXPECT_TRUE(motionData.VerifyIntegrity());
  215. EXPECT_FLOAT_EQ(motionData.GetDuration(), 10.0f);
  216. // Make a morph track and make it go to 20 seconds.
  217. // This is invalid as this would cause a mismatch in duration of all key tracks (all have to end at the same time).
  218. motionData.AllocateMorphSamples(1, numSamples);
  219. for (size_t i = 0; i < numSamples; ++i)
  220. {
  221. NonUniformMotionData::FloatKey key;
  222. key.m_time = static_cast<float>(i) * 2.0f;
  223. key.m_value = static_cast<float>(i);
  224. motionData.SetMorphSample(1, i, key);
  225. }
  226. EXPECT_FALSE(motionData.VerifyIntegrity());
  227. // Fix the track again by adjusting the time values so the duration of the track is the same as the other joint's track duration.
  228. for (size_t i = 0; i < numSamples; ++i)
  229. {
  230. NonUniformMotionData::FloatKey key;
  231. key.m_time = static_cast<float>(i);
  232. motionData.SetMorphSample(1, i, key);
  233. }
  234. EXPECT_TRUE(motionData.VerifyIntegrity());
  235. motionData.UpdateDuration();
  236. EXPECT_FLOAT_EQ(motionData.GetDuration(), 10.0f);
  237. // Make one of the samples time happen before the previous sample's time.
  238. // The key track time values are no sorted in ascending order anymore, which is not valid.
  239. motionData.SetMorphSample(1, 4, { 0.0f, 0.0f });
  240. EXPECT_FALSE(motionData.VerifyIntegrity());
  241. }
  242. TEST_F(NonUniformMotionDataTests, ReduceKeysFloats)
  243. {
  244. NonUniformMotionData motionData;
  245. motionData.Resize(0, 0, 1);
  246. // All key values are the same, so a flat signal.
  247. motionData.AllocateFloatSamples(0, 11);
  248. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  249. {
  250. const float iFloat = static_cast<float>(i);
  251. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  252. }
  253. NonUniformMotionData::ReduceSettings reduceSettings;
  254. size_t numRemoved = motionData.ReduceSamples(reduceSettings);
  255. EXPECT_EQ(motionData.GetNumFloatSamples(0), 0);
  256. EXPECT_EQ(numRemoved, 11);
  257. // All key values are the same, so a flat signal, but not the same as the pose value.
  258. motionData.AllocateFloatSamples(0, 11);
  259. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  260. {
  261. const float iFloat = static_cast<float>(i);
  262. motionData.SetFloatSample(0, i, { iFloat, 1.0f });
  263. }
  264. numRemoved = motionData.ReduceSamples(reduceSettings);
  265. EXPECT_EQ(motionData.GetNumFloatSamples(0), 2);
  266. EXPECT_EQ(numRemoved, 9);
  267. /*
  268. Set the sample in the middle to 1.0 and the rest to 0.
  269. /\
  270. / \
  271. ---------------------/ \---------------------
  272. */
  273. motionData.AllocateFloatSamples(0, 11);
  274. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  275. {
  276. const float iFloat = static_cast<float>(i);
  277. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  278. }
  279. motionData.SetFloatSample(0, 5, { 5.0f, 1.0f });
  280. numRemoved = motionData.ReduceSamples(reduceSettings);
  281. EXPECT_EQ(motionData.GetNumFloatSamples(0), 5);
  282. EXPECT_EQ(numRemoved, 6);
  283. /*
  284. Make a bump of 2 frames.
  285. /------\
  286. / \
  287. ---------------------/ \---------------
  288. */
  289. motionData.AllocateFloatSamples(0, 11);
  290. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  291. {
  292. const float iFloat = static_cast<float>(i);
  293. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  294. }
  295. motionData.SetFloatSample(0, 5, { 5.0f, 1.0f });
  296. motionData.SetFloatSample(0, 6, { 6.0f, 1.0f });
  297. numRemoved = motionData.ReduceSamples(reduceSettings);
  298. EXPECT_EQ(motionData.GetNumFloatSamples(0), 6);
  299. EXPECT_EQ(numRemoved, 5);
  300. // Switch from 0 to 1 in the middle.
  301. //
  302. // /------------------------
  303. // /
  304. // ---------------------/
  305. motionData.AllocateFloatSamples(0, 11);
  306. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  307. {
  308. const float iFloat = static_cast<float>(i);
  309. motionData.SetFloatSample(0, i, { iFloat, i <= 5 ? 0.0f : 1.0f });
  310. }
  311. numRemoved = motionData.ReduceSamples(reduceSettings);
  312. EXPECT_EQ(motionData.GetNumFloatSamples(0), 4);
  313. EXPECT_EQ(numRemoved, 7);
  314. // Have just two samples with flat signal.
  315. // Make sure it removes those too.
  316. motionData.AllocateFloatSamples(0, 2);
  317. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  318. {
  319. const float iFloat = static_cast<float>(i);
  320. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  321. }
  322. numRemoved = motionData.ReduceSamples(reduceSettings);
  323. EXPECT_EQ(motionData.GetNumFloatSamples(0), 0);
  324. EXPECT_EQ(numRemoved, 2);
  325. // Set the sample in the middle to 0.001 and the rest to 0.
  326. // Use a threshold that will force this to get removed.
  327. //
  328. // ---------------------/\-------------------------
  329. motionData.AllocateFloatSamples(0, 11);
  330. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  331. {
  332. const float iFloat = static_cast<float>(i);
  333. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  334. }
  335. motionData.SetFloatSample(0, 5, { 5.0f, 0.001f });
  336. reduceSettings.m_maxFloatError = 0.01f;
  337. numRemoved = motionData.ReduceSamples(reduceSettings);
  338. EXPECT_EQ(motionData.GetNumFloatSamples(0), 0);
  339. EXPECT_EQ(numRemoved, 11);
  340. // Set the sample in the middle to 0.001 and the rest to 0.
  341. // Use a threshold that not remove this key.
  342. //
  343. // ---------------------/\-------------------------
  344. motionData.AllocateFloatSamples(0, 11);
  345. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  346. {
  347. const float iFloat = static_cast<float>(i);
  348. motionData.SetFloatSample(0, i, { iFloat, 0.0f });
  349. }
  350. motionData.SetFloatSample(0, 5, { 5.0f, 0.001f });
  351. reduceSettings.m_maxFloatError = 0.0001f;
  352. numRemoved = motionData.ReduceSamples(reduceSettings);
  353. EXPECT_EQ(motionData.GetNumFloatSamples(0), 5);
  354. EXPECT_EQ(numRemoved, 6);
  355. }
  356. TEST_F(NonUniformMotionDataTests, ReduceKeysQuaternion)
  357. {
  358. const AZ::Quaternion rotatedQuat = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(1.0f, 0.0f, 0.0f), AZ::DegToRad(5.0f));
  359. NonUniformMotionData motionData;
  360. motionData.Resize(1, 0, 0);
  361. // All key values are the same, so a flat signal.
  362. motionData.AllocateJointRotationSamples(0, 11);
  363. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  364. {
  365. const float iFloat = static_cast<float>(i);
  366. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  367. }
  368. NonUniformMotionData::ReduceSettings reduceSettings;
  369. size_t numRemoved = motionData.ReduceSamples(reduceSettings);
  370. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 0);
  371. EXPECT_EQ(numRemoved, 11);
  372. /*
  373. Set the sample in the middle to 1.0 and the rest to 0.
  374. /\
  375. / \
  376. ---------------------/ \---------------------
  377. */
  378. motionData.AllocateJointRotationSamples(0, 11);
  379. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  380. {
  381. const float iFloat = static_cast<float>(i);
  382. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  383. }
  384. motionData.SetJointRotationSample(0, 5, { 5.0f, rotatedQuat });
  385. numRemoved = motionData.ReduceSamples(reduceSettings);
  386. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 5);
  387. EXPECT_EQ(numRemoved, 6);
  388. /*
  389. Make a bump of 2 frames.
  390. /------\
  391. / \
  392. ---------------------/ \---------------
  393. */
  394. motionData.AllocateJointRotationSamples(0, 11);
  395. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  396. {
  397. const float iFloat = static_cast<float>(i);
  398. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  399. }
  400. motionData.SetJointRotationSample(0, 5, { 5.0f, rotatedQuat });
  401. motionData.SetJointRotationSample(0, 6, { 6.0f, rotatedQuat });
  402. numRemoved = motionData.ReduceSamples(reduceSettings);
  403. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 6);
  404. EXPECT_EQ(numRemoved, 5);
  405. // Switch from 0 to 1 in the middle.
  406. //
  407. // /------------------------
  408. // /
  409. // ---------------------/
  410. motionData.AllocateJointRotationSamples(0, 11);
  411. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  412. {
  413. const float iFloat = static_cast<float>(i);
  414. const AZ::Quaternion rot = (i <= 5) ? AZ::Quaternion::CreateIdentity() : rotatedQuat;
  415. motionData.SetJointRotationSample(0, i, { iFloat, rot });
  416. }
  417. numRemoved = motionData.ReduceSamples(reduceSettings);
  418. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 4);
  419. EXPECT_EQ(numRemoved, 7);
  420. // Have just two samples with flat signal.
  421. // Make sure it will remove all keys.
  422. motionData.AllocateJointRotationSamples(0, 2);
  423. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  424. {
  425. const float iFloat = static_cast<float>(i);
  426. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  427. }
  428. numRemoved = motionData.ReduceSamples(reduceSettings);
  429. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 0);
  430. EXPECT_EQ(numRemoved, 2);
  431. // Set the sample in the middle to 0.001 and the rest to 0.
  432. // Use a threshold that will force this to get removed.
  433. //
  434. // ---------------------/\-------------------------
  435. motionData.AllocateJointRotationSamples(0, 11);
  436. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  437. {
  438. const float iFloat = static_cast<float>(i);
  439. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  440. }
  441. const AZ::Quaternion tinyQuat = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3(1.0f, 0.0f, 0.0f), AZ::DegToRad(0.001f));
  442. motionData.SetJointRotationSample(0, 5, { 5.0f, tinyQuat });
  443. numRemoved = motionData.ReduceSamples(reduceSettings);
  444. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 0);
  445. EXPECT_EQ(numRemoved, 11);
  446. // Set the sample in the middle to 0.001 and the rest to 0.
  447. // Use a threshold that not remove this key.
  448. //
  449. // ---------------------/\-------------------------
  450. motionData.AllocateJointRotationSamples(0, 11);
  451. for (size_t i = 0; i < motionData.GetNumJointRotationSamples(0); ++i)
  452. {
  453. const float iFloat = static_cast<float>(i);
  454. motionData.SetJointRotationSample(0, i, { iFloat, AZ::Quaternion::CreateIdentity() });
  455. }
  456. motionData.SetJointRotationSample(0, 5, { 5.0f, tinyQuat });
  457. reduceSettings.m_maxRotError = 0.0001f;
  458. numRemoved = motionData.ReduceSamples(reduceSettings);
  459. EXPECT_EQ(motionData.GetNumJointRotationSamples(0), 5);
  460. EXPECT_EQ(numRemoved, 6);
  461. }
  462. TEST_F(NonUniformMotionDataTests, ReduceKeysVector3)
  463. {
  464. NonUniformMotionData motionData;
  465. motionData.Resize(1, 0, 0);
  466. // All key values are the same, so a flat signal.
  467. motionData.AllocateJointPositionSamples(0, 11);
  468. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  469. {
  470. const float iFloat = static_cast<float>(i);
  471. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  472. }
  473. NonUniformMotionData::ReduceSettings reduceSettings;
  474. size_t numRemoved = motionData.ReduceSamples(reduceSettings);
  475. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 0);
  476. EXPECT_EQ(numRemoved, 11);
  477. /*
  478. Set the sample in the middle to 1.0 and the rest to 0.
  479. /\
  480. / \
  481. ---------------------/ \---------------------
  482. */
  483. motionData.AllocateJointPositionSamples(0, 11);
  484. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  485. {
  486. const float iFloat = static_cast<float>(i);
  487. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  488. }
  489. motionData.SetJointPositionSample(0, 5, { 5.0f, AZ::Vector3::CreateOne() });
  490. numRemoved = motionData.ReduceSamples(reduceSettings);
  491. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 5);
  492. EXPECT_EQ(numRemoved, 6);
  493. /*
  494. Make a bump of 2 frames.
  495. /------\
  496. / \
  497. ---------------------/ \---------------
  498. */
  499. motionData.AllocateJointPositionSamples(0, 11);
  500. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  501. {
  502. const float iFloat = static_cast<float>(i);
  503. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  504. }
  505. motionData.SetJointPositionSample(0, 5, { 5.0f, AZ::Vector3::CreateOne() });
  506. motionData.SetJointPositionSample(0, 6, { 6.0f, AZ::Vector3::CreateOne() });
  507. numRemoved = motionData.ReduceSamples(reduceSettings);
  508. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 6);
  509. EXPECT_EQ(numRemoved, 5);
  510. // Switch from 0 to 1 in the middle.
  511. //
  512. // /------------------------
  513. // /
  514. // ---------------------/
  515. motionData.AllocateJointPositionSamples(0, 11);
  516. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  517. {
  518. const float iFloat = static_cast<float>(i);
  519. const AZ::Vector3 pos((i <= 5) ? 0.0f : 1.0f, 0.0f, 0.0f);
  520. motionData.SetJointPositionSample(0, i, { iFloat, pos });
  521. }
  522. numRemoved = motionData.ReduceSamples(reduceSettings);
  523. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 4);
  524. EXPECT_EQ(numRemoved, 7);
  525. // Have just two samples with flat signal.
  526. // Make sure it removes those keys.
  527. motionData.AllocateJointPositionSamples(0, 2);
  528. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  529. {
  530. const float iFloat = static_cast<float>(i);
  531. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  532. }
  533. numRemoved = motionData.ReduceSamples(reduceSettings);
  534. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 0);
  535. EXPECT_EQ(numRemoved, 2);
  536. // Set the sample in the middle to 0.001 and the rest to 0.
  537. // Use a threshold that will force this to get removed.
  538. //
  539. // ---------------------/\-------------------------
  540. motionData.AllocateJointPositionSamples(0, 11);
  541. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  542. {
  543. const float iFloat = static_cast<float>(i);
  544. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  545. }
  546. motionData.SetJointPositionSample(0, 5, { 5.0f, AZ::Vector3(0.001f, 0.0f, 0.0f) });
  547. reduceSettings.m_maxPosError = 0.01f;
  548. numRemoved = motionData.ReduceSamples(reduceSettings);
  549. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 0);
  550. EXPECT_EQ(numRemoved, 11);
  551. // Set the sample in the middle to 0.001 and the rest to 0.
  552. // Use a threshold that not remove this key.
  553. //
  554. // ---------------------/\-------------------------
  555. motionData.AllocateJointPositionSamples(0, 11);
  556. for (size_t i = 0; i < motionData.GetNumJointPositionSamples(0); ++i)
  557. {
  558. const float iFloat = static_cast<float>(i);
  559. motionData.SetJointPositionSample(0, i, { iFloat, AZ::Vector3::CreateZero() });
  560. }
  561. motionData.SetJointPositionSample(0, 5, { 5.0f, AZ::Vector3::CreateOne() });
  562. reduceSettings.m_maxPosError = 0.0001f;
  563. numRemoved = motionData.ReduceSamples(reduceSettings);
  564. EXPECT_EQ(motionData.GetNumJointPositionSamples(0), 5);
  565. EXPECT_EQ(numRemoved, 6);
  566. }
  567. TEST_F(NonUniformMotionDataTests, MainTest)
  568. {
  569. NonUniformMotionData motionData;
  570. motionData.Resize(3, 3, 3);
  571. EXPECT_FLOAT_EQ(motionData.GetDuration(), 0.0f);
  572. EXPECT_EQ(motionData.GetNumJoints(), 3);
  573. EXPECT_EQ(motionData.GetNumMorphs(), 3);
  574. EXPECT_EQ(motionData.GetNumFloats(), 3);
  575. motionData.SetJointName(0, "Joint1");
  576. motionData.SetJointName(1, "Joint2");
  577. motionData.SetJointName(2, "Joint3");
  578. motionData.SetMorphName(0, "Morph1");
  579. motionData.SetMorphName(1, "Morph2");
  580. motionData.SetMorphName(2, "Morph3");
  581. motionData.SetFloatName(0, "Float1");
  582. motionData.SetFloatName(1, "Float2");
  583. motionData.SetFloatName(2, "Float3");
  584. for (size_t i = 0; i < 3; ++i)
  585. {
  586. const float iFloat = static_cast<float>(i);
  587. motionData.SetMorphPoseValue(i, iFloat);
  588. motionData.SetFloatPoseValue(i, iFloat);
  589. EXPECT_FLOAT_EQ(motionData.GetMorphPoseValue(i), iFloat);
  590. EXPECT_FLOAT_EQ(motionData.GetFloatPoseValue(i), iFloat);
  591. }
  592. const size_t numSamples = 301;
  593. motionData.AllocateMorphSamples(0, numSamples);
  594. EXPECT_EQ(motionData.GetNumMorphSamples(0), numSamples);
  595. for (size_t i = 0; i < motionData.GetNumMorphSamples(0); ++i)
  596. {
  597. const float iFloat = static_cast<float>(i);
  598. motionData.SetMorphSample(0, i, { iFloat, iFloat * 10.0f });
  599. EXPECT_FLOAT_EQ(motionData.GetMorphSample(0, i).m_time, iFloat);
  600. EXPECT_FLOAT_EQ(motionData.GetMorphSample(0, i).m_value, iFloat * 10.0f);
  601. }
  602. motionData.AllocateFloatSamples(0, 601);
  603. EXPECT_EQ(motionData.GetNumFloatSamples(0), 601);
  604. for (size_t i = 0; i < motionData.GetNumFloatSamples(0); ++i)
  605. {
  606. const float iFloat = static_cast<float>(i);
  607. motionData.SetFloatSample(0, i, { iFloat * 0.5f, iFloat * 5.0f });
  608. EXPECT_FLOAT_EQ(motionData.GetFloatSample(0, i).m_time, iFloat * 0.5f);
  609. EXPECT_FLOAT_EQ(motionData.GetFloatSample(0, i).m_value, iFloat * 5.0f);
  610. }
  611. motionData.UpdateDuration();
  612. EXPECT_FLOAT_EQ(motionData.GetDuration(), 300.0f);
  613. EXPECT_TRUE(motionData.VerifyIntegrity());
  614. EXPECT_TRUE(motionData.IsMorphAnimated(0));
  615. EXPECT_TRUE(motionData.IsFloatAnimated(0));
  616. EXPECT_FALSE(motionData.IsMorphAnimated(1));
  617. EXPECT_FALSE(motionData.IsFloatAnimated(1));
  618. EXPECT_FALSE(motionData.IsMorphAnimated(2));
  619. EXPECT_FALSE(motionData.IsFloatAnimated(2));
  620. // Test morph sampling.
  621. AZ::Outcome<size_t> index = motionData.FindMorphIndexByName("Morph1");
  622. ASSERT_TRUE(index.IsSuccess());
  623. if (index.IsSuccess())
  624. {
  625. float result = -1.0f;
  626. const AZ::u32 id = motionData.GetMorphNameId(index.GetValue());
  627. using ValuePair = AZStd::pair<float, float>; // First = time, second = expectedValue.
  628. const AZStd::vector<ValuePair> values{
  629. { -1.0f, 0.0f },
  630. { 0.0f, 0.0f },
  631. { 0.25f, 2.5f },
  632. { 1.0f, 10.0f },
  633. { 2.75f, 27.5f },
  634. { motionData.GetDuration(), 3000.0f },
  635. { motionData.GetDuration() + 10.0f, 3000.0f }
  636. };
  637. for (const ValuePair& valuePair : values)
  638. {
  639. MotionData::SampleSettings sampleSettings;
  640. sampleSettings.m_sampleTime = valuePair.first;
  641. sampleSettings.m_actorInstance = m_actorInstance;
  642. const bool success = motionData.SampleMorph(sampleSettings, id, result);
  643. EXPECT_TRUE(success);
  644. EXPECT_FLOAT_EQ(result, valuePair.second);
  645. }
  646. }
  647. // Test float sampling.
  648. index = motionData.FindFloatIndexByName("Float1");
  649. ASSERT_TRUE(index.IsSuccess());
  650. if (index.IsSuccess())
  651. {
  652. float result = -1.0f;
  653. const AZ::u32 id = motionData.GetFloatNameId(index.GetValue());
  654. using ValuePair = AZStd::pair<float, float>; // First = time, second = expectedValue.
  655. const AZStd::vector<ValuePair> values{
  656. { -1.0f, 0.0f },
  657. { 0.0f, 0.0f },
  658. { 0.25f, 2.5f },
  659. { 1.0f, 10.0f },
  660. { 2.75f, 27.5f },
  661. { motionData.GetDuration(), 3000.0f },
  662. { motionData.GetDuration() + 10.0f, 3000.0f }
  663. };
  664. for (const ValuePair& valuePair : values)
  665. {
  666. MotionData::SampleSettings sampleSettings;
  667. sampleSettings.m_sampleTime = valuePair.first;
  668. sampleSettings.m_actorInstance = m_actorInstance;
  669. const bool success = motionData.SampleFloat(sampleSettings, id, result);
  670. EXPECT_TRUE(success);
  671. EXPECT_FLOAT_EQ(result, valuePair.second);
  672. }
  673. }
  674. // Test sampling morphs and floats without any animation data.
  675. MotionData::SampleSettings sampleSettings;
  676. sampleSettings.m_actorInstance = m_actorInstance;
  677. sampleSettings.m_sampleTime = motionData.GetDuration() / 2.0f;
  678. float sampleResult = motionData.SampleFloat(sampleSettings, 1);
  679. EXPECT_FLOAT_EQ(sampleResult, 1.0f);
  680. sampleResult = motionData.SampleFloat(sampleSettings, 2);
  681. EXPECT_FLOAT_EQ(sampleResult, 2.0f);
  682. sampleResult = motionData.SampleMorph(sampleSettings, 1);
  683. EXPECT_FLOAT_EQ(sampleResult, 1.0f);
  684. sampleResult = motionData.SampleMorph(sampleSettings, 2);
  685. EXPECT_FLOAT_EQ(sampleResult, 2.0f);
  686. // Test adding a joint.
  687. AZ::Quaternion q1, q2;
  688. q1.SetFromEulerDegrees(AZ::Vector3(0.1f, 0.2f, 0.3f));
  689. q2.SetFromEulerDegrees(AZ::Vector3(0.4f, 0.5f, 0.6f));
  690. const Transform poseTransform(AZ::Vector3(1.0f, 2.0f, 3.0f), q1, AZ::Vector3(1.0f, 2.0f, 3.0f));
  691. const Transform bindTransform(AZ::Vector3(4.0f, 5.0f, 6.0f), q2, AZ::Vector3(4.0f, 5.0f, 6.0f));
  692. const size_t jointIndex = motionData.AddJoint("Joint4", poseTransform, bindTransform);
  693. EXPECT_EQ(jointIndex, 3);
  694. EXPECT_FALSE(motionData.IsJointAnimated(3));
  695. EXPECT_STREQ(motionData.GetJointName(3).c_str(), "Joint4");
  696. EXPECT_THAT(motionData.GetJointPoseTransform(3).m_position, IsClose(poseTransform.m_position));
  697. EXPECT_THAT(motionData.GetJointPoseTransform(3).m_rotation, IsClose(poseTransform.m_rotation));
  698. #ifndef EMFX_SCALE_DISABLED
  699. EXPECT_THAT(motionData.GetJointPoseTransform(3).m_scale, IsClose(poseTransform.m_scale));
  700. #endif
  701. EXPECT_THAT(motionData.GetJointBindPoseTransform(3).m_position, IsClose(bindTransform.m_position));
  702. EXPECT_THAT(motionData.GetJointBindPoseTransform(3).m_rotation, IsClose(bindTransform.m_rotation));
  703. #ifndef EMFX_SCALE_DISABLED
  704. EXPECT_THAT(motionData.GetJointBindPoseTransform(3).m_scale, IsClose(bindTransform.m_scale));
  705. #endif
  706. // Test adding a morph.
  707. const size_t morphIndex = motionData.AddMorph("Morph4", 1.0f);
  708. EXPECT_EQ(morphIndex, 3);
  709. EXPECT_FALSE(motionData.IsMorphAnimated(3));
  710. EXPECT_STREQ(motionData.GetMorphName(3).c_str(), "Morph4");
  711. EXPECT_FLOAT_EQ(motionData.GetMorphPoseValue(3), 1.0f);
  712. // Test adding a float.
  713. const size_t floatIndex = motionData.AddFloat("Float4", 1.0f);
  714. EXPECT_EQ(floatIndex, 3);
  715. EXPECT_FALSE(motionData.IsFloatAnimated(3));
  716. EXPECT_STREQ(motionData.GetFloatName(3).c_str(), "Float4");
  717. EXPECT_FLOAT_EQ(motionData.GetFloatPoseValue(3), 1.0f);
  718. // Construct some transform tracks.
  719. EXPECT_FALSE(motionData.IsJointAnimated(0));
  720. EXPECT_FALSE(motionData.IsJointPositionAnimated(0));
  721. EXPECT_FALSE(motionData.IsJointRotationAnimated(0));
  722. #ifndef EMFX_SCALE_DISABLED
  723. EXPECT_FALSE(motionData.IsJointScaleAnimated(0));
  724. #endif
  725. motionData.AllocateJointPositionSamples(0, numSamples);
  726. motionData.AllocateJointRotationSamples(0, numSamples);
  727. EXPECT_TRUE(motionData.IsJointPositionAnimated(0));
  728. EXPECT_TRUE(motionData.IsJointRotationAnimated(0));
  729. #ifndef EMFX_SCALE_DISABLED
  730. EXPECT_FALSE(motionData.IsJointScaleAnimated(0));
  731. motionData.AllocateJointScaleSamples(0, numSamples);
  732. EXPECT_TRUE(motionData.IsJointScaleAnimated(0));
  733. #endif
  734. // Set the values for the transform samples.
  735. for (size_t i = 0; i < numSamples; ++i)
  736. {
  737. const float iFloat = static_cast<float>(i);
  738. const AZ::Vector3 position(iFloat, 1.0f, 2.0f);
  739. motionData.SetJointPositionSample(0, i, { iFloat, position });
  740. EXPECT_THAT(motionData.GetJointPositionSample(0, i).m_value, IsClose(position));
  741. EXPECT_NEAR(motionData.GetJointPositionSample(0, i).m_time, iFloat, 0.00001f);
  742. const AZ::Quaternion rotation = AZ::Quaternion::CreateRotationZ((iFloat / static_cast<float>(numSamples)) * 180.0f).GetNormalized();
  743. motionData.SetJointRotationSample(0, i, { iFloat, rotation });
  744. EXPECT_THAT(motionData.GetJointRotationSample(0, i).m_value, IsClose(rotation));
  745. EXPECT_NEAR(motionData.GetJointRotationSample(0, i).m_time, iFloat, 0.00001f);
  746. #ifndef EMFX_SCALE_DISABLED
  747. const AZ::Vector3 scale(iFloat + 1.0f, iFloat * 2.0f + 1.0f, iFloat * 3.0f + 1.0f);
  748. motionData.SetJointScaleSample(0, i, { iFloat, scale });
  749. EXPECT_THAT(motionData.GetJointScaleSample(0, i).m_value, IsClose(scale));
  750. EXPECT_NEAR(motionData.GetJointScaleSample(0, i).m_time, iFloat, 0.00001f);
  751. #endif
  752. }
  753. motionData.UpdateDuration();
  754. EXPECT_TRUE(motionData.VerifyIntegrity());
  755. // Rename our sub motion data to match our actor.
  756. const Skeleton* skeleton = m_actor->GetSkeleton();
  757. for (size_t i = 0; i < 3; ++i)
  758. {
  759. motionData.SetJointName(i, skeleton->GetNode(static_cast<AZ::u32>(i))->GetNameString());
  760. EXPECT_STREQ(motionData.GetJointName(i).c_str(), skeleton->GetNode(static_cast<AZ::u32>(i))->GetName());
  761. }
  762. // Adjust bind pose of our fourth joint.
  763. Pose* bindPose = m_actorInstance->GetTransformData()->GetBindPose();
  764. #ifndef EMFX_SCALE_DISABLED
  765. const Transform expectedBindTransform(AZ::Vector3(0.0f, 1.0f, 2.0f), AZ::Quaternion::CreateIdentity(), AZ::Vector3(10.0f, 20.0f, 30.0f));
  766. #else
  767. const Transform expectedBindTransform(AZ::Vector3(0.0f, 1.0f, 2.0f), AZ::Quaternion::CreateIdentity());
  768. #endif
  769. bindPose->SetLocalSpaceTransform(3, expectedBindTransform);
  770. // Now sample the joint transforms.
  771. const size_t lastSampleIndex = numSamples - 1;
  772. #ifndef EMFX_SCALE_DISABLED
  773. const AZ::Vector3 firstScaleSample = motionData.GetJointScaleSample(0, 0).m_value;
  774. const AZ::Vector3 lastScaleSample = motionData.GetJointScaleSample(0, lastSampleIndex).m_value;
  775. #else
  776. const AZ::Vector3 firstScaleSample(1.0f, 1.0f, 1.0f);
  777. const AZ::Vector3 lastScaleSample(1.0f, 1.0f, 1.0f);
  778. #endif
  779. using TransformPair = AZStd::pair<float, Transform>; // First = time, second = expectedValue.
  780. const AZStd::vector<TransformPair> values{
  781. { -1.0f,
  782. Transform(
  783. motionData.GetJointPositionSample(0, 0).m_value,
  784. motionData.GetJointRotationSample(0, 0).m_value,
  785. firstScaleSample) },
  786. { 0.0f,
  787. Transform(
  788. motionData.GetJointPositionSample(0, 0).m_value,
  789. motionData.GetJointRotationSample(0, 0).m_value,
  790. firstScaleSample) },
  791. { 0.25f,
  792. Transform(
  793. AZ::Vector3(0.25f, 1.0f, 2.0f),
  794. AZ::Quaternion::CreateRotationZ((0.25f / static_cast<float>(numSamples)) * 180.0f).GetNormalized(),
  795. AZ::Vector3(1.25f, 1.5f, 1.75f)) },
  796. { 0.5f,
  797. Transform(
  798. AZ::Vector3(0.5f, 1.0f, 2.0f),
  799. AZ::Quaternion::CreateRotationZ((0.5f / static_cast<float>(numSamples)) * 180.0f).GetNormalized(),
  800. AZ::Vector3(1.5f, 2.0f, 2.5f)) },
  801. { 0.75f,
  802. Transform(
  803. AZ::Vector3(0.75f, 1.0f, 2.0f),
  804. AZ::Quaternion::CreateRotationZ((0.75f / static_cast<float>(numSamples)) * 180.0f).GetNormalized(),
  805. AZ::Vector3(1.75f, 2.5f, 3.25f)) },
  806. { 1.0f,
  807. Transform(
  808. AZ::Vector3(1.0f, 1.0f, 2.0f),
  809. AZ::Quaternion::CreateRotationZ((1.0f / static_cast<float>(numSamples)) * 180.0f).GetNormalized(),
  810. AZ::Vector3(2.0f, 3.0f, 4.0f)) },
  811. { 5.5f,
  812. Transform(
  813. AZ::Vector3(5.5f, 1.0f, 2.0f),
  814. AZ::Quaternion::CreateRotationZ((5.5f / static_cast<float>(numSamples)) * 180.0f).GetNormalized(),
  815. AZ::Vector3(6.5f, 12.0f, 17.5f)) },
  816. { motionData.GetDuration() + 1.0f,
  817. Transform(
  818. motionData.GetJointPositionSample(0, lastSampleIndex).m_value,
  819. motionData.GetJointRotationSample(0, lastSampleIndex).m_value,
  820. lastScaleSample) }
  821. };
  822. for (const TransformPair& expectation : values)
  823. {
  824. MotionData::SampleSettings sampleSettings;
  825. sampleSettings.m_actorInstance = m_actorInstance;
  826. sampleSettings.m_sampleTime = expectation.first;
  827. const Transform sampledResult = motionData.SampleJointTransform(sampleSettings, 0);
  828. EXPECT_THAT(sampledResult.m_position, IsClose(expectation.second.m_position));
  829. EXPECT_THAT(sampledResult.m_rotation, IsClose(expectation.second.m_rotation));
  830. #ifndef EMFX_SCALE_DISABLED
  831. EXPECT_THAT(sampledResult.m_scale, IsClose(expectation.second.m_scale));
  832. #endif
  833. // Fourth joint has no motion data to apply to our actor, so expect a bind pose.
  834. // It has motion data, but there is no joint in the skeleton that matches its name, so it is like motion data for a joint that doesn't exist in our actor.
  835. const Transform fourthJointTransform = motionData.SampleJointTransform(sampleSettings, 3);
  836. EXPECT_THAT(fourthJointTransform.m_position, IsClose(expectedBindTransform.m_position));
  837. EXPECT_THAT(fourthJointTransform.m_rotation, IsClose(expectedBindTransform.m_rotation));
  838. #ifndef EMFX_SCALE_DISABLED
  839. EXPECT_THAT(fourthJointTransform.m_scale, IsClose(expectedBindTransform.m_scale));
  840. #endif
  841. }
  842. // Sample the entire pose.
  843. for (const TransformPair& expectation : values)
  844. {
  845. Pose pose;
  846. pose.LinkToActorInstance(m_actorInstance);
  847. MotionData::SampleSettings sampleSettings;
  848. sampleSettings.m_actorInstance = m_actorInstance;
  849. sampleSettings.m_sampleTime = expectation.first;
  850. motionData.SamplePose(sampleSettings, &pose);
  851. // We only verify the first joint, to see if it interpolated fine.
  852. const Transform sampledResult = pose.GetLocalSpaceTransform(0);
  853. EXPECT_THAT(sampledResult.m_position, IsClose(expectation.second.m_position));
  854. EXPECT_THAT(sampledResult.m_rotation, IsClose(expectation.second.m_rotation));
  855. #ifndef EMFX_SCALE_DISABLED
  856. EXPECT_THAT(sampledResult.m_scale, IsClose(expectation.second.m_scale));
  857. #endif
  858. // Fourth joint has no motion data to apply to our actor, so expect a bind pose.
  859. // It has motion data, but there is no joint in the skeleton that matches its name, so it is like motion data for a joint that doesn't exist in our actor.
  860. const Transform fourthJointTransform = pose.GetLocalSpaceTransform(3);
  861. EXPECT_THAT(fourthJointTransform.m_position, IsClose(expectedBindTransform.m_position));
  862. EXPECT_THAT(fourthJointTransform.m_rotation, IsClose(expectedBindTransform.m_rotation));
  863. #ifndef EMFX_SCALE_DISABLED
  864. EXPECT_THAT(fourthJointTransform.m_scale, IsClose(expectedBindTransform.m_scale));
  865. #endif
  866. }
  867. }
  868. } // namespace EMotionFX