diff --git a/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds.slice b/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds.slice
index c8709b3a6a..8d7605318e 100644
--- a/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds.slice
+++ b/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds.slice
@@ -39,13 +39,13 @@
+
-
@@ -188,6 +188,7 @@
+
diff --git a/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds_broken.slice b/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds_broken.slice
index e3ef330b3e..ff955f260e 100644
--- a/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds_broken.slice
+++ b/Gems/NvCloth/Assets/slices/Cloth/cloth_blinds_broken.slice
@@ -39,13 +39,13 @@
+
-
@@ -188,6 +188,7 @@
+
diff --git a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_four.slice b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_four.slice
index 0a32a4191a..d0bcd1296a 100644
--- a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_four.slice
+++ b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_four.slice
@@ -39,13 +39,13 @@
+
-
@@ -188,6 +188,7 @@
+
diff --git a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_two.slice b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_two.slice
index 369cb76841..8e07a65147 100644
--- a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_two.slice
+++ b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_corners_two.slice
@@ -39,13 +39,13 @@
+
-
@@ -188,6 +188,7 @@
+
diff --git a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_edge.slice b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_edge.slice
index b44a440d4d..a80058d820 100644
--- a/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_edge.slice
+++ b/Gems/NvCloth/Assets/slices/Cloth/cloth_locked_edge.slice
@@ -39,13 +39,13 @@
+
-
@@ -188,6 +188,7 @@
+
diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.cpp
index 411c727d33..88aae06bf2 100644
--- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.cpp
+++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.cpp
@@ -430,10 +430,12 @@ namespace NvCloth
AZStd::unique_ptr ActorClothSkinning::Create(
AZ::EntityId entityId,
const MeshNodeInfo& meshNodeInfo,
- const size_t numVertices,
+ const AZStd::vector& originalMeshParticles,
const size_t numSimulatedVertices,
const AZStd::vector& meshRemappedVertices)
{
+ const size_t numVertices = originalMeshParticles.size();
+
AZStd::vector skinningInfluences;
if (!Internal::ObtainSkinningInfluences(entityId, meshNodeInfo, numVertices, skinningInfluences))
{
@@ -480,11 +482,14 @@ namespace NvCloth
for (size_t vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex)
{
const int remappedIndex = meshRemappedVertices[vertexIndex];
+
if (remappedIndex >= 0)
{
actorClothSkinning->m_simulatedVertices[remappedIndex] = vertexIndex;
}
- else
+
+ if (remappedIndex < 0 ||
+ originalMeshParticles[vertexIndex].GetW() == 0.0f)
{
actorClothSkinning->m_nonSimulatedVertices.emplace_back(vertexIndex);
}
diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.h b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.h
index 01df1f87ef..1fd667b9c0 100644
--- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.h
+++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothSkinning.h
@@ -21,8 +21,6 @@
namespace NvCloth
{
- struct MeshNodeInfo;
-
//! One skinning influence of a vertex.
struct SkinningInfluence
{
@@ -45,7 +43,7 @@ namespace NvCloth
static AZStd::unique_ptr Create(
AZ::EntityId entityId,
const MeshNodeInfo& meshNodeInfo,
- const size_t numVertices,
+ const AZStd::vector& originalMeshParticles,
const size_t numSimulatedVertices,
const AZStd::vector& meshRemappedVertices);
diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp
index 52c93ea672..d88fcb65ce 100644
--- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp
+++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ClothComponentMesh.cpp
@@ -187,7 +187,7 @@ namespace NvCloth
m_actorClothSkinning = ActorClothSkinning::Create(
m_entityId,
m_meshNodeInfo,
- m_meshClothInfo.m_particles.size(),
+ m_meshClothInfo.m_particles,
m_cloth->GetParticles().size(),
m_meshRemappedVertices);
m_numberOfClothSkinningUpdates = 0;
@@ -406,7 +406,7 @@ namespace NvCloth
auto& renderData = GetRenderData();
- if (m_config.m_removeStaticTriangles && m_actorClothSkinning)
+ if (m_actorClothSkinning)
{
// Apply skinning to the non-simulated part of the mesh.
m_actorClothSkinning->ApplySkinningOnNonSimulatedVertices(m_meshClothInfo, renderData);
@@ -427,7 +427,15 @@ namespace NvCloth
if (remappedIndex >= 0)
{
renderData.m_particles[index] = particles[remappedIndex];
- renderData.m_normals[index] = normals[remappedIndex];
+
+ // For static particles only use the updated normal when indicated in the configuration.
+ const bool useSimulatedClothParticleNormal =
+ m_meshClothInfo.m_particles[index].GetW() != 0.0f ||
+ m_config.m_updateNormalsOfStaticParticles;
+ if (useSimulatedClothParticleNormal)
+ {
+ renderData.m_normals[index] = normals[remappedIndex];
+ }
}
}
diff --git a/Gems/NvCloth/Code/Source/Components/ClothConfiguration.cpp b/Gems/NvCloth/Code/Source/Components/ClothConfiguration.cpp
index 7173f78e92..164e0c62d8 100644
--- a/Gems/NvCloth/Code/Source/Components/ClothConfiguration.cpp
+++ b/Gems/NvCloth/Code/Source/Components/ClothConfiguration.cpp
@@ -74,6 +74,7 @@ namespace NvCloth
->Field("Solver Frequency", &ClothConfiguration::m_solverFrequency)
->Field("Acceleration Filter Iterations", &ClothConfiguration::m_accelerationFilterIterations)
->Field("Remove Static Triangles", &ClothConfiguration::m_removeStaticTriangles)
+ ->Field("Update Normals of Static Particles", &ClothConfiguration::m_updateNormalsOfStaticParticles)
;
}
}
diff --git a/Gems/NvCloth/Code/Source/Components/ClothConfiguration.h b/Gems/NvCloth/Code/Source/Components/ClothConfiguration.h
index e5a5bdb8e8..7c08b2bfb0 100644
--- a/Gems/NvCloth/Code/Source/Components/ClothConfiguration.h
+++ b/Gems/NvCloth/Code/Source/Components/ClothConfiguration.h
@@ -96,6 +96,7 @@ namespace NvCloth
float m_solverFrequency = 300.0f;
uint32_t m_accelerationFilterIterations = 30;
bool m_removeStaticTriangles = true;
+ bool m_updateNormalsOfStaticParticles = false;
// Fabric phases parameters
float m_horizontalStiffness = 1.0f;
diff --git a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp
index 1254a13cb2..7da677a7eb 100644
--- a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp
+++ b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp
@@ -374,12 +374,15 @@ namespace NvCloth
->DataElement(AZ::Edit::UIHandlers::Default, &ClothConfiguration::m_solverFrequency, "Solver frequency",
"Target solver iterations per second. At least 1 iteration per frame will be solved regardless of the value set.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->DataElement(AZ::Edit::UIHandlers::Default, &ClothConfiguration::m_accelerationFilterIterations, "Acceleration filter Iterations",
+ ->DataElement(AZ::Edit::UIHandlers::Default, &ClothConfiguration::m_accelerationFilterIterations, "Acceleration filter iterations",
"Number of iterations to average delta time factor used for gravity and external acceleration.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->DataElement(AZ::Edit::UIHandlers::Default, &ClothConfiguration::m_removeStaticTriangles, "Remove static triangles",
"Removing static triangles improves performance by not taking into account triangles whose particles are all static.\n"
"The removed static particles will not be present for collision or self collision during simulation.")
+ ->DataElement(AZ::Edit::UIHandlers::Default, &ClothConfiguration::m_updateNormalsOfStaticParticles, "Update normals of static particles",
+ "When enabled the normals of static particles will be updated according with the movement of the simulated mesh.\n"
+ "When disabled the static particles will keep the same normals as the original mesh.")
;
}
}
diff --git a/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp b/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp
index 00ce77c176..32e745f9cb 100644
--- a/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp
+++ b/Gems/NvCloth/Code/Source/Utils/MeshAssetHelper.cpp
@@ -172,6 +172,7 @@ namespace NvCloth
meshClothInfo.m_uvs.reserve(numTotalVertices);
meshClothInfo.m_motionConstraints.reserve(numTotalVertices);
meshClothInfo.m_backstopData.reserve(numTotalVertices);
+ meshClothInfo.m_normals.reserve(numTotalVertices);
meshClothInfo.m_indices.reserve(numTotalIndices);
struct Vec2
@@ -192,10 +193,15 @@ namespace NvCloth
{
const auto sourceIndices = mesh->GetIndexBufferTyped();
const auto sourcePositions = mesh->GetSemanticBufferTyped(AZ::Name("POSITION"));
+ const auto sourceNormals = mesh->GetSemanticBufferTyped(AZ::Name("NORMAL"));
const auto sourceClothData = mesh->GetSemanticBufferTyped(AZ::Name("CLOTH_DATA"));
const auto sourceUVs = mesh->GetSemanticBufferTyped(AZ::Name("UV"));
- if (sourceIndices.empty() || sourcePositions.empty() || sourceClothData.empty())
+ if (sourceIndices.empty() ||
+ sourcePositions.empty() ||
+ sourceNormals.empty() ||
+ sourceClothData.empty() ||
+ sourceUVs.empty())
{
return false;
}
@@ -214,6 +220,11 @@ namespace NvCloth
sourcePositions[index].z,
inverseMass);
+ meshClothInfo.m_normals.emplace_back(
+ sourceNormals[index].x,
+ sourceNormals[index].y,
+ sourceNormals[index].z);
+
meshClothInfo.m_motionConstraints.emplace_back(motionConstraint);
meshClothInfo.m_backstopData.emplace_back(backstopOffset, backstopRadius);
@@ -226,12 +237,12 @@ namespace NvCloth
meshClothInfo.m_indices.insert(meshClothInfo.m_indices.end(), sourceIndices.begin(), sourceIndices.end());
}
- // Calculate tangent space for the mesh.
- [[maybe_unused]] bool tangentSpaceCalculated =
- AZ::Interface::Get()->CalculateTangentSpace(
- meshClothInfo.m_particles, meshClothInfo.m_indices, meshClothInfo.m_uvs,
- meshClothInfo.m_tangents, meshClothInfo.m_bitangents, meshClothInfo.m_normals);
- AZ_Assert(tangentSpaceCalculated, "Failed to calculate tangent space.");
+ // Calculate tangents and bitangents for the whole mesh
+ [[maybe_unused]] bool tangentsAndBitangentsCalculated =
+ AZ::Interface::Get()->CalculateTangentsAndBitagents(
+ meshClothInfo.m_particles, meshClothInfo.m_indices, meshClothInfo.m_uvs, meshClothInfo.m_normals,
+ meshClothInfo.m_tangents, meshClothInfo.m_bitangents);
+ AZ_Assert(tangentsAndBitangentsCalculated, "Failed to calculate tangents and bitangents.");
return true;
}
diff --git a/Gems/NvCloth/Code/Tests/Components/ClothComponentMesh/ActorClothSkinningTest.cpp b/Gems/NvCloth/Code/Tests/Components/ClothComponentMesh/ActorClothSkinningTest.cpp
index 3fba04f695..818c36e7d2 100644
--- a/Gems/NvCloth/Code/Tests/Components/ClothComponentMesh/ActorClothSkinningTest.cpp
+++ b/Gems/NvCloth/Code/Tests/Components/ClothComponentMesh/ActorClothSkinningTest.cpp
@@ -36,6 +36,12 @@ namespace UnitTest
AZ::Vector3(1.0f, 0.0f, 0.0f),
AZ::Vector3(0.0f, 1.0f, 0.0f)
}};
+
+ const AZStd::vector MeshParticles = {{
+ NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[0], 1.0f),
+ NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[1], 1.0f),
+ NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[2], 1.0f),
+ }};
const AZStd::vector MeshIndices = {{
0, 1, 2
@@ -98,7 +104,7 @@ namespace UnitTest
{
AZ::EntityId entityId;
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(entityId, {}, 0, 0, {});
+ NvCloth::ActorClothSkinning::Create(entityId, {}, {}, 0, {});
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
@@ -107,7 +113,7 @@ namespace UnitTest
{
AZ::EntityId entityId;
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(entityId, MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(entityId, MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
@@ -122,7 +128,7 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), {}, 0, 0, {});
+ NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), {}, {}, 0, {});
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
@@ -139,7 +145,7 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
}
@@ -156,7 +162,7 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
EXPECT_TRUE(actorClothSkinning.get() != nullptr);
}
@@ -184,20 +190,15 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(actorComponent->GetEntityId(), MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
- const AZStd::vector clothParticles = {{
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[0], 1.0f),
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[1], 1.0f),
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[2], 1.0f),
- }};
- AZStd::vector skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
+ AZStd::vector skinnedClothParticles(MeshParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
- actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
+ actorClothSkinning->ApplySkinning(MeshParticles, skinnedClothParticles);
- EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
+ EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), MeshParticles));
// Update actor instance's joints transforms
const AZ::Transform newMeshNodeTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(180.0f));
@@ -205,10 +206,10 @@ namespace UnitTest
currentPose->SetLocalSpaceTransform(0, newMeshNodeTransform);
actorComponent->GetActorInstance()->UpdateSkinningMatrices();
- AZStd::vector newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
+ AZStd::vector newSkinnedClothParticles(MeshParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
- actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
+ actorClothSkinning->ApplySkinning(MeshParticles, newSkinnedClothParticles);
const AZ::Transform diffTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f));
const AZStd::vector clothParticlesResult = {{
@@ -245,20 +246,15 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
- const AZStd::vector clothParticles = {{
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[0], 1.0f),
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[1], 1.0f),
- NvCloth::SimParticleFormat::CreateFromVector3AndFloat(MeshVertices[2], 1.0f),
- }};
- AZStd::vector skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
+ AZStd::vector skinnedClothParticles(MeshParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
- actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
+ actorClothSkinning->ApplySkinning(MeshParticles, skinnedClothParticles);
- EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
+ EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), MeshParticles));
// 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));
@@ -268,10 +264,10 @@ namespace UnitTest
currentPose->SetLocalSpaceTransform(1, newJointChildTransform);
m_actorComponent->GetActorInstance()->UpdateSkinningMatrices();
- AZStd::vector newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
+ AZStd::vector newSkinnedClothParticles(MeshParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
actorClothSkinning->UpdateSkinning();
- actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
+ actorClothSkinning->ApplySkinning(MeshParticles, newSkinnedClothParticles);
const AZStd::vector clothParticlesResult = {{
NvCloth::SimParticleFormat(-48.4177f, -31.9446f, 45.2279f, 1.0f),
@@ -294,7 +290,7 @@ namespace UnitTest
}
AZStd::unique_ptr actorClothSkinning =
- NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
+ NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshParticles, MeshParticles.size(), MeshRemappedVertices);
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
EXPECT_FALSE(actorClothSkinning->IsActorVisible());