123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /*
- * 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 <AZTestShared/Math/MathTestHelpers.h>
- #include <AzCore/Interface/Interface.h>
- #include <AzCore/Component/TickBus.h>
- #include <UnitTestHelper.h>
- #include <TriangleInputHelper.h>
- #include <System/Factory.h>
- #include <System/Solver.h>
- #include <System/Fabric.h>
- #include <System/FabricCooker.h>
- #include <System/Cloth.h>
- #include <NvCloth/IClothSystem.h>
- namespace UnitTest
- {
- //! Sets up a solver and cloth for each test case.
- //! It also allows to create additional cloths and solvers.
- class NvClothSystemSolver
- : public ::testing::Test
- {
- protected:
- // ::testing::Test overrides ...
- void SetUp() override;
- void TearDown() override;
- AZStd::unique_ptr<NvCloth::Solver> CreateSolver(const AZStd::string& name);
- AZStd::unique_ptr<NvCloth::Cloth> CreateCloth();
- const AZStd::string m_solverName = "SolverTest";
- AZStd::unique_ptr<NvCloth::Solver> m_solver;
- AZStd::unique_ptr<NvCloth::Cloth> m_cloth;
- private:
- void CreateFabric();
- NvCloth::Factory m_factory;
- AZStd::unique_ptr<NvCloth::Fabric> m_fabric;
- };
- void NvClothSystemSolver::SetUp()
- {
- m_factory.Init();
- m_solver = CreateSolver(m_solverName);
- CreateFabric();
- m_cloth = CreateCloth();
- }
- void NvClothSystemSolver::TearDown()
- {
- m_cloth.reset();
- m_fabric.reset();
- m_solver.reset();
- m_factory.Destroy();
- }
- AZStd::unique_ptr<NvCloth::Solver> NvClothSystemSolver::CreateSolver(const AZStd::string& name)
- {
- return m_factory.CreateSolver(name);
- }
- AZStd::unique_ptr<NvCloth::Cloth> NvClothSystemSolver::CreateCloth()
- {
- return m_factory.CreateCloth(m_fabric->m_cookedData.m_particles, m_fabric.get());
- }
- void NvClothSystemSolver::CreateFabric()
- {
- const NvCloth::FabricCookedData fabricCookedData = CreateTestFabricCookedData();
- m_fabric = m_factory.CreateFabric(fabricCookedData);
- }
- TEST_F(NvClothSystemSolver, Solver_AddAndRemoveCloth_NumClothsIncrementAndDecrementInSolver)
- {
- EXPECT_EQ(m_solver->GetNumCloths(), 0);
- EXPECT_EQ(m_cloth->GetSolver(), nullptr);
- m_solver->AddCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- m_solver->RemoveCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 0);
- EXPECT_EQ(m_cloth->GetSolver(), nullptr);
- }
- TEST_F(NvClothSystemSolver, Solver_AddSameClothTwice_SameClothIsNotAddedTwiceToSolver)
- {
- m_solver->AddCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- m_solver->AddCloth(m_cloth.get()); // Second addition of the same m_cloth
- EXPECT_EQ(m_solver->GetNumCloths(), 1); // Number of Cloths should remain the same
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- }
- TEST_F(NvClothSystemSolver, Solver_RemoveClothNotInSolver_DoesNotAffectSolver)
- {
- m_solver->AddCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- auto newCloth = CreateCloth();
- m_solver->RemoveCloth(newCloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- }
- TEST_F(NvClothSystemSolver, Solver_ClothDestroyedWhileInASolver_ClothIsRemovedFromSolver)
- {
- auto newCloth = CreateCloth();
- m_solver->AddCloth(newCloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(newCloth->GetSolver(), m_solver.get());
- newCloth.reset();
- EXPECT_EQ(m_solver->GetNumCloths(), 0);
- }
- TEST_F(NvClothSystemSolver, Solver_SolverDestroyedWhileStillHavingACloth_ClothIsRemovedFromSolver)
- {
- auto newSolver = CreateSolver("NewSolver");
- auto newCloth = CreateCloth();
- newSolver->AddCloth(newCloth.get());
- EXPECT_EQ(newSolver->GetNumCloths(), 1);
- EXPECT_EQ(newCloth->GetSolver(), newSolver.get());
- newSolver.reset();
- EXPECT_EQ(newCloth->GetSolver(), nullptr);
- }
- TEST_F(NvClothSystemSolver, Solver_ClothAddedToASecondSolver_ClothIsRemovedFromTheFirstSolver)
- {
- m_solver->AddCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), m_solver.get());
- auto anotherSolver = CreateSolver("AnotherSolver");
- anotherSolver->AddCloth(m_cloth.get());
- EXPECT_EQ(m_solver->GetNumCloths(), 0);
- EXPECT_EQ(anotherSolver->GetNumCloths(), 1);
- EXPECT_EQ(m_cloth->GetSolver(), anotherSolver.get());
- }
- TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_SimulationEventsSignaledWhenEnabled)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- bool solverPreSimulationEventSignaled = false;
- NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
- [&solverPreSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPreSimulationEventSignaled = true;
- });
- bool solverPostSimulationEventSignaled = false;
- NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
- [&solverPostSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPostSimulationEventSignaled = true;
- });
- m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
- m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
- m_solver->AddCloth(m_cloth.get()); // Solver needs at least one cloth to simulate
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- EXPECT_TRUE(solverPreSimulationEventSignaled);
- EXPECT_TRUE(solverPostSimulationEventSignaled);
- solverPreSimulationEventSignaled = false;
- solverPostSimulationEventSignaled = false;
- m_solver->Enable(false);
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- EXPECT_FALSE(solverPreSimulationEventSignaled);
- EXPECT_FALSE(solverPostSimulationEventSignaled);
- }
- TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulationWithNoCloths_SimulationEventsNotSignaled)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- bool solverPreSimulationEventSignaled = false;
- NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
- [&solverPreSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPreSimulationEventSignaled = true;
- });
- bool solverPostSimulationEventSignaled = false;
- NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
- [&solverPostSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPostSimulationEventSignaled = true;
- });
- m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
- m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
- m_solver->AddCloth(m_cloth.get()); // Solver needs at least one cloth to simulate
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- EXPECT_TRUE(solverPreSimulationEventSignaled);
- EXPECT_TRUE(solverPostSimulationEventSignaled);
- solverPreSimulationEventSignaled = false;
- solverPostSimulationEventSignaled = false;
- m_solver->RemoveCloth(m_cloth.get()); // Leave solver without have any cloths
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- EXPECT_FALSE(solverPreSimulationEventSignaled);
- EXPECT_FALSE(solverPostSimulationEventSignaled);
- }
- TEST_F(NvClothSystemSolver, Solver_PreAndPostSimulationEvent_SolverNameAndDeltaTimePassedAsArgumentsMatch)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
- [this, deltaTimeSim](const AZStd::string& solverName, float deltaTime)
- {
- EXPECT_EQ(m_solverName, solverName);
- EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
- });
- NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
- [this, deltaTimeSim](const AZStd::string& solverName, float deltaTime)
- {
- EXPECT_EQ(m_solverName, solverName);
- EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
- });
- m_solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
- m_solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
- m_solver->AddCloth(m_cloth.get()); // It needs at least one cloth to simulate
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- }
- TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_SignalsClothSimulationEvents)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- bool clothPreSimulationEventSignaled = false;
- NvCloth::ICloth::PreSimulationEvent::Handler clothPreSimulationEventHandler(
- [&clothPreSimulationEventSignaled](NvCloth::ClothId, float)
- {
- clothPreSimulationEventSignaled = true;
- });
- bool clothPostSimulationEventSignaled = false;
- NvCloth::ICloth::PostSimulationEvent::Handler clothPostSimulationEventHandler(
- [&clothPostSimulationEventSignaled](NvCloth::ClothId, float, const AZStd::vector<NvCloth::SimParticleFormat>&)
- {
- clothPostSimulationEventSignaled = true;
- });
- m_cloth->ConnectPreSimulationEventHandler(clothPreSimulationEventHandler);
- m_cloth->ConnectPostSimulationEventHandler(clothPostSimulationEventHandler);
- m_solver->AddCloth(m_cloth.get());
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- EXPECT_TRUE(clothPreSimulationEventSignaled);
- EXPECT_TRUE(clothPostSimulationEventSignaled);
- }
- TEST_F(NvClothSystemSolver, Solver_StartAndFinishSimulation_ClothSimulationEventParametersMatch)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- NvCloth::ICloth::PreSimulationEvent::Handler clothPreSimulationEventHandler(
- [cloth = m_cloth.get(), deltaTimeSim](NvCloth::ClothId clothId, float deltaTime)
- {
- EXPECT_EQ(cloth->GetId(), clothId);
- EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
- });
- NvCloth::ICloth::PostSimulationEvent::Handler clothPostSimulationEventHandler(
- [cloth = m_cloth.get(), deltaTimeSim](NvCloth::ClothId clothId, float deltaTime, const AZStd::vector<NvCloth::SimParticleFormat>& updatedParticles)
- {
- EXPECT_EQ(cloth->GetId(), clothId);
- EXPECT_NEAR(deltaTimeSim, deltaTime, Tolerance);
- EXPECT_THAT(cloth->GetParticles(), ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), updatedParticles));
- });
- m_cloth->ConnectPreSimulationEventHandler(clothPreSimulationEventHandler);
- m_cloth->ConnectPostSimulationEventHandler(clothPostSimulationEventHandler);
- m_solver->AddCloth(m_cloth.get());
- m_solver->StartSimulation(deltaTimeSim);
- m_solver->FinishSimulation();
- }
- // This test uses Cloth System to check if the system's tick will update a solver in user simulated mode.
- // Since it relies on cloth system, the test has to use a solver and a cloth created from the system.
- // NvClothSystemSolver fixture is not necessary for this test.
- TEST(NvClothSystem, Solver_StartAndFinishSimulationCalledBySystem_SimulationEventsSignaledWhenSolverIsNotUserSimulated)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- // Create a solver using Cloth System
- NvCloth::ISolver* solver = AZ::Interface<NvCloth::IClothSystem>::Get()->FindOrCreateSolver("Solver_UserSimulatedTest");
- // Create a cloth using Cloth System
- const NvCloth::FabricCookedData fabricCookedData = CreateTestFabricCookedData();
- NvCloth::ICloth* cloth = AZ::Interface<NvCloth::IClothSystem>::Get()->CreateCloth(fabricCookedData.m_particles, fabricCookedData);
- bool solverPreSimulationEventSignaled = false;
- NvCloth::ISolver::PreSimulationEvent::Handler solverPreSimulationEventHandler(
- [&solverPreSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPreSimulationEventSignaled = true;
- });
- bool solverPostSimulationEventSignaled = false;
- NvCloth::ISolver::PostSimulationEvent::Handler solverPostSimulationEventHandler(
- [&solverPostSimulationEventSignaled](const AZStd::string&, float)
- {
- solverPostSimulationEventSignaled = true;
- });
- solver->ConnectPreSimulationEventHandler(solverPreSimulationEventHandler);
- solver->ConnectPostSimulationEventHandler(solverPostSimulationEventHandler);
- AZ::Interface<NvCloth::IClothSystem>::Get()->AddCloth(cloth, solver->GetName()); // Solver needs at least one cloth to simulate
- // Ticking Cloth System updates all its solvers
- AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
- deltaTimeSim,
- AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
- EXPECT_TRUE(solverPreSimulationEventSignaled);
- EXPECT_TRUE(solverPostSimulationEventSignaled);
- solverPreSimulationEventSignaled = false;
- solverPostSimulationEventSignaled = false;
- solver->SetUserSimulated(true);
- // Ticking Cloth System updates all its solvers
- AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
- deltaTimeSim,
- AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
- EXPECT_FALSE(solverPreSimulationEventSignaled);
- EXPECT_FALSE(solverPostSimulationEventSignaled);
- // Manually calling simulation (as expected when solver is in used simulated mode)
- solver->StartSimulation(deltaTimeSim);
- solver->FinishSimulation();
- EXPECT_TRUE(solverPreSimulationEventSignaled);
- EXPECT_TRUE(solverPostSimulationEventSignaled);
- AZ::Interface<NvCloth::IClothSystem>::Get()->DestroySolver(solver);
- AZ::Interface<NvCloth::IClothSystem>::Get()->DestroyCloth(cloth);
- }
- } // namespace UnitTest
|