123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AzCore/Interface/Interface.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Serialization/EditContext.h>
- #include <AzCore/Serialization/EditContextConstants.inl>
- #include <System/SystemComponent.h>
- #include <Utils/Allocators.h>
- // NvCloth library includes
- #include <foundation/PxAllocatorCallback.h>
- #include <foundation/PxErrorCallback.h>
- #include <NvCloth/Callbacks.h>
- #include <NvCloth/Solver.h>
- namespace NvCloth
- {
- namespace
- {
- // Implementation of the memory allocation callback interface using nvcloth allocator.
- class AzClothAllocatorCallback
- : public physx::PxAllocatorCallback
- {
- // NvCloth requires 16-byte alignment
- static const size_t alignment = 16;
- void* allocate(size_t size, [[maybe_unused]] const char* typeName, const char* filename, int line) override
- {
- void* ptr = AZ::AllocatorInstance<AzClothAllocator>::Get().Allocate(size, alignment, 0, "NvCloth", filename, line);
- AZ_Assert((reinterpret_cast<size_t>(ptr) & (alignment-1)) == 0, "NvCloth requires %zu-byte aligned memory allocations.", alignment);
- return ptr;
- }
- void deallocate(void* ptr) override
- {
- AZ::AllocatorInstance<AzClothAllocator>::Get().DeAllocate(ptr);
- }
- };
- // Implementation of the error callback interface directing nvcloth library errors to Open 3D Engine error output.
- class AzClothErrorCallback
- : public physx::PxErrorCallback
- {
- public:
- void reportError(physx::PxErrorCode::Enum code, [[maybe_unused]] const char* message, [[maybe_unused]] const char* file, [[maybe_unused]] int line) override
- {
- switch (code)
- {
- case physx::PxErrorCode::eDEBUG_INFO:
- case physx::PxErrorCode::eNO_ERROR:
- AZ_TracePrintf("NvCloth", "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
- break;
- case physx::PxErrorCode::eDEBUG_WARNING:
- case physx::PxErrorCode::ePERF_WARNING:
- AZ_Warning("NvCloth", false, "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
- break;
- default:
- AZ_Error("NvCloth", false, "PxErrorCode %i: %s (line %i in %s)", code, message, line, file);
- m_lastError = code;
- break;
- }
- }
- physx::PxErrorCode::Enum GetLastError() const
- {
- return m_lastError;
- }
- void ResetLastError()
- {
- m_lastError = physx::PxErrorCode::eNO_ERROR;
- }
- private:
- physx::PxErrorCode::Enum m_lastError = physx::PxErrorCode::eNO_ERROR;
- };
- // Implementation of the assert handler interface directing nvcloth asserts to Open 3D Engine assertion system.
- class AzClothAssertHandler
- : public nv::cloth::PxAssertHandler
- {
- public:
- void operator()([[maybe_unused]] const char* exp, [[maybe_unused]] const char* file, [[maybe_unused]] int line, bool& ignore) override
- {
- AZ_UNUSED(ignore);
- AZ_Assert(false, "NvCloth library assertion failed in file %s:%d: %s", file, line, exp);
- }
- };
- // Implementation of the profiler callback interface for NvCloth.
- class AzClothProfilerCallback
- : public physx::PxProfilerCallback
- {
- public:
- void* zoneStart(const char* eventName, bool detached,
- [[maybe_unused]] uint64_t contextId) override
- {
- if (detached)
- {
- AZ_PROFILE_INTERVAL_START(Cloth, AZ::Crc32(eventName), eventName);
- }
- else
- {
- AZ_PROFILE_BEGIN(Cloth, eventName);
- }
- return nullptr;
- }
- void zoneEnd([[maybe_unused]] void* profilerData,
- [[maybe_unused]] const char* eventName, bool detached,
- [[maybe_unused]] uint64_t contextId) override
- {
- if (detached)
- {
- AZ_PROFILE_INTERVAL_END(Cloth, AZ::Crc32(eventName));
- }
- else
- {
- AZ_PROFILE_END(Cloth);
- }
- }
- };
- AZStd::unique_ptr<AzClothAllocatorCallback> ClothAllocatorCallback;
- AZStd::unique_ptr<AzClothErrorCallback> ClothErrorCallback;
- AZStd::unique_ptr<AzClothAssertHandler> ClothAssertHandler;
- AZStd::unique_ptr<AzClothProfilerCallback> ClothProfilerCallback;
- }
- void SystemComponent::Reflect(AZ::ReflectContext* context)
- {
- if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<SystemComponent, AZ::Component>()
- ->Version(0);
- if (auto editContext = serializeContext->GetEditContext())
- {
- editContext->Class<SystemComponent>("NvCloth", "Provides functionality for simulating cloth using NvCloth")
- ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
- ;
- }
- }
- }
- void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
- {
- provided.push_back(AZ_CRC_CE("NvClothService"));
- }
- void SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
- {
- incompatible.push_back(AZ_CRC_CE("NvClothService"));
- }
- void SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
- {
- }
- void SystemComponent::InitializeNvClothLibrary()
- {
- ClothAllocatorCallback = AZStd::make_unique<AzClothAllocatorCallback>();
- ClothErrorCallback = AZStd::make_unique<AzClothErrorCallback>();
- ClothAssertHandler = AZStd::make_unique<AzClothAssertHandler>();
- ClothProfilerCallback = AZStd::make_unique<AzClothProfilerCallback>();
- nv::cloth::InitializeNvCloth(
- ClothAllocatorCallback.get(),
- ClothErrorCallback.get(),
- ClothAssertHandler.get(),
- ClothProfilerCallback.get());
- AZ_Assert(CheckLastClothError(), "Failed to initialize NvCloth library");
- }
- void SystemComponent::TearDownNvClothLibrary()
- {
- // NvCloth library doesn't need any destruction
- ClothProfilerCallback.reset();
- ClothAssertHandler.reset();
- ClothErrorCallback.reset();
- ClothAllocatorCallback.reset();
- }
- bool SystemComponent::CheckLastClothError()
- {
- if (ClothErrorCallback)
- {
- return ClothErrorCallback->GetLastError() == physx::PxErrorCode::eNO_ERROR;
- }
- return false;
- }
- void SystemComponent::ResetLastClothError()
- {
- if (ClothErrorCallback)
- {
- return ClothErrorCallback->ResetLastError();
- }
- }
- void SystemComponent::Activate()
- {
- InitializeSystem();
- }
- void SystemComponent::Deactivate()
- {
- DestroySystem();
- }
- ISolver* SystemComponent::FindOrCreateSolver(const AZStd::string& name)
- {
- if (ISolver* solver = GetSolver(name))
- {
- return solver;
- }
- if (AZStd::unique_ptr<Solver> newSolver = m_factory->CreateSolver(name))
- {
- m_solvers.push_back(AZStd::move(newSolver));
- return m_solvers.back().get();
- }
- return nullptr;
- }
- void SystemComponent::DestroySolver(ISolver*& solver)
- {
- if (solver)
- {
- const AZStd::string& solverName = solver->GetName();
- auto solverIt = AZStd::find_if(m_solvers.begin(), m_solvers.end(),
- [&solverName](const auto& solverInstance)
- {
- return solverInstance->GetName() == solverName;
- });
- if (solverIt != m_solvers.end())
- {
- // The solver will remove all its remaining cloths from it when destroyed
- m_solvers.erase(solverIt);
- solver = nullptr;
- }
- }
- }
- ISolver* SystemComponent::GetSolver(const AZStd::string& name)
- {
- auto solverIt = AZStd::find_if(m_solvers.begin(), m_solvers.end(),
- [&name](const auto& solverInstance)
- {
- return solverInstance->GetName() == name;
- });
- if (solverIt != m_solvers.end())
- {
- return solverIt->get();
- }
- return nullptr;
- }
- FabricId SystemComponent::FindOrCreateFabric(const FabricCookedData& fabricCookedData)
- {
- if (m_fabrics.count(fabricCookedData.m_id) != 0)
- {
- return fabricCookedData.m_id;
- }
- if (AZStd::unique_ptr<Fabric> newFabric = m_factory->CreateFabric(fabricCookedData))
- {
- m_fabrics[fabricCookedData.m_id] = AZStd::move(newFabric);
- return fabricCookedData.m_id;
- }
- return {}; // Returns invalid fabric id
- }
- void SystemComponent::DestroyFabric(FabricId fabricId)
- {
- if (auto fabricIt = m_fabrics.find(fabricId);
- fabricIt != m_fabrics.end())
- {
- // Destroy the fabric only if not used by any cloth
- if (fabricIt->second->m_numClothsUsingFabric <= 0)
- {
- m_fabrics.erase(fabricIt);
- }
- }
- }
- ICloth* SystemComponent::CreateCloth(
- const AZStd::vector<SimParticleFormat>& initialParticles,
- const FabricCookedData& fabricCookedData)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- FabricId fabricId = FindOrCreateFabric(fabricCookedData);
- if (!fabricId.IsValid())
- {
- AZ_Warning("NvCloth", false, "Failed to create cloth because it couldn't create the fabric.");
- return nullptr;
- }
- if (auto newCloth = m_factory->CreateCloth(initialParticles, m_fabrics[fabricId].get()))
- {
- ClothId newClothId = newCloth->GetId();
- auto newClothIt = m_cloths.insert({ newClothId, AZStd::move(newCloth) }).first;
- return newClothIt->second.get();
- }
- else
- {
- DestroyFabric(fabricId);
- }
- return nullptr;
- }
- void SystemComponent::DestroyCloth(ICloth*& cloth)
- {
- if (cloth)
- {
- FabricId fabricId = cloth->GetFabricCookedData().m_id;
- // Cloth will decrement its fabric's counter on destruction.
- // In addition, if the cloth still remains added into a solver, it will remove itself from it.
- m_cloths.erase(cloth->GetId());
- cloth = nullptr;
- DestroyFabric(fabricId);
- }
- }
- ICloth* SystemComponent::GetCloth(ClothId clothId)
- {
- if (auto clothIt = m_cloths.find(clothId);
- clothIt != m_cloths.end())
- {
- return clothIt->second.get();
- }
- else
- {
- return nullptr;
- }
- }
- bool SystemComponent::AddCloth(ICloth* cloth, const AZStd::string& solverName)
- {
- if (cloth)
- {
- ISolver* solver = GetSolver(solverName);
- if (!solver)
- {
- return false;
- }
- Cloth* clothInstance = azdynamic_cast<Cloth*>(cloth);
- AZ_Assert(clothInstance, "Dynamic casting from ICloth to Cloth failed.");
- Solver* solverInstance = azdynamic_cast<Solver*>(solver);
- AZ_Assert(solverInstance, "Dynamic casting from ISolver to Solver failed.");
- solverInstance->AddCloth(clothInstance);
- return true;
- }
- return false;
- }
- void SystemComponent::RemoveCloth(ICloth* cloth)
- {
- if (cloth)
- {
- Cloth* clothInstance = azdynamic_cast<Cloth*>(cloth);
- AZ_Assert(clothInstance, "Dynamic casting from ICloth to Cloth failed.");
- Solver* solverInstance = clothInstance->GetSolver();
- if (solverInstance)
- {
- solverInstance->RemoveCloth(clothInstance);
- }
- }
- }
- void SystemComponent::OnTick(
- float deltaTime,
- [[maybe_unused]] AZ::ScriptTimePoint time)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- for (auto& solverIt : m_solvers)
- {
- if (!solverIt->IsUserSimulated())
- {
- solverIt->StartSimulation(deltaTime);
- solverIt->FinishSimulation();
- }
- }
- }
- int SystemComponent::GetTickOrder()
- {
- return AZ::TICK_PHYSICS;
- }
- void SystemComponent::InitializeSystem()
- {
- // Create Factory
- m_factory = AZStd::make_unique<Factory>();
- m_factory->Init();
- // Create Default Solver
- [[maybe_unused]] ISolver* solver = FindOrCreateSolver(DefaultSolverName);
- AZ_Assert(solver, "Error: Default solver failed to be created");
- AZ::Interface<IClothSystem>::Register(this);
- AZ::TickBus::Handler::BusConnect();
- }
- void SystemComponent::DestroySystem()
- {
- AZ::TickBus::Handler::BusDisconnect();
- AZ::Interface<IClothSystem>::Unregister(this);
- // Destroy Cloths
- m_cloths.clear();
- // Destroy Fabrics
- m_fabrics.clear();
- // Destroy Solvers
- m_solvers.clear();
- // Destroy Factory
- m_factory->Destroy();
- m_factory.reset();
- }
- } // namespace NvCloth
|