| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- /*
- * 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/UnitTest/UnitTest.h>
- #include <AzCore/Component/Entity.h>
- #include <AzFramework/Components/TransformComponent.h>
- #include <Components/ClothComponentMesh/ClothComponentMesh.h>
- #include <UnitTestHelper.h>
- #include <ActorHelper.h>
- #include <Integration/Components/ActorComponent.h>
- namespace UnitTest
- {
- //! Fixture to setup entity with actor component and the tests data.
- class NvClothComponentMesh
- : public ::testing::Test
- {
- public:
- const AZStd::string MeshNodeName = "cloth_node";
- const AZStd::vector<AZ::Vector3> MeshVertices = {{
- AZ::Vector3(-1.0f, 0.0f, 0.0f),
- AZ::Vector3(1.0f, 0.0f, 0.0f),
- AZ::Vector3(0.0f, 1.0f, 0.0f)
- }};
- const AZStd::vector<NvCloth::SimIndexType> MeshIndices = {{
- 0, 1, 2
- }};
- const AZStd::vector<VertexSkinInfluences> MeshSkinningInfo = {{
- VertexSkinInfluences{ SkinInfluence(0, 1.0f) },
- VertexSkinInfluences{ SkinInfluence(0, 1.0f) },
- VertexSkinInfluences{ SkinInfluence(0, 1.0f) }
- }};
- const AZStd::vector<AZ::Vector2> MeshUVs = {{
- AZ::Vector2(0.0f, 0.0f),
- AZ::Vector2(1.0f, 0.0f),
- AZ::Vector2(0.5f, 1.0f)
- }};
- // [inverse mass, motion constrain radius, backstop offset, backstop radius]
- const AZStd::vector<AZ::Color> MeshClothData = {{
- AZ::Color(0.75f, 0.6f, 0.5f, 0.1f),
- AZ::Color(1.0f, 0.16f, 0.1f, 1.0f),
- AZ::Color(0.25f, 1.0f, 0.9f, 0.5f)
- }};
- const AZ::u32 LodLevel = 0;
- protected:
- // ::testing::Test overrides ...
- void SetUp() override;
- void TearDown() override;
- EMotionFX::Integration::ActorComponent* m_actorComponent = nullptr;
- private:
- AZStd::unique_ptr<AZ::Entity> m_entity;
- };
- void NvClothComponentMesh::SetUp()
- {
- m_entity = AZStd::make_unique<AZ::Entity>();
- m_entity->CreateComponent<AzFramework::TransformComponent>();
- m_actorComponent = m_entity->CreateComponent<EMotionFX::Integration::ActorComponent>();
- m_entity->Init();
- m_entity->Activate();
- }
- void NvClothComponentMesh::TearDown()
- {
- m_entity->Deactivate();
- m_actorComponent = nullptr;
- m_entity.reset();
- }
- TEST_F(NvClothComponentMesh, ClothComponentMesh_DefaultConstructor_ReturnsEmptyRenderData)
- {
- AZ::EntityId entityId;
- NvCloth::ClothComponentMesh clothComponentMesh(entityId, {});
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_TRUE(renderData.m_particles.empty());
- EXPECT_TRUE(renderData.m_tangents.empty());
- EXPECT_TRUE(renderData.m_bitangents.empty());
- EXPECT_TRUE(renderData.m_normals.empty());
- }
- TEST_F(NvClothComponentMesh, ClothComponentMesh_InitWithEmptyActor_ReturnsEmptyRenderData)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), {});
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_TRUE(renderData.m_particles.empty());
- EXPECT_TRUE(renderData.m_tangents.empty());
- EXPECT_TRUE(renderData.m_bitangents.empty());
- EXPECT_TRUE(renderData.m_normals.empty());
- }
- TEST_F(NvClothComponentMesh, ClothComponentMesh_InitWithActorWithNoMesh_ReturnsEmptyRenderData)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- actor->AddJoint(MeshNodeName);
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_TRUE(renderData.m_particles.empty());
- EXPECT_TRUE(renderData.m_tangents.empty());
- EXPECT_TRUE(renderData.m_bitangents.empty());
- EXPECT_TRUE(renderData.m_normals.empty());
- }
- // [TODO LYN-1891]
- // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
- // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
- // notification and this test does not setup a model yet.
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_InitWithEntityActorWithNoClothData_TriggersError)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- AZ_TEST_START_TRACE_SUPPRESSION;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Expect 1 error
- }
- // [TODO LYN-1891]
- // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
- // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
- // notification and this test does not setup a model yet.
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_InitWithEntityActor_ReturnsValidRenderData)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_EQ(renderData.m_particles.size(), MeshVertices.size());
- EXPECT_EQ(renderData.m_particles.size(), MeshClothData.size());
- for (size_t i = 0; i < renderData.m_particles.size(); ++i)
- {
- EXPECT_THAT(renderData.m_particles[i].GetAsVector3(), IsCloseTolerance(MeshVertices[i], Tolerance));
- EXPECT_NEAR(renderData.m_particles[i].GetW(), MeshClothData[i].GetR(), ToleranceU8);
- }
- EXPECT_THAT(renderData.m_tangents, ::testing::Each(IsCloseTolerance(AZ::Vector3::CreateAxisX(), Tolerance)));
- EXPECT_THAT(renderData.m_bitangents, ::testing::Each(IsCloseTolerance(AZ::Vector3::CreateAxisY(), Tolerance)));
- EXPECT_THAT(renderData.m_normals, ::testing::Each(IsCloseTolerance(AZ::Vector3::CreateAxisZ(), Tolerance)));
- }
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_TickClothSystem_RunningSimulationVerticesGoDown)
- {
- {
- const float height = 4.7f;
- const float radius = 1.2f;
- const auto collider = CreateCapsuleCollider(MeshNodeName, height, radius);
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->AddClothCollider(collider);
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- const AZStd::vector<NvCloth::SimParticleFormat> particlesBefore = clothComponentMesh.GetRenderData().m_particles;
- // Ticking Cloth System updates all its solvers
- for (size_t i = 0; i < 300.0f; ++i)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
- deltaTimeSim,
- AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
- }
- const AZStd::vector<NvCloth::SimParticleFormat> particlesAfter = clothComponentMesh.GetRenderData().m_particles;
- EXPECT_EQ(particlesAfter.size(), particlesBefore.size());
- for (size_t i = 0; i < particlesAfter.size(); ++i)
- {
- EXPECT_LT(particlesAfter[i].GetZ(), particlesBefore[i].GetZ());
- }
- }
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_UpdateConfigurationInvalidEntity_ReturnEmptyRenderData)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- AZ::EntityId newEntityId;
- clothComponentMesh.UpdateConfiguration(newEntityId, clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_TRUE(renderData.m_particles.empty());
- EXPECT_TRUE(renderData.m_tangents.empty());
- EXPECT_TRUE(renderData.m_bitangents.empty());
- EXPECT_TRUE(renderData.m_normals.empty());
- }
- // [TODO LYN-1891]
- // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
- // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
- // notification and this test does not setup a model yet.
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_UpdateConfigurationDifferentEntity_ReturnsRenderDataFromNewEntity)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- const AZStd::vector<AZ::Vector3> newMeshVertices = {{
- AZ::Vector3(-2.3f, 0.0f, 0.0f),
- AZ::Vector3(4.0f, 0.0f, 0.0f),
- AZ::Vector3(0.0f, -1.0f, 0.0f)
- }};
- auto newEntity = AZStd::make_unique<AZ::Entity>();
- newEntity->CreateComponent<AzFramework::TransformComponent>();
- auto* newActorComponent = newEntity->CreateComponent<EMotionFX::Integration::ActorComponent>();
- newEntity->Init();
- newEntity->Activate();
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test2");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(newMeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- newActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- clothComponentMesh.UpdateConfiguration(newActorComponent->GetEntityId(), clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_EQ(renderData.m_particles.size(), newMeshVertices.size());
- EXPECT_EQ(renderData.m_particles.size(), MeshClothData.size());
- for (size_t i = 0; i < renderData.m_particles.size(); ++i)
- {
- EXPECT_THAT(renderData.m_particles[i].GetAsVector3(), IsCloseTolerance(newMeshVertices[i], Tolerance));
- EXPECT_NEAR(renderData.m_particles[i].GetW(), MeshClothData[i].GetR(), ToleranceU8);
- }
- }
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_UpdateConfigurationInvalidMeshNode_ReturnEmptyRenderData)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- clothConfig.m_meshNode = "unknown_cloth_mesh_node";
- clothComponentMesh.UpdateConfiguration(m_actorComponent->GetEntityId(), clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_TRUE(renderData.m_particles.empty());
- EXPECT_TRUE(renderData.m_tangents.empty());
- EXPECT_TRUE(renderData.m_bitangents.empty());
- EXPECT_TRUE(renderData.m_normals.empty());
- }
- // [TODO LYN-1891]
- // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
- // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
- // notification and this test does not setup a model yet.
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_UpdateConfigurationNewMeshNode_ReturnsRenderDataFromNewMeshNode)
- {
- const AZStd::string meshNode2Name = "cloth_node_2";
- const AZStd::vector<AZ::Vector3> mesh2Vertices = {{
- AZ::Vector3(-2.3f, 0.0f, 0.0f),
- AZ::Vector3(4.0f, 0.0f, 0.0f),
- AZ::Vector3(0.0f, -1.0f, 0.0f)
- }};
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- auto meshNode2Index = actor->AddJoint(meshNode2Name);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->SetMesh(LodLevel, meshNode2Index, CreateEMotionFXMesh(mesh2Vertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- clothConfig.m_meshNode = meshNode2Name;
- clothComponentMesh.UpdateConfiguration(m_actorComponent->GetEntityId(), clothConfig);
- const auto& renderData = clothComponentMesh.GetRenderData();
- EXPECT_EQ(renderData.m_particles.size(), mesh2Vertices.size());
- EXPECT_EQ(renderData.m_particles.size(), MeshClothData.size());
- for (size_t i = 0; i < renderData.m_particles.size(); ++i)
- {
- EXPECT_THAT(renderData.m_particles[i].GetAsVector3(), IsCloseTolerance(mesh2Vertices[i], Tolerance));
- EXPECT_NEAR(renderData.m_particles[i].GetW(), MeshClothData[i].GetR(), ToleranceU8);
- }
- }
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_UpdateConfigurationInvertingGravity_RunningSimulationVerticesGoUp)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- const AZStd::vector<NvCloth::SimParticleFormat> particlesBefore = clothComponentMesh.GetRenderData().m_particles;
- clothConfig.m_gravityScale = -1.0f;
- clothComponentMesh.UpdateConfiguration(m_actorComponent->GetEntityId(), clothConfig);
- // Ticking Cloth System updates all its solvers
- for (size_t i = 0; i < 300.0f; ++i)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
- deltaTimeSim,
- AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
- }
- const AZStd::vector<NvCloth::SimParticleFormat> particlesAfter = clothComponentMesh.GetRenderData().m_particles;
- EXPECT_EQ(particlesAfter.size(), particlesBefore.size());
- for (size_t i = 0; i < particlesAfter.size(); ++i)
- {
- EXPECT_GT(particlesAfter[i].GetZ(), particlesBefore[i].GetZ());
- }
- }
- // [TODO LYN-1891]
- // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
- // At the moment, CreateAssetFromActor fills only Actor to the ActorAsset, but not the RenderActor,
- // because of that the AtomModel is not created and OnModelReady is not called.
- TEST_F(NvClothComponentMesh, DISABLED_ClothComponentMesh_ModifyMesh_RenderMeshIsUpdated)
- {
- {
- auto actor = AZStd::make_unique<ActorHelper>("actor_test");
- auto meshNodeIndex = actor->AddJoint(MeshNodeName);
- actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo, MeshUVs/*, MeshClothData*/));
- actor->FinishSetup();
- m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
- }
- NvCloth::ClothConfiguration clothConfig;
- clothConfig.m_meshNode = MeshNodeName;
- NvCloth::ClothComponentMesh clothComponentMesh(m_actorComponent->GetEntityId(), clothConfig);
- // Ticking Cloth System updates all its solvers
- for (size_t i = 0; i < 300.0f; ++i)
- {
- const float deltaTimeSim = 1.0f / 60.0f;
- AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick,
- deltaTimeSim,
- AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
- }
- /*
- CryRenderMeshStub renderMesh(MeshVertices);
- LmbrCentral::MeshModificationNotificationBus::Event(
- m_actorComponent->GetEntityId(),
- &LmbrCentral::MeshModificationNotificationBus::Events::ModifyMesh,
- LodLevel,
- 0,
- &renderMesh);
- const AZStd::vector<NvCloth::SimParticleFormat>& clothParticles = clothComponentMesh.GetRenderData().m_particles;
- const AZStd::vector<Vec3>& renderMeshPositions = renderMesh.m_positions;
- EXPECT_EQ(renderMeshPositions.size(), clothParticles.size());
- for (size_t i = 0; i < renderMeshPositions.size(); ++i)
- {
- EXPECT_THAT(LYVec3ToAZVec3(renderMeshPositions[i]), IsCloseTolerance(clothParticles[i].GetAsVector3(), Tolerance));
- }
- */
- }
- } // namespace UnitTest
|