Performance pass to Cloth CPU Skinning
- Added operator+(Matrix3x4), operator*(float), RetrieveScaleSq and GetReciprocalScaled to Matrix3x4. Used by Cloth CPU Linear Skinning. These operation will be performant as they use SIMD. - Modified so there are no virtual functions calls at vertex level. - Caching indices to simplify the loop when applying skinning. - Caching static variables Matrix3x4 zero and DualQuaternion zero to avoid creating it for every vertex. - Removing branching to skip joints when the weight is zero, these cases are rarely and this improves performance by removing branching from loops at vertex level. - Changing skinning influences so it's a continuous block of memory. - Caching the vector size() if a variable instead of directly using it in a for loop.
This commit is contained in:
@@ -231,6 +231,18 @@ namespace AZ
|
||||
//! Compound assignment operator for matrix-matrix multiplication.
|
||||
Matrix3x4& operator*=(const Matrix3x4& rhs);
|
||||
|
||||
//! Operator for matrix-matrix addition.
|
||||
[[nodiscard]] Matrix3x4 operator+(const Matrix3x4& rhs) const;
|
||||
|
||||
//! Compound assignment operator for matrix-matrix addition.
|
||||
Matrix3x4& operator+=(const Matrix3x4& rhs);
|
||||
|
||||
//! Operator for multiplying all matrix's elements with a scalar
|
||||
[[nodiscard]] Matrix3x4 operator*(float scalar) const;
|
||||
|
||||
//! Compound assignment operator for multiplying all matrix's elements with a scalar
|
||||
Matrix3x4& operator*=(float scalar);
|
||||
|
||||
//! Operator for transforming a Vector3.
|
||||
[[nodiscard]] Vector3 operator*(const Vector3& rhs) const;
|
||||
|
||||
@@ -274,12 +286,18 @@ namespace AZ
|
||||
//! Gets the scale part of the transformation (the length of the basis vectors).
|
||||
[[nodiscard]] Vector3 RetrieveScale() const;
|
||||
|
||||
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
|
||||
[[nodiscard]] Vector3 RetrieveScaleSq() const;
|
||||
|
||||
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
|
||||
Vector3 ExtractScale();
|
||||
|
||||
//! Multiplies the basis vectors of the matrix by the elements of the scale specified.
|
||||
void MultiplyByScale(const Vector3& scale);
|
||||
|
||||
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
|
||||
[[nodiscard]] Matrix3x4 GetReciprocalScaled() const;
|
||||
|
||||
//! Tests if the 3x3 part of the matrix is orthogonal.
|
||||
bool IsOrthogonal(float tolerance = Constants::Tolerance) const;
|
||||
|
||||
|
||||
@@ -487,6 +487,43 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator+(const Matrix3x4& rhs) const
|
||||
{
|
||||
return Matrix3x4
|
||||
(
|
||||
Simd::Vec4::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
|
||||
Simd::Vec4::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
|
||||
Simd::Vec4::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator+=(const Matrix3x4& rhs)
|
||||
{
|
||||
*this = *this + rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(float scalar) const
|
||||
{
|
||||
const Vector4 vector4Scalar(scalar);
|
||||
return Matrix3x4
|
||||
(
|
||||
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), vector4Scalar.GetSimdValue()),
|
||||
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), vector4Scalar.GetSimdValue()),
|
||||
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), vector4Scalar.GetSimdValue())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(float scalar)
|
||||
{
|
||||
*this = *this * scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Vector3 Matrix3x4::operator*(const Vector3& rhs) const
|
||||
{
|
||||
return Vector3
|
||||
@@ -583,6 +620,12 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Vector3 Matrix3x4::RetrieveScaleSq() const
|
||||
{
|
||||
return Vector3(GetColumn(0).GetLengthSq(), GetColumn(1).GetLengthSq(), GetColumn(2).GetLengthSq());
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Vector3 Matrix3x4::ExtractScale()
|
||||
{
|
||||
const Vector3 scale = RetrieveScale();
|
||||
@@ -600,6 +643,14 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix3x4 Matrix3x4::GetReciprocalScaled() const
|
||||
{
|
||||
Matrix3x4 result = *this;
|
||||
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Matrix3x4::Orthogonalize()
|
||||
{
|
||||
*this = GetOrthogonalized();
|
||||
|
||||
@@ -484,6 +484,38 @@ namespace UnitTest
|
||||
EXPECT_TRUE(matrix5.IsClose(matrix1 * matrix4));
|
||||
}
|
||||
|
||||
TEST(MATH_Matrix3x4, AddMatrix3x4)
|
||||
{
|
||||
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(1.2f);
|
||||
const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.3f, 1.5f, 0.4f));
|
||||
const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f));
|
||||
const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateRotationX(-0.7f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f));
|
||||
AZ::Matrix3x4 matrix5 = matrix1;
|
||||
matrix5 += matrix4;
|
||||
EXPECT_THAT(matrix1 + (matrix2 + matrix3), IsClose((matrix1 + matrix2) + matrix3));
|
||||
EXPECT_THAT(matrix2 + AZ::Matrix3x4::CreateZero(), IsClose(matrix2));
|
||||
EXPECT_THAT(matrix3 + AZ::Matrix3x4::CreateZero(), IsClose(AZ::Matrix3x4::CreateZero() + matrix3));
|
||||
EXPECT_THAT(matrix3 + matrix3, IsClose(matrix3 * 2.0f));
|
||||
EXPECT_THAT(matrix5, IsClose(matrix1 + matrix4));
|
||||
}
|
||||
|
||||
TEST(MATH_Matrix3x4, MultiplyByScalar)
|
||||
{
|
||||
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
|
||||
const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
|
||||
const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
|
||||
const float scalar = 3.2f;
|
||||
const AZ::Vector4 row0Result = row0 * scalar;
|
||||
const AZ::Vector4 row1Result = row1 * scalar;
|
||||
const AZ::Vector4 row2Result = row2 * scalar;
|
||||
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
|
||||
EXPECT_THAT(matrix * 0.0f, IsClose(AZ::Matrix3x4::CreateZero()));
|
||||
EXPECT_THAT(matrix * 1.0f, IsClose(matrix));
|
||||
EXPECT_THAT(matrix * scalar, IsClose(AZ::Matrix3x4::CreateFromRows(row0Result, row1Result, row2Result)));
|
||||
EXPECT_THAT(matrix * 2.0f, IsClose(matrix + matrix));
|
||||
}
|
||||
|
||||
TEST(MATH_Matrix3x4, MultiplyByVector3)
|
||||
{
|
||||
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
|
||||
@@ -652,6 +684,34 @@ namespace UnitTest
|
||||
EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
|
||||
}
|
||||
|
||||
TEST_P(Matrix3x4ScaleFixture, ScaleSq)
|
||||
{
|
||||
const AZ::Matrix3x4 orthogonalMatrix = GetParam();
|
||||
EXPECT_THAT(orthogonalMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
|
||||
AZ::Matrix3x4 unscaledMatrix = orthogonalMatrix;
|
||||
unscaledMatrix.ExtractScale();
|
||||
EXPECT_THAT(unscaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
|
||||
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
|
||||
AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
|
||||
scaledMatrix.MultiplyByScale(scale);
|
||||
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scale * scale));
|
||||
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scaledMatrix.RetrieveScale() * scaledMatrix.RetrieveScale()));
|
||||
scaledMatrix.ExtractScale();
|
||||
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
|
||||
}
|
||||
|
||||
TEST_P(Matrix3x4ScaleFixture, GetReciprocalScaled)
|
||||
{
|
||||
const AZ::Matrix3x4 orthogonalMatrix = GetParam();
|
||||
EXPECT_THAT(orthogonalMatrix.GetReciprocalScaled(), IsClose(orthogonalMatrix));
|
||||
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
|
||||
AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
|
||||
scaledMatrix.MultiplyByScale(scale);
|
||||
AZ::Matrix3x4 reciprocalScaledMatrix = orthogonalMatrix;
|
||||
reciprocalScaledMatrix.MultiplyByScale(scale.GetReciprocal());
|
||||
EXPECT_THAT(scaledMatrix.GetReciprocalScaled(), IsClose(reciprocalScaledMatrix));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4ScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
|
||||
|
||||
TEST(MATH_Matrix3x4, IsOrthogonal)
|
||||
|
||||
@@ -27,11 +27,11 @@ namespace NvCloth
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
bool ObtainSkinningData(
|
||||
bool ObtainSkinningInfluences(
|
||||
AZ::EntityId entityId,
|
||||
const MeshNodeInfo& meshNodeInfo,
|
||||
const size_t numSimParticles,
|
||||
AZStd::vector<SkinningInfo>& skinningData)
|
||||
const size_t numVertices,
|
||||
AZStd::vector<SkinningInfluence>& skinningInfluences)
|
||||
{
|
||||
AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset;
|
||||
AZ::Render::MeshComponentRequestBus::EventResult(
|
||||
@@ -67,7 +67,7 @@ namespace NvCloth
|
||||
|
||||
const auto& skinToSkeletonIndexMap = actor->GetSkinToSkeletonIndexMap();
|
||||
|
||||
skinningData.resize(numSimParticles);
|
||||
size_t numberOfInfluencesPerVertex = 0;
|
||||
|
||||
// For each submesh...
|
||||
for (const auto& subMeshInfo : meshNodeInfo.m_subMeshes)
|
||||
@@ -98,28 +98,48 @@ namespace NvCloth
|
||||
|
||||
if (sourceSkinJointIndices.empty() || sourceSkinWeights.empty())
|
||||
{
|
||||
continue;
|
||||
// Ignoring skinning when there is no skin data.
|
||||
// All submeshes will either have or not have skin data, since they come from the same mesh.
|
||||
return false;
|
||||
}
|
||||
AZ_Assert(sourceSkinJointIndices.size() == sourceSkinWeights.size(),
|
||||
"Size of skin joint indices buffer (%zu) different from skin weights buffer (%zu)",
|
||||
sourceSkinJointIndices.size(), sourceSkinWeights.size());
|
||||
|
||||
const size_t influenceCount = sourceSkinWeights.size() / sourcePositions.size();
|
||||
if (influenceCount == 0)
|
||||
const size_t subMeshInfluenceCount = sourceSkinWeights.size() / sourcePositions.size();
|
||||
AZ_Assert(subMeshInfluenceCount > 0,
|
||||
"Submesh %d skinning data has zero joint influences per vertex.",
|
||||
subMeshInfo.m_primitiveIndex);
|
||||
|
||||
if (numberOfInfluencesPerVertex == 0)
|
||||
{
|
||||
continue;
|
||||
// Resize only in the first loop once we know the number of influences per vertex.
|
||||
// The other submeshes should match the number of influences.
|
||||
numberOfInfluencesPerVertex = subMeshInfluenceCount;
|
||||
skinningInfluences.resize(numVertices * numberOfInfluencesPerVertex);
|
||||
}
|
||||
else if (subMeshInfluenceCount != numberOfInfluencesPerVertex)
|
||||
{
|
||||
AZ_Error("ActorClothSkinning", false,
|
||||
"Submesh %d number of influences (%d) is different from a previous submesh (%d).",
|
||||
subMeshInfo.m_primitiveIndex,
|
||||
subMeshInfluenceCount,
|
||||
numberOfInfluencesPerVertex);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int vertexIndex = 0; vertexIndex < subMeshInfo.m_numVertices; ++vertexIndex)
|
||||
{
|
||||
SkinningInfo& skinningInfo = skinningData[subMeshInfo.m_verticesFirstIndex + vertexIndex];
|
||||
skinningInfo.m_jointIndices.resize(influenceCount);
|
||||
skinningInfo.m_jointWeights.resize(influenceCount);
|
||||
const size_t subMeshVertexIndex = vertexIndex * numberOfInfluencesPerVertex;
|
||||
const size_t meshVertexIndex = (subMeshInfo.m_verticesFirstIndex + vertexIndex) * numberOfInfluencesPerVertex;
|
||||
|
||||
for (size_t influenceIndex = 0; influenceIndex < influenceCount; ++influenceIndex)
|
||||
for (size_t influenceIndex = 0; influenceIndex < numberOfInfluencesPerVertex; ++influenceIndex)
|
||||
{
|
||||
const AZ::u16 jointIndex = sourceSkinJointIndices[vertexIndex * influenceCount + influenceIndex];
|
||||
const float weight = sourceSkinWeights[vertexIndex * influenceCount + influenceIndex];
|
||||
const size_t subMeshVertexInfluenceIndex = subMeshVertexIndex + influenceIndex;
|
||||
const size_t meshVertexInfluenceIndex = meshVertexIndex + influenceIndex;
|
||||
|
||||
const AZ::u16 jointIndex = sourceSkinJointIndices[subMeshVertexInfluenceIndex];
|
||||
const float weight = sourceSkinWeights[subMeshVertexInfluenceIndex];
|
||||
|
||||
auto skeletonIndexIt = skinToSkeletonIndexMap.find(jointIndex);
|
||||
if (skeletonIndexIt == skinToSkeletonIndexMap.end())
|
||||
@@ -130,8 +150,8 @@ namespace NvCloth
|
||||
return false;
|
||||
}
|
||||
|
||||
skinningInfo.m_jointIndices[influenceIndex] = skeletonIndexIt->second;
|
||||
skinningInfo.m_jointWeights[influenceIndex] = weight;
|
||||
skinningInfluences[meshVertexInfluenceIndex].m_jointIndex = skeletonIndexIt->second;
|
||||
skinningInfluences[meshVertexInfluenceIndex].m_jointWeight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,17 +218,21 @@ namespace NvCloth
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// ActorClothSkinning overrides ...
|
||||
void UpdateSkinning() override;
|
||||
bool HasSkinningTransformData() override;
|
||||
void ComputeVertexSkinnningTransform(const SkinningInfo& skinningInfo) override;
|
||||
AZ::Vector3 ComputeSkinningPosition(const AZ::Vector3& originalPosition) override;
|
||||
AZ::Vector3 ComputeSkinningVector(const AZ::Vector3& originalVector) override;
|
||||
void ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions) override;
|
||||
void ApplySkinningOnNonSimulatedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData) override;
|
||||
|
||||
private:
|
||||
AZ::Matrix3x4 ComputeVertexSkinnningTransform(AZ::u32 vertexIndex);
|
||||
|
||||
const AZ::Matrix3x4* m_skinningMatrices = nullptr;
|
||||
AZ::Matrix3x4 m_vertexSkinningTransform = AZ::Matrix3x4::CreateIdentity();
|
||||
|
||||
inline static const AZ::Matrix3x4 s_zeroMatrix3x4 = AZ::Matrix3x4::CreateZero();
|
||||
};
|
||||
|
||||
void ActorClothSkinningLinear::UpdateSkinning()
|
||||
@@ -218,41 +242,75 @@ namespace NvCloth
|
||||
m_skinningMatrices = Internal::ObtainSkinningMatrices(m_entityId);
|
||||
}
|
||||
|
||||
bool ActorClothSkinningLinear::HasSkinningTransformData()
|
||||
void ActorClothSkinningLinear::ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions)
|
||||
{
|
||||
return m_skinningMatrices != nullptr;
|
||||
}
|
||||
|
||||
void ActorClothSkinningLinear::ComputeVertexSkinnningTransform(const SkinningInfo& skinningInfo)
|
||||
{
|
||||
m_vertexSkinningTransform = AZ::Matrix3x4::CreateZero();
|
||||
for (size_t weightIndex = 0; weightIndex < skinningInfo.m_jointWeights.size(); ++weightIndex)
|
||||
if (!m_skinningMatrices ||
|
||||
originalPositions.empty() ||
|
||||
originalPositions.size() != positions.size() ||
|
||||
originalPositions.size() != m_simulatedVertices.size())
|
||||
{
|
||||
const AZ::u16 jointIndex = skinningInfo.m_jointIndices[weightIndex];
|
||||
const float jointWeight = skinningInfo.m_jointWeights[weightIndex];
|
||||
return;
|
||||
}
|
||||
|
||||
if (AZ::IsClose(jointWeight, 0.0f))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
// Blending matrices the same way done in GPU shaders, by adding each weighted matrix element by element.
|
||||
// This way the skinning results are much similar to the skinning performed in GPU.
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
m_vertexSkinningTransform.SetRow(i, m_vertexSkinningTransform.GetRow(i) + m_skinningMatrices[jointIndex].GetRow(i) * jointWeight);
|
||||
}
|
||||
const size_t vertexCount = m_simulatedVertices.size();
|
||||
for (size_t index = 0; index < vertexCount; ++index)
|
||||
{
|
||||
const AZ::Matrix3x4 vertexSkinningTransform = ComputeVertexSkinnningTransform(m_simulatedVertices[index]);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = vertexSkinningTransform * originalPositions[index].GetAsVector3();
|
||||
positions[index].Set(skinnedPosition, positions[index].GetW()); // Avoid overwriting the w component
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 ActorClothSkinningLinear::ComputeSkinningPosition(const AZ::Vector3& originalPosition)
|
||||
void ActorClothSkinningLinear::ApplySkinningOnNonSimulatedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData)
|
||||
{
|
||||
return m_vertexSkinningTransform * originalPosition;
|
||||
if (!m_skinningMatrices ||
|
||||
originalData.m_particles.empty() ||
|
||||
originalData.m_particles.size() != renderData.m_particles.size() ||
|
||||
originalData.m_particles.size() != m_skinningInfluences.size() / m_numberOfInfluencesPerVertex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
for (const AZ::u32 index : m_nonSimulatedVertices)
|
||||
{
|
||||
const AZ::Matrix3x4 vertexSkinningTransform = ComputeVertexSkinnningTransform(index);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = vertexSkinningTransform * originalData.m_particles[index].GetAsVector3();
|
||||
renderData.m_particles[index].Set(skinnedPosition, renderData.m_particles[index].GetW()); // Avoid overwriting the w component
|
||||
|
||||
// Calculate the reciprocal scale version of the matrix to transform the vectors.
|
||||
const AZ::Matrix3x4 vertexSkinningTransformReciprocalScale = vertexSkinningTransform.GetReciprocalScaled();
|
||||
|
||||
renderData.m_tangents[index] = vertexSkinningTransformReciprocalScale.TransformVector(originalData.m_tangents[index]).GetNormalized();
|
||||
renderData.m_bitangents[index] = vertexSkinningTransformReciprocalScale.TransformVector(originalData.m_bitangents[index]).GetNormalized();
|
||||
renderData.m_normals[index] = vertexSkinningTransformReciprocalScale.TransformVector(originalData.m_normals[index]).GetNormalized();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 ActorClothSkinningLinear::ComputeSkinningVector(const AZ::Vector3& originalVector)
|
||||
AZ::Matrix3x4 ActorClothSkinningLinear::ComputeVertexSkinnningTransform(AZ::u32 vertexIndex)
|
||||
{
|
||||
return (m_vertexSkinningTransform * AZ::Vector4::CreateFromVector3AndFloat(originalVector, 0.0f)).GetAsVector3().GetNormalized();
|
||||
AZ::Matrix3x4 vertexSkinningTransform = s_zeroMatrix3x4;
|
||||
for (size_t influenceIndex = 0; influenceIndex < m_numberOfInfluencesPerVertex; ++influenceIndex)
|
||||
{
|
||||
const size_t vertexInfluenceIndex = vertexIndex * m_numberOfInfluencesPerVertex + influenceIndex;
|
||||
|
||||
const AZ::u16 jointIndex = m_skinningInfluences[vertexInfluenceIndex].m_jointIndex;
|
||||
const float jointWeight = m_skinningInfluences[vertexInfluenceIndex].m_jointWeight;
|
||||
|
||||
// Blending matrices the same way done in GPU shaders, by adding each weighted matrix element by element.
|
||||
// This way the skinning results are much similar to the skinning performed in GPU.
|
||||
vertexSkinningTransform += m_skinningMatrices[jointIndex] * jointWeight;
|
||||
}
|
||||
return vertexSkinningTransform;
|
||||
}
|
||||
|
||||
// Specialized class that applies dual quaternion blending skinning
|
||||
@@ -265,17 +323,21 @@ namespace NvCloth
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// ActorClothSkinning overrides ...
|
||||
void UpdateSkinning() override;
|
||||
bool HasSkinningTransformData() override;
|
||||
void ComputeVertexSkinnningTransform(const SkinningInfo& skinningInfo) override;
|
||||
AZ::Vector3 ComputeSkinningPosition(const AZ::Vector3& originalPosition) override;
|
||||
AZ::Vector3 ComputeSkinningVector(const AZ::Vector3& originalVector) override;
|
||||
void ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions) override;
|
||||
void ApplySkinningOnNonSimulatedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData) override;
|
||||
|
||||
private:
|
||||
MCore::DualQuaternion ComputeVertexSkinnningTransform(AZ::u32 vertexIndex);
|
||||
|
||||
AZStd::unordered_map<AZ::u16, MCore::DualQuaternion> m_skinningDualQuaternions;
|
||||
MCore::DualQuaternion m_vertexSkinningTransform;
|
||||
|
||||
inline static const MCore::DualQuaternion s_zeroDualQuaternion = MCore::DualQuaternion(AZ::Quaternion::CreateZero(), AZ::Quaternion::CreateZero());
|
||||
};
|
||||
|
||||
void ActorClothSkinningDualQuaternion::UpdateSkinning()
|
||||
@@ -285,58 +347,93 @@ namespace NvCloth
|
||||
m_skinningDualQuaternions = Internal::ObtainSkinningDualQuaternions(m_entityId, m_jointIndices);
|
||||
}
|
||||
|
||||
bool ActorClothSkinningDualQuaternion::HasSkinningTransformData()
|
||||
void ActorClothSkinningDualQuaternion::ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions)
|
||||
{
|
||||
return !m_skinningDualQuaternions.empty();
|
||||
}
|
||||
|
||||
void ActorClothSkinningDualQuaternion::ComputeVertexSkinnningTransform(const SkinningInfo& skinningInfo)
|
||||
{
|
||||
m_vertexSkinningTransform = MCore::DualQuaternion(AZ::Quaternion::CreateZero(), AZ::Quaternion::CreateZero());
|
||||
for (size_t weightIndex = 0; weightIndex < skinningInfo.m_jointWeights.size(); ++weightIndex)
|
||||
if (m_skinningDualQuaternions.empty() ||
|
||||
originalPositions.empty() ||
|
||||
originalPositions.size() != positions.size() ||
|
||||
originalPositions.size() != m_simulatedVertices.size())
|
||||
{
|
||||
const AZ::u16 jointIndex = skinningInfo.m_jointIndices[weightIndex];
|
||||
const float jointWeight = skinningInfo.m_jointWeights[weightIndex];
|
||||
|
||||
if (AZ::IsClose(jointWeight, 0.0f))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vertexSkinningTransform += m_skinningDualQuaternions.at(jointIndex) * jointWeight;
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
const size_t vertexCount = m_simulatedVertices.size();
|
||||
for (size_t index = 0; index < vertexCount; ++index)
|
||||
{
|
||||
const MCore::DualQuaternion vertexSkinningTransform = ComputeVertexSkinnningTransform(m_simulatedVertices[index]);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = vertexSkinningTransform.TransformPoint(originalPositions[index].GetAsVector3());
|
||||
positions[index].Set(skinnedPosition, positions[index].GetW()); // Avoid overwriting the w component
|
||||
}
|
||||
m_vertexSkinningTransform.Normalize();
|
||||
}
|
||||
|
||||
AZ::Vector3 ActorClothSkinningDualQuaternion::ComputeSkinningPosition(const AZ::Vector3& originalPosition)
|
||||
void ActorClothSkinningDualQuaternion::ApplySkinningOnNonSimulatedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData)
|
||||
{
|
||||
return m_vertexSkinningTransform.TransformPoint(originalPosition);
|
||||
if (m_skinningDualQuaternions.empty() ||
|
||||
originalData.m_particles.empty() ||
|
||||
originalData.m_particles.size() != renderData.m_particles.size() ||
|
||||
originalData.m_particles.size() != m_skinningInfluences.size() / m_numberOfInfluencesPerVertex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
for (const AZ::u32 index : m_nonSimulatedVertices)
|
||||
{
|
||||
const MCore::DualQuaternion vertexSkinningTransform = ComputeVertexSkinnningTransform(index);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = vertexSkinningTransform.TransformPoint(originalData.m_particles[index].GetAsVector3());
|
||||
renderData.m_particles[index].Set(skinnedPosition, renderData.m_particles[index].GetW()); // Avoid overwriting the w component
|
||||
|
||||
// ComputeVertexSkinnningTransform is normalizing the dual quaternion, so it won't have scale
|
||||
// and there is no need to compute the reciprocal scale version for transforming vectors.
|
||||
|
||||
renderData.m_tangents[index] = vertexSkinningTransform.TransformVector(originalData.m_tangents[index]).GetNormalized();
|
||||
renderData.m_bitangents[index] = vertexSkinningTransform.TransformVector(originalData.m_bitangents[index]).GetNormalized();
|
||||
renderData.m_normals[index] = vertexSkinningTransform.TransformVector(originalData.m_normals[index]).GetNormalized();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 ActorClothSkinningDualQuaternion::ComputeSkinningVector(const AZ::Vector3& originalVector)
|
||||
MCore::DualQuaternion ActorClothSkinningDualQuaternion::ComputeVertexSkinnningTransform(AZ::u32 vertexIndex)
|
||||
{
|
||||
return m_vertexSkinningTransform.TransformVector(originalVector).GetNormalized();
|
||||
MCore::DualQuaternion vertexSkinningTransform = s_zeroDualQuaternion;
|
||||
for (size_t influenceIndex = 0; influenceIndex < m_numberOfInfluencesPerVertex; ++influenceIndex)
|
||||
{
|
||||
const size_t vertexInfluenceIndex = vertexIndex * m_numberOfInfluencesPerVertex + influenceIndex;
|
||||
|
||||
const AZ::u16 jointIndex = m_skinningInfluences[vertexInfluenceIndex].m_jointIndex;
|
||||
const float jointWeight = m_skinningInfluences[vertexInfluenceIndex].m_jointWeight;
|
||||
|
||||
const MCore::DualQuaternion& skinningDualQuaternion = m_skinningDualQuaternions.at(jointIndex);
|
||||
|
||||
float flip = AZ::GetSign(vertexSkinningTransform.mReal.Dot(skinningDualQuaternion.mReal));
|
||||
vertexSkinningTransform += skinningDualQuaternion * jointWeight * flip;
|
||||
}
|
||||
// Normalizing the dual quaternion as the GPU shaders do. This will remove the scale from the transform.
|
||||
vertexSkinningTransform.Normalize();
|
||||
return vertexSkinningTransform;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ActorClothSkinning> ActorClothSkinning::Create(
|
||||
AZ::EntityId entityId,
|
||||
const MeshNodeInfo& meshNodeInfo,
|
||||
const size_t numSimParticles)
|
||||
const size_t numVertices,
|
||||
const size_t numSimulatedVertices,
|
||||
const AZStd::vector<int>& meshRemappedVertices)
|
||||
{
|
||||
AZStd::vector<SkinningInfo> skinningData;
|
||||
if (!Internal::ObtainSkinningData(entityId, meshNodeInfo, numSimParticles, skinningData))
|
||||
AZStd::vector<SkinningInfluence> skinningInfluences;
|
||||
if (!Internal::ObtainSkinningInfluences(entityId, meshNodeInfo, numVertices, skinningInfluences))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (numSimParticles != skinningData.size())
|
||||
{
|
||||
AZ_Error("ActorClothSkinning", false,
|
||||
"Number of simulation particles (%zu) doesn't match with skinning data obtained (%zu)",
|
||||
numSimParticles, skinningData.size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ActorClothSkinning> actorClothSkinning;
|
||||
const auto skinningMethod = Internal::ObtainSkinningMethod(entityId);
|
||||
switch (skinningMethod)
|
||||
@@ -355,27 +452,40 @@ namespace NvCloth
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Insert the indices of the joints that influence the particle (weight is not 0)
|
||||
AZStd::set<AZ::u16> jointIndices;
|
||||
for (size_t particleIndex = 0; particleIndex < numSimParticles; ++particleIndex)
|
||||
actorClothSkinning->m_numberOfInfluencesPerVertex = skinningInfluences.size() / numVertices;
|
||||
if (actorClothSkinning->m_numberOfInfluencesPerVertex == 0)
|
||||
{
|
||||
const SkinningInfo& skinningInfo = skinningData[particleIndex];
|
||||
for (size_t weightIndex = 0; weightIndex < skinningInfo.m_jointWeights.size(); ++weightIndex)
|
||||
{
|
||||
const AZ::u16 jointIndex = skinningInfo.m_jointIndices[weightIndex];
|
||||
const float jointWeight = skinningInfo.m_jointWeights[weightIndex];
|
||||
AZ_Error("ActorClothSkinning", false,
|
||||
"Number of skinning joint influences per vertex is zero.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (AZ::IsClose(jointWeight, 0.0f))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
jointIndices.insert(jointIndex);
|
||||
}
|
||||
// Collect all indices of the joints that influence the vertices
|
||||
AZStd::set<AZ::u16> jointIndices;
|
||||
for (const auto& skinningInfluence : skinningInfluences)
|
||||
{
|
||||
jointIndices.insert(skinningInfluence.m_jointIndex);
|
||||
}
|
||||
actorClothSkinning->m_jointIndices.assign(jointIndices.begin(), jointIndices.end());
|
||||
|
||||
actorClothSkinning->m_skinningData = AZStd::move(skinningData);
|
||||
// Collect the indices for simulated and non-simulated vertices
|
||||
actorClothSkinning->m_simulatedVertices.resize(numSimulatedVertices);
|
||||
actorClothSkinning->m_nonSimulatedVertices.reserve(numVertices);
|
||||
for (size_t vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex)
|
||||
{
|
||||
const int remappedIndex = meshRemappedVertices[vertexIndex];
|
||||
if (remappedIndex >= 0)
|
||||
{
|
||||
actorClothSkinning->m_simulatedVertices[remappedIndex] = vertexIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
actorClothSkinning->m_nonSimulatedVertices.emplace_back(vertexIndex);
|
||||
}
|
||||
}
|
||||
actorClothSkinning->m_nonSimulatedVertices.shrink_to_fit();
|
||||
|
||||
actorClothSkinning->m_skinningInfluences = AZStd::move(skinningInfluences);
|
||||
|
||||
return actorClothSkinning;
|
||||
}
|
||||
@@ -385,69 +495,6 @@ namespace NvCloth
|
||||
{
|
||||
}
|
||||
|
||||
void ActorClothSkinning::ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions,
|
||||
const AZStd::vector<int>& meshRemappedVertices)
|
||||
{
|
||||
if (!HasSkinningTransformData() ||
|
||||
originalPositions.empty() ||
|
||||
originalPositions.size() != positions.size() ||
|
||||
m_skinningData.size() != meshRemappedVertices.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
AZStd::unordered_set<int> skinnedIndices;
|
||||
for (size_t index = 0; index < meshRemappedVertices.size(); ++index)
|
||||
{
|
||||
const int remappedIndex = meshRemappedVertices[index];
|
||||
if (remappedIndex >= 0 && !skinnedIndices.contains(remappedIndex))
|
||||
{
|
||||
ComputeVertexSkinnningTransform(m_skinningData[index]);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = ComputeSkinningPosition(originalPositions[remappedIndex].GetAsVector3());
|
||||
positions[remappedIndex].Set(skinnedPosition, positions[remappedIndex].GetW()); // Avoid overwriting the w component
|
||||
|
||||
skinnedIndices.emplace(remappedIndex); // Avoid computing this index again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorClothSkinning::ApplySkinninOnRemovedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData,
|
||||
const AZStd::vector<int>& meshRemappedVertices)
|
||||
{
|
||||
if (!HasSkinningTransformData() ||
|
||||
originalData.m_particles.empty() ||
|
||||
originalData.m_particles.size() != renderData.m_particles.size() ||
|
||||
originalData.m_particles.size() != m_skinningData.size() ||
|
||||
m_skinningData.size() != meshRemappedVertices.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
|
||||
|
||||
for (size_t index = 0; index < originalData.m_particles.size(); ++index)
|
||||
{
|
||||
if (meshRemappedVertices[index] < 0)
|
||||
{
|
||||
ComputeVertexSkinnningTransform(m_skinningData[index]);
|
||||
|
||||
const AZ::Vector3 skinnedPosition = ComputeSkinningPosition(originalData.m_particles[index].GetAsVector3());
|
||||
renderData.m_particles[index].Set(skinnedPosition, renderData.m_particles[index].GetW()); // Avoid overwriting the w component
|
||||
|
||||
renderData.m_tangents[index] = ComputeSkinningVector(originalData.m_tangents[index]);
|
||||
renderData.m_bitangents[index] = ComputeSkinningVector(originalData.m_bitangents[index]);
|
||||
renderData.m_normals[index] = ComputeSkinningVector(originalData.m_normals[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorClothSkinning::UpdateActorVisibility()
|
||||
{
|
||||
bool isVisible = true;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
|
||||
#include <NvCloth/Types.h>
|
||||
@@ -22,14 +23,14 @@ namespace NvCloth
|
||||
{
|
||||
struct MeshNodeInfo;
|
||||
|
||||
//! Skinning information of a particle.
|
||||
struct SkinningInfo
|
||||
//! One skinning influence of a vertex.
|
||||
struct SkinningInfluence
|
||||
{
|
||||
//! Weights of each joint that influence the particle.
|
||||
AZStd::vector<float> m_jointWeights;
|
||||
//! Weight of the joint that influences the vertex.
|
||||
float m_jointWeight = 0.0f;
|
||||
|
||||
//! List of joints that influence the particle.
|
||||
AZStd::vector<AZ::u16> m_jointIndices;
|
||||
//! Index of the joint that influences the vertex.
|
||||
AZ::u16 m_jointIndex = AZStd::numeric_limits<AZ::u16>::max();
|
||||
};
|
||||
|
||||
//! Class to retrieve skinning information from an actor on the same entity
|
||||
@@ -44,7 +45,9 @@ namespace NvCloth
|
||||
static AZStd::unique_ptr<ActorClothSkinning> Create(
|
||||
AZ::EntityId entityId,
|
||||
const MeshNodeInfo& meshNodeInfo,
|
||||
const size_t numSimParticles);
|
||||
const size_t numVertices,
|
||||
const size_t numSimulatedVertices,
|
||||
const AZStd::vector<int>& meshRemappedVertices);
|
||||
|
||||
explicit ActorClothSkinning(AZ::EntityId entityId);
|
||||
|
||||
@@ -53,17 +56,15 @@ namespace NvCloth
|
||||
|
||||
//! Applies skinning to a list of positions.
|
||||
//! @note w components are not affected.
|
||||
void ApplySkinning(
|
||||
virtual void ApplySkinning(
|
||||
const AZStd::vector<AZ::Vector4>& originalPositions,
|
||||
AZStd::vector<AZ::Vector4>& positions,
|
||||
const AZStd::vector<int>& meshRemappedVertices);
|
||||
AZStd::vector<AZ::Vector4>& positions) = 0;
|
||||
|
||||
//! Applies skinning to a list of positions and vectors whose vertices
|
||||
//! have not been used for simulation (remapped index is negative).
|
||||
void ApplySkinninOnRemovedVertices(
|
||||
//! have not been used for simulation.
|
||||
virtual void ApplySkinningOnNonSimulatedVertices(
|
||||
const MeshClothInfo& originalData,
|
||||
ClothComponentMesh::RenderData& renderData,
|
||||
const AZStd::vector<int>& meshRemappedVertices);
|
||||
ClothComponentMesh::RenderData& renderData) = 0;
|
||||
|
||||
//! Updates visibility variables.
|
||||
void UpdateActorVisibility();
|
||||
@@ -75,24 +76,20 @@ namespace NvCloth
|
||||
bool WasActorVisible() const;
|
||||
|
||||
protected:
|
||||
//! Returns true if it has valid skinning trasform data.
|
||||
virtual bool HasSkinningTransformData() = 0;
|
||||
|
||||
//! Computes the skinnning transformation to apply to a vertex data.
|
||||
virtual void ComputeVertexSkinnningTransform(const SkinningInfo& skinningInfo) = 0;
|
||||
|
||||
//! Computes skinning on a position.
|
||||
virtual AZ::Vector3 ComputeSkinningPosition(const AZ::Vector3& originalPosition) = 0;
|
||||
|
||||
//! Computes skinning on a vector.
|
||||
virtual AZ::Vector3 ComputeSkinningVector(const AZ::Vector3& originalVector) = 0;
|
||||
|
||||
AZ::EntityId m_entityId;
|
||||
|
||||
// Skinning information of all particles
|
||||
AZStd::vector<SkinningInfo> m_skinningData;
|
||||
size_t m_numberOfInfluencesPerVertex = 0;
|
||||
|
||||
// Collection of skeleton joint indices that influence the particles
|
||||
// Skinning influences of all vertices
|
||||
AZStd::vector<SkinningInfluence> m_skinningInfluences;
|
||||
|
||||
// Indices to skinning influences that are part of the simulation
|
||||
AZStd::vector<AZ::u32> m_simulatedVertices;
|
||||
|
||||
// Indices to skinning influences that are not part of the simulation
|
||||
AZStd::vector<AZ::u32> m_nonSimulatedVertices;
|
||||
|
||||
// Collection of skeleton joint indices that influence the vertices
|
||||
AZStd::vector<AZ::u16> m_jointIndices;
|
||||
|
||||
// Visibility variables
|
||||
|
||||
@@ -184,7 +184,12 @@ namespace NvCloth
|
||||
m_actorClothColliders = ActorClothColliders::Create(m_entityId);
|
||||
|
||||
// It will return a valid instance if it's an actor with skinning data.
|
||||
m_actorClothSkinning = ActorClothSkinning::Create(m_entityId, m_meshNodeInfo, m_meshClothInfo.m_particles.size());
|
||||
m_actorClothSkinning = ActorClothSkinning::Create(
|
||||
m_entityId,
|
||||
m_meshNodeInfo,
|
||||
m_meshClothInfo.m_particles.size(),
|
||||
m_cloth->GetParticles().size(),
|
||||
m_meshRemappedVertices);
|
||||
m_numberOfClothSkinningUpdates = 0;
|
||||
|
||||
m_clothConstraints = ClothConstraints::Create(
|
||||
@@ -363,7 +368,7 @@ namespace NvCloth
|
||||
{
|
||||
// Update skinning for all particles and apply it to cloth
|
||||
AZStd::vector<SimParticleFormat> particles = m_cloth->GetParticles();
|
||||
m_actorClothSkinning->ApplySkinning(m_cloth->GetInitialParticles(), particles, m_meshRemappedVertices);
|
||||
m_actorClothSkinning->ApplySkinning(m_cloth->GetInitialParticles(), particles);
|
||||
m_cloth->SetParticles(AZStd::move(particles));
|
||||
m_cloth->DiscardParticleDelta();
|
||||
}
|
||||
@@ -379,8 +384,8 @@ namespace NvCloth
|
||||
|
||||
if (m_actorClothSkinning)
|
||||
{
|
||||
m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetMotionConstraints(), m_motionConstraints, m_meshRemappedVertices);
|
||||
m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetSeparationConstraints(), m_separationConstraints, m_meshRemappedVertices);
|
||||
m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetMotionConstraints(), m_motionConstraints);
|
||||
m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetSeparationConstraints(), m_separationConstraints);
|
||||
}
|
||||
|
||||
m_cloth->GetClothConfigurator()->SetMotionConstraints(m_motionConstraints);
|
||||
@@ -404,7 +409,7 @@ namespace NvCloth
|
||||
if (m_config.m_removeStaticTriangles && m_actorClothSkinning)
|
||||
{
|
||||
// Apply skinning to the non-simulated part of the mesh.
|
||||
m_actorClothSkinning->ApplySkinninOnRemovedVertices(m_meshClothInfo, renderData, m_meshRemappedVertices);
|
||||
m_actorClothSkinning->ApplySkinningOnNonSimulatedVertices(m_meshClothInfo, renderData);
|
||||
}
|
||||
|
||||
// Calculate normals of the cloth particles (simplified mesh).
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace UnitTest
|
||||
{
|
||||
AZ::EntityId entityId;
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(entityId, {}, 0);
|
||||
NvCloth::ActorClothSkinning::Create(entityId, {}, 0, 0, {});
|
||||
|
||||
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace UnitTest
|
||||
{
|
||||
AZ::EntityId entityId;
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(entityId, MeshNodeInfo, MeshRemappedVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(entityId, MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
|
||||
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), {}, 0);
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), {}, 0, 0, {});
|
||||
|
||||
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
|
||||
EXPECT_TRUE(actorClothSkinning.get() == nullptr);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
|
||||
EXPECT_TRUE(actorClothSkinning.get() != nullptr);
|
||||
}
|
||||
@@ -184,7 +184,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
|
||||
|
||||
const AZStd::vector<NvCloth::SimParticleFormat> clothParticles = {{
|
||||
@@ -195,7 +195,7 @@ namespace UnitTest
|
||||
AZStd::vector<NvCloth::SimParticleFormat> skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
actorClothSkinning->UpdateSkinning();
|
||||
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles, MeshRemappedVertices);
|
||||
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
|
||||
|
||||
EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace UnitTest
|
||||
AZStd::vector<NvCloth::SimParticleFormat> newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
actorClothSkinning->UpdateSkinning();
|
||||
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles, MeshRemappedVertices);
|
||||
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
|
||||
|
||||
const AZ::Transform diffTransform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f));
|
||||
const AZStd::vector<NvCloth::SimParticleFormat> clothParticlesResult = {{
|
||||
@@ -245,7 +245,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
|
||||
|
||||
const AZStd::vector<NvCloth::SimParticleFormat> clothParticles = {{
|
||||
@@ -256,7 +256,7 @@ namespace UnitTest
|
||||
AZStd::vector<NvCloth::SimParticleFormat> skinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
actorClothSkinning->UpdateSkinning();
|
||||
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles, MeshRemappedVertices);
|
||||
actorClothSkinning->ApplySkinning(clothParticles, skinnedClothParticles);
|
||||
|
||||
EXPECT_THAT(skinnedClothParticles, ::testing::Pointwise(ContainerIsCloseTolerance(Tolerance), clothParticles));
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace UnitTest
|
||||
AZStd::vector<NvCloth::SimParticleFormat> newSkinnedClothParticles(clothParticles.size(), NvCloth::SimParticleFormat(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
actorClothSkinning->UpdateSkinning();
|
||||
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles, MeshRemappedVertices);
|
||||
actorClothSkinning->ApplySkinning(clothParticles, newSkinnedClothParticles);
|
||||
|
||||
const AZStd::vector<NvCloth::SimParticleFormat> clothParticlesResult = {{
|
||||
NvCloth::SimParticleFormat(-48.4177f, -31.9446f, 45.2279f, 1.0f),
|
||||
@@ -294,7 +294,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<NvCloth::ActorClothSkinning> actorClothSkinning =
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size());
|
||||
NvCloth::ActorClothSkinning::Create(m_actorComponent->GetEntityId(), MeshNodeInfo, MeshVertices.size(), MeshVertices.size(), MeshRemappedVertices);
|
||||
ASSERT_TRUE(actorClothSkinning.get() != nullptr);
|
||||
|
||||
EXPECT_FALSE(actorClothSkinning->IsActorVisible());
|
||||
|
||||
Reference in New Issue
Block a user