SystemComponent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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/Interface/Interface.h>
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/EditContextConstants.inl>
  12. #include <System/SystemComponent.h>
  13. #include <Utils/Allocators.h>
  14. // NvCloth library includes
  15. #include <foundation/PxAllocatorCallback.h>
  16. #include <foundation/PxErrorCallback.h>
  17. #include <NvCloth/Callbacks.h>
  18. #include <NvCloth/Solver.h>
  19. namespace NvCloth
  20. {
  21. namespace
  22. {
  23. // Implementation of the memory allocation callback interface using nvcloth allocator.
  24. class AzClothAllocatorCallback
  25. : public physx::PxAllocatorCallback
  26. {
  27. // NvCloth requires 16-byte alignment
  28. static const size_t alignment = 16;
  29. void* allocate(size_t size, [[maybe_unused]] const char* typeName, const char* filename, int line) override
  30. {
  31. void* ptr = AZ::AllocatorInstance<AzClothAllocator>::Get().Allocate(size, alignment, 0, "NvCloth", filename, line);
  32. AZ_Assert((reinterpret_cast<size_t>(ptr) & (alignment-1)) == 0, "NvCloth requires %zu-byte aligned memory allocations.", alignment);
  33. return ptr;
  34. }
  35. void deallocate(void* ptr) override
  36. {
  37. AZ::AllocatorInstance<AzClothAllocator>::Get().DeAllocate(ptr);
  38. }
  39. };
  40. // Implementation of the error callback interface directing nvcloth library errors to Open 3D Engine error output.
  41. class AzClothErrorCallback
  42. : public physx::PxErrorCallback
  43. {
  44. public:
  45. void reportError(physx::PxErrorCode::Enum code, [[maybe_unused]] const char* message, [[maybe_unused]] const char* file, [[maybe_unused]] int line) override
  46. {
  47. switch (code)
  48. {
  49. case physx::PxErrorCode::eDEBUG_INFO:
  50. case physx::PxErrorCode::eNO_ERROR:
  51. AZ_TracePrintf("NvCloth", "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
  52. break;
  53. case physx::PxErrorCode::eDEBUG_WARNING:
  54. case physx::PxErrorCode::ePERF_WARNING:
  55. AZ_Warning("NvCloth", false, "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
  56. break;
  57. default:
  58. AZ_Error("NvCloth", false, "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
  59. m_lastError = code;
  60. break;
  61. }
  62. }
  63. physx::PxErrorCode::Enum GetLastError() const
  64. {
  65. return m_lastError;
  66. }
  67. void ResetLastError()
  68. {
  69. m_lastError = physx::PxErrorCode::eNO_ERROR;
  70. }
  71. private:
  72. physx::PxErrorCode::Enum m_lastError = physx::PxErrorCode::eNO_ERROR;
  73. };
  74. // Implementation of the assert handler interface directing nvcloth asserts to Open 3D Engine assertion system.
  75. class AzClothAssertHandler
  76. : public nv::cloth::PxAssertHandler
  77. {
  78. public:
  79. void operator()([[maybe_unused]] const char* exp, [[maybe_unused]] const char* file, [[maybe_unused]] int line, bool& ignore) override
  80. {
  81. AZ_UNUSED(ignore);
  82. AZ_Assert(false, "NvCloth library assertion failed in file %s:%d: %s", file, line, exp);
  83. }
  84. };
  85. // Implementation of the profiler callback interface for NvCloth.
  86. class AzClothProfilerCallback
  87. : public physx::PxProfilerCallback
  88. {
  89. public:
  90. void* zoneStart(const char* eventName, bool detached,
  91. [[maybe_unused]] uint64_t contextId) override
  92. {
  93. if (detached)
  94. {
  95. AZ_PROFILE_INTERVAL_START(Cloth, AZ::Crc32(eventName), eventName);
  96. }
  97. else
  98. {
  99. AZ_PROFILE_BEGIN(Cloth, eventName);
  100. }
  101. return nullptr;
  102. }
  103. void zoneEnd([[maybe_unused]] void* profilerData,
  104. [[maybe_unused]] const char* eventName, bool detached,
  105. [[maybe_unused]] uint64_t contextId) override
  106. {
  107. if (detached)
  108. {
  109. AZ_PROFILE_INTERVAL_END(Cloth, AZ::Crc32(eventName));
  110. }
  111. else
  112. {
  113. AZ_PROFILE_END(Cloth);
  114. }
  115. }
  116. };
  117. AZStd::unique_ptr<AzClothAllocatorCallback> ClothAllocatorCallback;
  118. AZStd::unique_ptr<AzClothErrorCallback> ClothErrorCallback;
  119. AZStd::unique_ptr<AzClothAssertHandler> ClothAssertHandler;
  120. AZStd::unique_ptr<AzClothProfilerCallback> ClothProfilerCallback;
  121. }
  122. void SystemComponent::Reflect(AZ::ReflectContext* context)
  123. {
  124. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  125. {
  126. serializeContext->Class<SystemComponent, AZ::Component>()
  127. ->Version(0);
  128. if (auto editContext = serializeContext->GetEditContext())
  129. {
  130. editContext->Class<SystemComponent>("NvCloth", "Provides functionality for simulating cloth using NvCloth")
  131. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  132. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  133. ;
  134. }
  135. }
  136. }
  137. void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  138. {
  139. provided.push_back(AZ_CRC_CE("NvClothService"));
  140. }
  141. void SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  142. {
  143. incompatible.push_back(AZ_CRC_CE("NvClothService"));
  144. }
  145. void SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
  146. {
  147. }
  148. void SystemComponent::InitializeNvClothLibrary()
  149. {
  150. ClothAllocatorCallback = AZStd::make_unique<AzClothAllocatorCallback>();
  151. ClothErrorCallback = AZStd::make_unique<AzClothErrorCallback>();
  152. ClothAssertHandler = AZStd::make_unique<AzClothAssertHandler>();
  153. ClothProfilerCallback = AZStd::make_unique<AzClothProfilerCallback>();
  154. nv::cloth::InitializeNvCloth(
  155. ClothAllocatorCallback.get(),
  156. ClothErrorCallback.get(),
  157. ClothAssertHandler.get(),
  158. ClothProfilerCallback.get());
  159. AZ_Assert(CheckLastClothError(), "Failed to initialize NvCloth library");
  160. }
  161. void SystemComponent::TearDownNvClothLibrary()
  162. {
  163. // NvCloth library doesn't need any destruction
  164. ClothProfilerCallback.reset();
  165. ClothAssertHandler.reset();
  166. ClothErrorCallback.reset();
  167. ClothAllocatorCallback.reset();
  168. }
  169. bool SystemComponent::CheckLastClothError()
  170. {
  171. if (ClothErrorCallback)
  172. {
  173. return ClothErrorCallback->GetLastError() == physx::PxErrorCode::eNO_ERROR;
  174. }
  175. return false;
  176. }
  177. void SystemComponent::ResetLastClothError()
  178. {
  179. if (ClothErrorCallback)
  180. {
  181. return ClothErrorCallback->ResetLastError();
  182. }
  183. }
  184. void SystemComponent::Activate()
  185. {
  186. InitializeSystem();
  187. }
  188. void SystemComponent::Deactivate()
  189. {
  190. DestroySystem();
  191. }
  192. ISolver* SystemComponent::FindOrCreateSolver(const AZStd::string& name)
  193. {
  194. if (ISolver* solver = GetSolver(name))
  195. {
  196. return solver;
  197. }
  198. if (AZStd::unique_ptr<Solver> newSolver = m_factory->CreateSolver(name))
  199. {
  200. m_solvers.push_back(AZStd::move(newSolver));
  201. return m_solvers.back().get();
  202. }
  203. return nullptr;
  204. }
  205. void SystemComponent::DestroySolver(ISolver*& solver)
  206. {
  207. if (solver)
  208. {
  209. const AZStd::string& solverName = solver->GetName();
  210. auto solverIt = AZStd::find_if(m_solvers.begin(), m_solvers.end(),
  211. [&solverName](const auto& solverInstance)
  212. {
  213. return solverInstance->GetName() == solverName;
  214. });
  215. if (solverIt != m_solvers.end())
  216. {
  217. // The solver will remove all its remaining cloths from it when destroyed
  218. m_solvers.erase(solverIt);
  219. solver = nullptr;
  220. }
  221. }
  222. }
  223. ISolver* SystemComponent::GetSolver(const AZStd::string& name)
  224. {
  225. auto solverIt = AZStd::find_if(m_solvers.begin(), m_solvers.end(),
  226. [&name](const auto& solverInstance)
  227. {
  228. return solverInstance->GetName() == name;
  229. });
  230. if (solverIt != m_solvers.end())
  231. {
  232. return solverIt->get();
  233. }
  234. return nullptr;
  235. }
  236. FabricId SystemComponent::FindOrCreateFabric(const FabricCookedData& fabricCookedData)
  237. {
  238. if (m_fabrics.count(fabricCookedData.m_id) != 0)
  239. {
  240. return fabricCookedData.m_id;
  241. }
  242. if (AZStd::unique_ptr<Fabric> newFabric = m_factory->CreateFabric(fabricCookedData))
  243. {
  244. m_fabrics[fabricCookedData.m_id] = AZStd::move(newFabric);
  245. return fabricCookedData.m_id;
  246. }
  247. return {}; // Returns invalid fabric id
  248. }
  249. void SystemComponent::DestroyFabric(FabricId fabricId)
  250. {
  251. if (auto fabricIt = m_fabrics.find(fabricId);
  252. fabricIt != m_fabrics.end())
  253. {
  254. // Destroy the fabric only if not used by any cloth
  255. if (fabricIt->second->m_numClothsUsingFabric <= 0)
  256. {
  257. m_fabrics.erase(fabricIt);
  258. }
  259. }
  260. }
  261. ICloth* SystemComponent::CreateCloth(
  262. const AZStd::vector<SimParticleFormat>& initialParticles,
  263. const FabricCookedData& fabricCookedData)
  264. {
  265. AZ_PROFILE_FUNCTION(Cloth);
  266. FabricId fabricId = FindOrCreateFabric(fabricCookedData);
  267. if (!fabricId.IsValid())
  268. {
  269. AZ_Warning("NvCloth", false, "Failed to create cloth because it couldn't create the fabric.");
  270. return nullptr;
  271. }
  272. if (auto newCloth = m_factory->CreateCloth(initialParticles, m_fabrics[fabricId].get()))
  273. {
  274. ClothId newClothId = newCloth->GetId();
  275. auto newClothIt = m_cloths.insert({ newClothId, AZStd::move(newCloth) }).first;
  276. return newClothIt->second.get();
  277. }
  278. else
  279. {
  280. DestroyFabric(fabricId);
  281. }
  282. return nullptr;
  283. }
  284. void SystemComponent::DestroyCloth(ICloth*& cloth)
  285. {
  286. if (cloth)
  287. {
  288. FabricId fabricId = cloth->GetFabricCookedData().m_id;
  289. // Cloth will decrement its fabric's counter on destruction.
  290. // In addition, if the cloth still remains added into a solver, it will remove itself from it.
  291. m_cloths.erase(cloth->GetId());
  292. cloth = nullptr;
  293. DestroyFabric(fabricId);
  294. }
  295. }
  296. ICloth* SystemComponent::GetCloth(ClothId clothId)
  297. {
  298. if (auto clothIt = m_cloths.find(clothId);
  299. clothIt != m_cloths.end())
  300. {
  301. return clothIt->second.get();
  302. }
  303. else
  304. {
  305. return nullptr;
  306. }
  307. }
  308. bool SystemComponent::AddCloth(ICloth* cloth, const AZStd::string& solverName)
  309. {
  310. if (cloth)
  311. {
  312. ISolver* solver = GetSolver(solverName);
  313. if (!solver)
  314. {
  315. return false;
  316. }
  317. Cloth* clothInstance = azdynamic_cast<Cloth*>(cloth);
  318. AZ_Assert(clothInstance, "Dynamic casting from ICloth to Cloth failed.");
  319. Solver* solverInstance = azdynamic_cast<Solver*>(solver);
  320. AZ_Assert(solverInstance, "Dynamic casting from ISolver to Solver failed.");
  321. solverInstance->AddCloth(clothInstance);
  322. return true;
  323. }
  324. return false;
  325. }
  326. void SystemComponent::RemoveCloth(ICloth* cloth)
  327. {
  328. if (cloth)
  329. {
  330. Cloth* clothInstance = azdynamic_cast<Cloth*>(cloth);
  331. AZ_Assert(clothInstance, "Dynamic casting from ICloth to Cloth failed.");
  332. Solver* solverInstance = clothInstance->GetSolver();
  333. if (solverInstance)
  334. {
  335. solverInstance->RemoveCloth(clothInstance);
  336. }
  337. }
  338. }
  339. void SystemComponent::OnTick(
  340. float deltaTime,
  341. [[maybe_unused]] AZ::ScriptTimePoint time)
  342. {
  343. AZ_PROFILE_FUNCTION(Cloth);
  344. for (auto& solverIt : m_solvers)
  345. {
  346. if (!solverIt->IsUserSimulated())
  347. {
  348. solverIt->StartSimulation(deltaTime);
  349. solverIt->FinishSimulation();
  350. }
  351. }
  352. }
  353. int SystemComponent::GetTickOrder()
  354. {
  355. return AZ::TICK_PHYSICS;
  356. }
  357. void SystemComponent::InitializeSystem()
  358. {
  359. // Create Factory
  360. m_factory = AZStd::make_unique<Factory>();
  361. m_factory->Init();
  362. // Create Default Solver
  363. [[maybe_unused]] ISolver* solver = FindOrCreateSolver(DefaultSolverName);
  364. AZ_Assert(solver, "Error: Default solver failed to be created");
  365. AZ::Interface<IClothSystem>::Register(this);
  366. AZ::TickBus::Handler::BusConnect();
  367. }
  368. void SystemComponent::DestroySystem()
  369. {
  370. AZ::TickBus::Handler::BusDisconnect();
  371. AZ::Interface<IClothSystem>::Unregister(this);
  372. // Destroy Cloths
  373. m_cloths.clear();
  374. // Destroy Fabrics
  375. m_fabrics.clear();
  376. // Destroy Solvers
  377. m_solvers.clear();
  378. // Destroy Factory
  379. m_factory->Destroy();
  380. m_factory.reset();
  381. }
  382. } // namespace NvCloth