Files
o3de/Gems/NvCloth/Code/Tests/Components/ClothComponentMesh/ActorClothSkinningTest.cpp
T
Aaron Ruiz Mora 5fd8c35878 Fixing cloth working with Actors by reading directly from ModelAsset instead from EMFX Mesh. (#235)
- Fixing cloth working with Actors by reading directly from ModelAsset instead from EMFX Mesh.
- Actor caches map from joint indices in skin metadata to skeleton indices so they can be query later.
- Actor cloth skinning reads indices and weights from Model Asset instead from EMFX Mesh.
- Actor cloth skinning with unlimited skinning bones.
- Sort out cloth unit tests by disabling them until there is a way to create an Atom mesh
- Addressing feedback.
2021-04-23 19:46:57 +01:00

326 lines
14 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Components/ClothComponentMesh/ActorClothSkinning.h>
#include <Utils/AssetHelper.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 NvClothActorClothSkinning
: public ::testing::Test
{
public:
const AZStd::string MeshNodeName = "cloth_mesh_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 = {{
{ {0, 1.0f} },
{ {0, 1.0f} },
{ {0, 1.0f} },
}};
const AZStd::vector<int> MeshRemappedVertices = {{
0, 1, 2
}};
const AZ::u32 LodLevel = 0;
const NvCloth::MeshNodeInfo MeshNodeInfo = {
static_cast<int>(LodLevel),
{{
// One SubMesh
{
0, // Primitive index
0, // First vertex
static_cast<int>(MeshVertices.size()), // Vertex count
0, // First index
static_cast<int>(MeshIndices.size()) // Index count
}
}}
};
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 NvClothActorClothSkinning::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 NvClothActorClothSkinning::TearDown()
{
m_entity->Deactivate();
m_actorComponent = nullptr;
m_entity.reset();
}
TEST_F(NvClothActorClothSkinning, ActorClothSkinning_CreateWithNoData_ReturnsNull)
{
AZ::EntityId entityId;
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(entityId, {}, 0, {});
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
TEST_F(NvClothActorClothSkinning, ActorClothSkinning_CreateWithDataButWithInvalidEntityId_ReturnsNull)
{
AZ::EntityId entityId;
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(entityId, MeshNodeInfo, MeshRemappedVertices.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
TEST_F(NvClothActorClothSkinning, ActorClothSkinning_CreateWithEmptyActor_ReturnsNull)
{
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
actor->FinishSetup();
m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), {}, 0, {});
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
TEST_F(NvClothActorClothSkinning, ActorClothSkinning_CreateWithActorWhoseMeshHasNoSkinningInfo_ReturnsNull)
{
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
auto meshNodeIndex = actor->AddJoint(MeshNodeName);
actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices));
actor->FinishSetup();
m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
TEST_F(NvClothActorClothSkinning, DISABLED_ActorClothSkinning_CreateWithActor_ReturnsValidInstance)
{
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
auto meshNodeIndex = actor->AddJoint(MeshNodeName);
actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo));
actor->FinishSetup();
m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() != nullptr);
}
TEST_F(NvClothActorClothSkinning, DISABLED_ActorClothSkinning_UpdateAndApplyLinearSkinning_ModifiesVertices)
{
const AZ::Transform meshNodeTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f));
EMotionFX::Integration::ActorComponent::Configuration actorConfig;
actorConfig.m_skinningMethod = EMotionFX::Integration::SkinningMethod::Linear;
auto entity = AZStd::make_unique<AZ::Entity>();
entity->CreateComponent<AzFramework::TransformComponent>();
auto* actorComponent = entity->CreateComponent<EMotionFX::Integration::ActorComponent>(&actorConfig);
entity->Init();
entity->Activate();
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
auto meshNodeIndex = actor->AddJoint(MeshNodeName, meshNodeTransform);
actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo));
actor->FinishSetup();
actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
const AZStd::vector<NvCloth::SimParticleFormat> clothParticles = {{
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[0], 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[1], 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[2], 1.0f),
}};
AZStd::vector<NvCloth::SimParticleFormat> skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
// Update actor instance's joints transforms
const AZ::Transform newMeshNodeTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(180.0f));
EMotionFX::Pose* currentPose = actorComponent->GetActorInstance()->GetTransformData()->GetCurrentPose();
currentPose->SetLocalSpaceTransform(0, newMeshNodeTransform);
actorComponent->GetActorInstance()->UpdateSkinningMatrices();
AZStd::vector<NvCloth::SimParticleFormat> newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
const AZ::Transform diffTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f));
const AZStd::vector<NvCloth::SimParticleFormat> clothParticlesResult = {{
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(diffTransform.TransformPoint(MeshVertices[0]), 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(diffTransform.TransformPoint(MeshVertices[1]), 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(diffTransform.TransformPoint(MeshVertices[2]), 1.0f),
}};
EXPECT_THAT(newSkinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticlesResult));
}
TEST_F(NvClothActorClothSkinning, DISABLED_ActorClothSkinning_UpdateAndApplyDualQuatSkinning_ModifiesVertices)
{
const AZStd::string rootNodeName = "root_node";
const AZStd::string meshNodeName = "cloth_mesh_node";
const AZStd::vector<VertexSkinInfluences> meshSkinningInfo = {{
{ {1, 0.75f}, {0, 0.25f} },
{ {1, 0.75f}, {0, 0.25f} },
{ {1, 0.75f}, {0, 0.25f} },
}};
const AZ::Transform rootNodeTransform = AZ::Transform::CreateTranslation(AZ::Vector3(2.0f, 53.0f, -65.0f));
const AZ::Transform meshNodeTransform = AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateRotationY(AZ::DegToRad(36.0f)), AZ::Vector3(3.0f, -2.3f, 16.0f));
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
actor->AddJoint(rootNodeName, rootNodeTransform);
auto meshNodeIndex = actor->AddJoint(meshNodeName, meshNodeTransform, rootNodeName);
actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, meshSkinningInfo));
actor->FinishSetup();
m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
const AZStd::vector<NvCloth::SimParticleFormat> clothParticles = {{
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[0], 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[1], 1.0f),
NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[2], 1.0f),
}};
AZStd::vector<NvCloth::SimParticleFormat> skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
// Update actor instance's joints transforms
const AZ::Transform newJointRootTransform = AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(-32.0f)), AZ::Vector3(2.5f, -6.0f, 0.2f));
const AZ::Transform newJointChildTransform = AZ::Transform::CreateTranslation(AZ::Vector3(-2.0f, 3.0f, 0.0f));
EMotionFX::Pose* currentPose = m_actorComponent->GetActorInstance()->GetTransformData()->GetCurrentPose();
currentPose->SetLocalSpaceTransform(0, newJointRootTransform);
currentPose->SetLocalSpaceTransform(1, newJointChildTransform);
m_actorComponent->GetActorInstance()->UpdateSkinningMatrices();
AZStd::vector<NvCloth::SimParticleFormat> newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
const AZStd::vector<NvCloth::SimParticleFormat> clothParticlesResult = {{
NvCloth::SimParticleFormat(-48.4177f, -31.9446f, 45.2279f, 1.0f),
NvCloth::SimParticleFormat(-46.9087f, -32.8876f, 46.1409f, 1.0f),
NvCloth::SimParticleFormat(-47.1333f, -31.568f, 45.6844f, 1.0f),
}};
EXPECT_THAT(newSkinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticlesResult));
}
TEST_F(NvClothActorClothSkinning, DISABLED_ActorClothSkinning_UpdateActorVisibility_ReturnsExpectedValues)
{
{
auto actor = AZStd::make_unique<ActorHelper>("actor_test");
auto meshNodeIndex = actor->AddJoint(MeshNodeName);
actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, MeshSkinningInfo));
actor->FinishSetup();
m_actorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
}
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
EXPECT_FALSE(actorClothSkinning->IsActorVisible());
EXPECT_FALSE(actorClothSkinning->WasActorVisible());
m_actorComponent->GetActorInstance()->SetIsVisible(true);
actorClothSkinning->UpdateActorVisibility();
EXPECT_TRUE(actorClothSkinning->IsActorVisible());
EXPECT_FALSE(actorClothSkinning->WasActorVisible());
actorClothSkinning->UpdateActorVisibility();
EXPECT_TRUE(actorClothSkinning->IsActorVisible());
EXPECT_TRUE(actorClothSkinning->WasActorVisible());
m_actorComponent->GetActorInstance()->SetIsVisible(false);
actorClothSkinning->UpdateActorVisibility();
EXPECT_FALSE(actorClothSkinning->IsActorVisible());
EXPECT_TRUE(actorClothSkinning->WasActorVisible());
actorClothSkinning->UpdateActorVisibility();
EXPECT_FALSE(actorClothSkinning->IsActorVisible());
EXPECT_FALSE(actorClothSkinning->WasActorVisible());
}
} // namespace UnitTest