Solver.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 <System/Solver.h>
  9. #include <System/Cloth.h>
  10. #include <AzCore/Jobs/JobFunction.h>
  11. // NvCloth library includes
  12. #include <NvCloth/Solver.h>
  13. namespace NvCloth
  14. {
  15. Solver::Solver(const AZStd::string& name, NvSolverUniquePtr nvSolver)
  16. : m_name(name)
  17. , m_nvSolver(AZStd::move(nvSolver))
  18. {
  19. }
  20. Solver::~Solver()
  21. {
  22. AZ_Assert(!m_isSimulating, "Please make sure the ongoing simulation is finished");
  23. // Remove any remaining cloths from the solver
  24. while (!m_cloths.empty())
  25. {
  26. RemoveClothInternal(m_cloths.begin());
  27. }
  28. }
  29. void Solver::AddCloth(Cloth* cloth)
  30. {
  31. AZ_Assert(!m_isSimulating, "Please make sure the ongoing simulation is finished before attempting to add cloth");
  32. // If the cloth was already added to a solver then remove it first.
  33. if (Solver* previousSolver = cloth->GetSolver())
  34. {
  35. // If it's already added to this solver then don't do anything.
  36. if (previousSolver->GetName() == GetName())
  37. {
  38. return;
  39. }
  40. previousSolver->RemoveCloth(cloth);
  41. }
  42. m_cloths.push_back(cloth);
  43. cloth->m_solver = this;
  44. m_nvSolver->addCloth(cloth->m_nvCloth.get());
  45. }
  46. void Solver::RemoveCloth(Cloth* cloth)
  47. {
  48. AZ_Assert(!m_isSimulating, "Please make sure the ongoing simulation is finished before attempting to remove cloth");
  49. if (cloth->GetSolver() != nullptr &&
  50. cloth->GetSolver()->GetName() == GetName())
  51. {
  52. auto clothIt = AZStd::find(m_cloths.begin(), m_cloths.end(), cloth);
  53. AZ_Assert(clothIt != m_cloths.end(), "Cloth indicates it is part of solver %s, but the solver doesn't contain it.", GetName().c_str());
  54. RemoveClothInternal(clothIt);
  55. }
  56. }
  57. size_t Solver::GetNumCloths() const
  58. {
  59. return m_cloths.size();
  60. }
  61. const AZStd::string& Solver::GetName() const
  62. {
  63. return m_name;
  64. }
  65. void Solver::Enable(bool value)
  66. {
  67. m_enabled = value;
  68. }
  69. bool Solver::IsEnabled() const
  70. {
  71. return m_enabled;
  72. }
  73. void Solver::SetUserSimulated(bool value)
  74. {
  75. m_userSimulated = value;
  76. }
  77. bool Solver::IsUserSimulated() const
  78. {
  79. return m_userSimulated;
  80. }
  81. void Solver::StartSimulation(float deltaTime)
  82. {
  83. if (!IsEnabled() || m_cloths.empty())
  84. {
  85. return;
  86. }
  87. AZ_Assert(!m_isSimulating, "Please make sure the ongoing simulation is finished before attempting to start a new one");
  88. AZ_PROFILE_FUNCTION(Cloth);
  89. m_deltaTime = deltaTime;
  90. m_simulationCompletion.Reset(true /*isClearDependent*/);
  91. m_preSimulationEvent.Signal(m_name, deltaTime);
  92. // Set isSimulating flag after the pre-simulation event is sent in case if there are handlers adding/removing cloth from the solver.
  93. m_isSimulating = true;
  94. // Setup the chain of jobs for the simulation pass
  95. // Post simulation jobs will unlock the entire simulation pass completion.
  96. ClothsPostSimulationJob* clothsPostSimulationJob = aznew ClothsPostSimulationJob(&m_cloths, m_deltaTime, &m_simulationCompletion);
  97. clothsPostSimulationJob->SetDependent(&m_simulationCompletion);
  98. // Simulation jobs will unlock the post simulation job.
  99. ClothsSimulationJob* clothsSimulationJob = aznew ClothsSimulationJob(m_nvSolver.get(), m_deltaTime, clothsPostSimulationJob);
  100. clothsSimulationJob->SetDependent(clothsPostSimulationJob);
  101. // Pre-simulation jobs will unlock the simulation job.
  102. ClothsPreSimulationJob* clothsPreSimulationJob = aznew ClothsPreSimulationJob(&m_cloths, m_deltaTime, clothsSimulationJob);
  103. clothsPreSimulationJob->SetDependent(clothsSimulationJob);
  104. // Start the jobs.
  105. clothsPreSimulationJob->Start();
  106. clothsSimulationJob->Start();
  107. clothsPostSimulationJob->Start();
  108. }
  109. void Solver::FinishSimulation()
  110. {
  111. if (!m_isSimulating)
  112. {
  113. return;
  114. }
  115. AZ_PROFILE_FUNCTION(Cloth);
  116. // Waiting for the simulation pass completition.
  117. m_simulationCompletion.StartAndWaitForCompletion();
  118. m_isSimulating = false;
  119. m_postSimulationEvent.Signal(m_name, m_deltaTime);
  120. }
  121. void Solver::SetInterCollisionDistance(float distance)
  122. {
  123. m_nvSolver->setInterCollisionDistance(distance);
  124. }
  125. void Solver::SetInterCollisionStiffness(float stiffness)
  126. {
  127. m_nvSolver->setInterCollisionStiffness(stiffness);
  128. }
  129. void Solver::SetInterCollisionIterations(AZ::u32 iterations)
  130. {
  131. m_nvSolver->setInterCollisionNbIterations(iterations);
  132. }
  133. // Note: Requires a valid cloth iterator that does not point to end()
  134. void Solver::RemoveClothInternal(Cloths::iterator clothIt)
  135. {
  136. m_nvSolver->removeCloth((*clothIt)->m_nvCloth.get());
  137. (*clothIt)->m_solver = nullptr;
  138. m_cloths.erase(clothIt);
  139. }
  140. Solver::ClothsSimulationJob::ClothsSimulationJob(nv::cloth::Solver* solver, float deltaTime,
  141. AZ::Job* continuationJob, AZ::JobContext* context) : Job(true /*isAutoDelete*/, context)
  142. , m_solver(solver)
  143. , m_continuationJob(continuationJob)
  144. , m_deltaTime(deltaTime)
  145. {
  146. }
  147. void Solver::ClothsSimulationJob::Process()
  148. {
  149. AZ_PROFILE_SCOPE(Cloth, "NvCloth::BeginSimulationJob");
  150. if (m_solver->beginSimulation(m_deltaTime))
  151. {
  152. // Setup the end simulation job.
  153. AZ::Job* endSimulationJob = AZ::CreateJobFunction([solver = m_solver]
  154. {
  155. AZ_PROFILE_SCOPE(Cloth, "NvCloth::EndSimulationJob");
  156. solver->endSimulation();
  157. }, true /*isAutoDelete*/);
  158. // Setup chunk simulation jobs.
  159. const int simulationChunkCount = m_solver->getSimulationChunkCount();
  160. for (int chunkIndex = 0; chunkIndex < simulationChunkCount; ++chunkIndex)
  161. {
  162. AZ::Job* chunkSimulationJob = AZ::CreateJobFunction([solver = m_solver, chunkIndex]
  163. {
  164. AZ_PROFILE_SCOPE(Cloth, "NvCloth::ChunkSimulationJob");
  165. solver->simulateChunk(chunkIndex);
  166. }, true /*isAutoDelete*/);
  167. // Setup job dependency to make sure the End simulation job runs _after_ all chunks are finished simulating
  168. chunkSimulationJob->SetDependent(endSimulationJob);
  169. chunkSimulationJob->Start();
  170. }
  171. // After the end simulation job is done, the next job in the chain is allowed to run
  172. endSimulationJob->SetDependentStarted(m_continuationJob);
  173. endSimulationJob->Start();
  174. }
  175. // Note that if beginSimulation returns false, we don't block the continuation job from running.
  176. // This is expected behavior.
  177. }
  178. Solver::ClothsPostSimulationJob::ClothsPostSimulationJob(const Cloths* cloths, float deltaTime,
  179. AZ::Job* continuationJob, AZ::JobContext* context) : Job(true /*isAutoDelete*/, context)
  180. , m_cloths(cloths)
  181. , m_continuationJob(continuationJob)
  182. , m_deltaTime(deltaTime)
  183. {
  184. }
  185. void Solver::ClothsPostSimulationJob::Process()
  186. {
  187. for (Cloth* cloth : *m_cloths)
  188. {
  189. AZ::Job* eventSignalJob = AZ::CreateJobFunction([cloth, deltaTime = m_deltaTime]
  190. {
  191. AZ_PROFILE_SCOPE(Cloth, "NvCloth::PostSimulationJob");
  192. // Update the cloth data after the simulation
  193. cloth->Update();
  194. // Issue post-simulation events
  195. cloth->m_postSimulationEvent.Signal(cloth->GetId(), deltaTime, cloth->GetParticles());
  196. }, true /*isAutoDelete*/);
  197. eventSignalJob->SetDependentStarted(m_continuationJob);
  198. eventSignalJob->Start();
  199. }
  200. }
  201. Solver::ClothsPreSimulationJob::ClothsPreSimulationJob(const Cloths* cloths, float deltaTime,
  202. AZ::Job* continuationJob, AZ::JobContext* context) : Job(true /*isAutoDelete*/, context)
  203. , m_cloths(cloths)
  204. , m_continuationJob(continuationJob)
  205. , m_deltaTime(deltaTime)
  206. {
  207. }
  208. void Solver::ClothsPreSimulationJob::Process()
  209. {
  210. for (Cloth* cloth : *m_cloths)
  211. {
  212. AZ::Job* eventSignalJob = AZ::CreateJobFunction([cloth, deltaTime = m_deltaTime]
  213. {
  214. AZ_PROFILE_SCOPE(Cloth, "NvCloth::PreSimulationJob");
  215. // Issue pre-simulation events
  216. cloth->m_preSimulationEvent.Signal(cloth->GetId(), deltaTime);
  217. }, true /*isAutoDelete*/);
  218. eventSignalJob->SetDependentStarted(m_continuationJob);
  219. eventSignalJob->Start();
  220. }
  221. }
  222. } // namespace NvCloth