SolverTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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 <AZTestShared/Math/MathTestHelpers.h>
  9. #include <AzCore/Interface/Interface.h>
  10. #include <AzCore/Component/TickBus.h>
  11. #include <UnitTestHelper.h>
  12. #include <TriangleInputHelper.h>
  13. #include <System/Factory.h>
  14. #include <System/Solver.h>
  15. #include <System/Fabric.h>
  16. #include <System/FabricCooker.h>
  17. #include <System/Cloth.h>
  18. #include <NvCloth/IClothSystem.h>
  19. namespace UnitTest
  20. {
  21. //! Sets up a solver and cloth for each test case.
  22. //! It also allows to create additional cloths and solvers.
  23. class NvClothSystemSolver
  24. : public ::testing::Test
  25. {
  26. protected:
  27. // ::testing::Test overrides ...
  28. void SetUp() override;
  29. void TearDown() override;
  30. AZStd::unique_ptr<NvCloth::Solver> CreateSolver(const AZStd::string& name);
  31. AZStd::unique_ptr<NvCloth::Cloth> CreateCloth();
  32. const AZStd::string m_solverName = "SolverTest";
  33. AZStd::unique_ptr<NvCloth::Solver> m_solver;
  34. AZStd::unique_ptr<NvCloth::Cloth> m_cloth;
  35. private:
  36. void CreateFabric();
  37. NvCloth::Factory m_factory;
  38. AZStd::unique_ptr<NvCloth::Fabric> m_fabric;
  39. };
  40. void NvClothSystemSolver::SetUp()
  41. {
  42. m_factory.Init();
  43. m_solver = CreateSolver(m_solverName);
  44. CreateFabric();
  45. m_cloth = CreateCloth();
  46. }
  47. void NvClothSystemSolver::TearDown()
  48. {
  49. m_cloth.reset();
  50. m_fabric.reset();
  51. m_solver.reset();
  52. m_factory.Destroy();
  53. }
  54. AZStd::unique_ptr<NvCloth::Solver> NvClothSystemSolver::CreateSolver(const AZStd::string& name)
  55. {
  56. return m_factory.CreateSolver(name);
  57. }
  58. AZStd::unique_ptr<NvCloth::Cloth> NvClothSystemSolver::CreateCloth()
  59. {
  60. return m_factory.CreateCloth(m_fabric->m_cookedData.m_particles, m_fabric.get());
  61. }
  62. void NvClothSystemSolver::CreateFabric()
  63. {
  64. const NvCloth::FabricCookedData fabricCookedData = CreateTestFabricCookedData();
  65. m_fabric = m_factory.CreateFabric(fabricCookedData);
  66. }
  67. TEST_F(NvClothSystemSolver, Solver_AddAndRemoveCloth_NumClothsIncrementAndDecrementInSolver)
  68. {
  69. EXPECT_EQ(m_solver->GetNumCloths(), 0);
  70. EXPECT_EQ(m_cloth->GetSolver(), nullptr);
  71. m_solver->AddCloth(m_cloth.get());
  72. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  73. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  74. m_solver->RemoveCloth(m_cloth.get());
  75. EXPECT_EQ(m_solver->GetNumCloths(), 0);
  76. EXPECT_EQ(m_cloth->GetSolver(), nullptr);
  77. }
  78. TEST_F(NvClothSystemSolver, Solver_AddSameClothTwice_SameClothIsNotAddedTwiceToSolver)
  79. {
  80. m_solver->AddCloth(m_cloth.get());
  81. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  82. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  83. m_solver->AddCloth(m_cloth.get()); // Second addition of the same m_cloth
  84. EXPECT_EQ(m_solver->GetNumCloths(), 1); // Number of Cloths should remain the same
  85. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  86. }
  87. TEST_F(NvClothSystemSolver, Solver_RemoveClothNotInSolver_DoesNotAffectSolver)
  88. {
  89. m_solver->AddCloth(m_cloth.get());
  90. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  91. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  92. auto newCloth = CreateCloth();
  93. m_solver->RemoveCloth(newCloth.get());
  94. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  95. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  96. }
  97. TEST_F(NvClothSystemSolver, Solver_ClothDestroyedWhileInASolver_ClothIsRemovedFromSolver)
  98. {
  99. auto newCloth = CreateCloth();
  100. m_solver->AddCloth(newCloth.get());
  101. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  102. EXPECT_EQ(newCloth->GetSolver(), m_solver.get());
  103. newCloth.reset();
  104. EXPECT_EQ(m_solver->GetNumCloths(), 0);
  105. }
  106. TEST_F(NvClothSystemSolver, Solver_SolverDestroyedWhileStillHavingACloth_ClothIsRemovedFromSolver)
  107. {
  108. auto newSolver = CreateSolver("NewSolver");
  109. auto newCloth = CreateCloth();
  110. newSolver->AddCloth(newCloth.get());
  111. EXPECT_EQ(newSolver->GetNumCloths(), 1);
  112. EXPECT_EQ(newCloth->GetSolver(), newSolver.get());
  113. newSolver.reset();
  114. EXPECT_EQ(newCloth->GetSolver(), nullptr);
  115. }
  116. TEST_F(NvClothSystemSolver, Solver_ClothAddedToASecondSolver_ClothIsRemovedFromTheFirstSolver)
  117. {
  118. m_solver->AddCloth(m_cloth.get());
  119. EXPECT_EQ(m_solver->GetNumCloths(), 1);
  120. EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
  121. auto anotherSolver = CreateSolver("AnotherSolver");
  122. anotherSolver->AddCloth(m_cloth.get());
  123. EXPECT_EQ(m_solver->GetNumCloths(), 0);
  124. EXPECT_EQ(anotherSolver->GetNumCloths(), 1);
  125. EXPECT_EQ(m_cloth->GetSolver(), anotherSolver.get());
  126. }
  127. TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_SimulationEventsSignaledWhenEnabled)
  128. {
  129. const float deltaTimeSim = 1.0f / 60.0f;
  130. bool solverPreSimulationEventSignaled = false;
  131. NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
  132. [&solverPreSimulationEventSignaled](const AZStd::string&, float)
  133. {
  134. solverPreSimulationEventSignaled = true;
  135. });
  136. bool solverPostSimulationEventSignaled = false;
  137. NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
  138. [&solverPostSimulationEventSignaled](const AZStd::string&, float)
  139. {
  140. solverPostSimulationEventSignaled = true;
  141. });
  142. m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
  143. m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
  144. m_solver->AddCloth(m_cloth.get()); // Solver needs at least one cloth to simulate
  145. m_solver->StartSimulation(deltaTimeSim);
  146. m_solver->FinishSimulation();
  147. EXPECT_TRUE(solverPreSimulationEventSignaled);
  148. EXPECT_TRUE(solverPostSimulationEventSignaled);
  149. solverPreSimulationEventSignaled = false;
  150. solverPostSimulationEventSignaled = false;
  151. m_solver->Enable(false);
  152. m_solver->StartSimulation(deltaTimeSim);
  153. m_solver->FinishSimulation();
  154. EXPECT_FALSE(solverPreSimulationEventSignaled);
  155. EXPECT_FALSE(solverPostSimulationEventSignaled);
  156. }
  157. TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulationWithNoCloths_SimulationEventsNotSignaled)
  158. {
  159. const float deltaTimeSim = 1.0f / 60.0f;
  160. bool solverPreSimulationEventSignaled = false;
  161. NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
  162. [&solverPreSimulationEventSignaled](const AZStd::string&, float)
  163. {
  164. solverPreSimulationEventSignaled = true;
  165. });
  166. bool solverPostSimulationEventSignaled = false;
  167. NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
  168. [&solverPostSimulationEventSignaled](const AZStd::string&, float)
  169. {
  170. solverPostSimulationEventSignaled = true;
  171. });
  172. m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
  173. m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
  174. m_solver->AddCloth(m_cloth.get()); // Solver needs at least one cloth to simulate
  175. m_solver->StartSimulation(deltaTimeSim);
  176. m_solver->FinishSimulation();
  177. EXPECT_TRUE(solverPreSimulationEventSignaled);
  178. EXPECT_TRUE(solverPostSimulationEventSignaled);
  179. solverPreSimulationEventSignaled = false;
  180. solverPostSimulationEventSignaled = false;
  181. m_solver->RemoveCloth(m_cloth.get()); // Leave solver without have any cloths
  182. m_solver->StartSimulation(deltaTimeSim);
  183. m_solver->FinishSimulation();
  184. EXPECT_FALSE(solverPreSimulationEventSignaled);
  185. EXPECT_FALSE(solverPostSimulationEventSignaled);
  186. }
  187. TEST_F(NvClothSystemSolver, Solver_PreAndPostSimulationEvent_SolverNameAndDeltaTimePassedAsArgumentsMatch)
  188. {
  189. const float deltaTimeSim = 1.0f / 60.0f;
  190. NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
  191. [this, deltaTimeSim](const AZStd::string& solverName, float deltaTime)
  192. {
  193. EXPECT_EQ(m_solverName, solverName);
  194. EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
  195. });
  196. NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
  197. [this, deltaTimeSim](const AZStd::string& solverName, float deltaTime)
  198. {
  199. EXPECT_EQ(m_solverName, solverName);
  200. EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
  201. });
  202. m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
  203. m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
  204. m_solver->AddCloth(m_cloth.get()); // It needs at least one cloth to simulate
  205. m_solver->StartSimulation(deltaTimeSim);
  206. m_solver->FinishSimulation();
  207. }
  208. TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_SignalsClothSimulationEvents)
  209. {
  210. const float deltaTimeSim = 1.0f / 60.0f;
  211. bool clothPreSimulationEventSignaled = false;
  212. NvCloth::ICloth::PreSimulationEvent::Handler clothPreSimulationEventHandler(
  213. [&clothPreSimulationEventSignaled](NvCloth::ClothId, float)
  214. {
  215. clothPreSimulationEventSignaled = true;
  216. });
  217. bool clothPostSimulationEventSignaled = false;
  218. NvCloth::ICloth::PostSimulationEvent::Handler clothPostSimulationEventHandler(
  219. [&clothPostSimulationEventSignaled](NvCloth::ClothId, float, const AZStd::vector<NvCloth::SimParticleFormat>&)
  220. {
  221. clothPostSimulationEventSignaled = true;
  222. });
  223. m_cloth->ConnectPreSimulationEventHandler(clothPreSimulationEventHandler);
  224. m_cloth->ConnectPostSimulationEventHandler(clothPostSimulationEventHandler);
  225. m_solver->AddCloth(m_cloth.get());
  226. m_solver->StartSimulation(deltaTimeSim);
  227. m_solver->FinishSimulation();
  228. EXPECT_TRUE(clothPreSimulationEventSignaled);
  229. EXPECT_TRUE(clothPostSimulationEventSignaled);
  230. }
  231. TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_ClothSimulationEventParametersMatch)
  232. {
  233. const float deltaTimeSim = 1.0f / 60.0f;
  234. NvCloth::ICloth::PreSimulationEvent::Handler clothPreSimulationEventHandler(
  235. [cloth = m_cloth.get(), deltaTimeSim](NvCloth::ClothId clothId, float deltaTime)
  236. {
  237. EXPECT_EQ(cloth->GetId(), clothId);
  238. EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
  239. });
  240. NvCloth::ICloth::PostSimulationEvent::Handler clothPostSimulationEventHandler(
  241. [cloth = m_cloth.get(), deltaTimeSim](NvCloth::ClothId clothId, float deltaTime, const AZStd::vector<NvCloth::SimParticleFormat>& updatedParticles)
  242. {
  243. EXPECT_EQ(cloth->GetId(), clothId);
  244. EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
  245. EXPECT_THAT(cloth->GetParticles(), ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), updatedParticles));
  246. });
  247. m_cloth->ConnectPreSimulationEventHandler(clothPreSimulationEventHandler);
  248. m_cloth->ConnectPostSimulationEventHandler(clothPostSimulationEventHandler);
  249. m_solver->AddCloth(m_cloth.get());
  250. m_solver->StartSimulation(deltaTimeSim);
  251. m_solver->FinishSimulation();
  252. }
  253. // This test uses Cloth System to check if the system's tick will update a solver in user simulated mode.
  254. // Since it relies on cloth system, the test has to use a solver and a cloth created from the system.
  255. // NvClothSystemSolver fixture is not necessary for this test.
  256. TEST(NvClothSystem, Solver_StartAndFinishSimulationCalledBySystem_SimulationEventsSignaledWhenSolverIsNotUserSimulated)
  257. {
  258. const float deltaTimeSim = 1.0f / 60.0f;
  259. // Create a solver using Cloth System
  260. NvCloth::ISolver* solver = AZ::Interface<NvCloth::IClothSystem>::Get()->FindOrCreateSolver("Solver_UserSimulatedTest");
  261. // Create a cloth using Cloth System
  262. const NvCloth::FabricCookedData fabricCookedData = CreateTestFabricCookedData();
  263. NvCloth::ICloth* cloth = AZ::Interface<NvCloth::IClothSystem>::Get()->CreateCloth(fabricCookedData.m_particles, fabricCookedData);
  264. bool solverPreSimulationEventSignaled = false;
  265. NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
  266. [&solverPreSimulationEventSignaled](const AZStd::string&, float)
  267. {
  268. solverPreSimulationEventSignaled = true;
  269. });
  270. bool solverPostSimulationEventSignaled = false;
  271. NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
  272. [&solverPostSimulationEventSignaled](const AZStd::string&, float)
  273. {
  274. solverPostSimulationEventSignaled = true;
  275. });
  276. solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
  277. solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
  278. AZ::Interface<NvCloth::IClothSystem>::Get()->AddCloth(cloth, solver->GetName()); // Solver needs at least one cloth to simulate
  279. // Ticking Cloth System updates all its solvers
  280. AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
  281. deltaTimeSim,
  282. AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
  283. EXPECT_TRUE(solverPreSimulationEventSignaled);
  284. EXPECT_TRUE(solverPostSimulationEventSignaled);
  285. solverPreSimulationEventSignaled = false;
  286. solverPostSimulationEventSignaled = false;
  287. solver->SetUserSimulated(true);
  288. // Ticking Cloth System updates all its solvers
  289. AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
  290. deltaTimeSim,
  291. AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
  292. EXPECT_FALSE(solverPreSimulationEventSignaled);
  293. EXPECT_FALSE(solverPostSimulationEventSignaled);
  294. // Manually calling simulation (as expected when solver is in used simulated mode)
  295. solver->StartSimulation(deltaTimeSim);
  296. solver->FinishSimulation();
  297. EXPECT_TRUE(solverPreSimulationEventSignaled);
  298. EXPECT_TRUE(solverPostSimulationEventSignaled);
  299. AZ::Interface<NvCloth::IClothSystem>::Get()->DestroySolver(solver);
  300. AZ::Interface<NvCloth::IClothSystem>::Get()->DestroyCloth(cloth);
  301. }
  302. } // namespace UnitTest