Add normals computing to cloth mesh

This commit is contained in:
Wojtek Figat
2023-07-16 12:02:33 +02:00
parent f0496d53e8
commit 31943ca9bd
5 changed files with 107 additions and 12 deletions

View File

@@ -127,7 +127,7 @@ void MeshDeformation::RunDeformers(const MeshBase* mesh, MeshBufferType type, GP
}
if (!deformation)
{
deformation = New<MeshDeformationData>(key, vertexStride);
deformation = New<MeshDeformationData>(key, type, vertexStride);
deformation->VertexBuffer.Data.Resize(vertexBuffer->GetSize());
deformation->Bounds = mesh->GetBox();
_deformations.Add(deformation);

View File

@@ -13,14 +13,16 @@
struct MeshDeformationData
{
uint64 Key;
MeshBufferType Type;
uint32 DirtyMinIndex = 0;
uint32 DirtyMaxIndex = MAX_uint32 - 1;
bool Dirty = true;
BoundingBox Bounds;
DynamicVertexBuffer VertexBuffer;
MeshDeformationData(uint64 key, uint32 stride)
MeshDeformationData(uint64 key, MeshBufferType type, uint32 stride)
: Key(key)
, Type(type)
, VertexBuffer(0, stride, TEXT("MeshDeformation"))
{
}

View File

@@ -3,6 +3,7 @@
#include "Cloth.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Physics/PhysicsBackend.h"
@@ -567,6 +568,8 @@ bool Cloth::CreateCloth()
Function<void(const MeshBase*, MeshDeformationData&)> deformer;
deformer.Bind<Cloth, &Cloth::RunClothDeformer>(this);
deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex0, deformer);
if (_simulationSettings.ComputeNormals)
deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex1, deformer);
_meshDeformation = deformation;
}
#endif
@@ -582,6 +585,7 @@ void Cloth::DestroyCloth()
Function<void(const MeshBase*, MeshDeformationData&)> deformer;
deformer.Bind<Cloth, &Cloth::RunClothDeformer>(this);
_meshDeformation->RemoveDeformer(_mesh.LODIndex, _mesh.MeshIndex, MeshBufferType::Vertex0, deformer);
_meshDeformation->RemoveDeformer(_mesh.LODIndex, _mesh.MeshIndex, MeshBufferType::Vertex1, deformer);
_meshDeformation = nullptr;
}
PhysicsBackend::DestroyCloth(_cloth);
@@ -788,6 +792,8 @@ void Cloth::OnPostUpdate()
BoundingBox localBounds;
BoundingBox::Transform(_box, invWorld, localBounds);
_meshDeformation->Dirty(_mesh.LODIndex, _mesh.MeshIndex, MeshBufferType::Vertex0, localBounds);
if (_simulationSettings.ComputeNormals)
_meshDeformation->Dirty(_mesh.LODIndex, _mesh.MeshIndex, MeshBufferType::Vertex1, localBounds);
// Update bounds (for mesh culling)
auto* actor = (ModelInstanceActor*)GetParent();
@@ -797,17 +803,69 @@ void Cloth::OnPostUpdate()
void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformation)
{
if (!_simulationSettings.ComputeNormals && deformation.Type != MeshBufferType::Vertex0)
return;
#if WITH_CLOTH
PROFILE_CPU_NAMED("Cloth");
PhysicsBackend::LockClothParticles(_cloth);
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
// Update mesh vertices based on the cloth particles positions
auto vbData = deformation.VertexBuffer.Data.Get();
auto vbCount = (uint32)mesh->GetVertexCount();
auto vbStride = (uint32)deformation.VertexBuffer.Data.Count() / vbCount;
// TODO: add support for mesh vertex data layout descriptor instead hardcoded position data at the beginning of VB0
ASSERT((uint32)particles.Length() >= vbCount);
// Calculate normals
Array<Float3> normals;
const ModelInstanceActor::MeshReference meshRef = GetMesh();
BytesContainer indicesData;
int32 indicesCount;
if ((_simulationSettings.ComputeNormals || deformation.Type == MeshBufferType::Vertex1) &&
meshRef.Actor && !meshRef.Actor->GetMeshData(meshRef, MeshBufferType::Index, indicesData, indicesCount))
{
// TODO: optimize memory allocs (eg. use shared allocator)
normals.Resize(vbCount);
Platform::MemoryClear(normals.Get(), vbCount * sizeof(Float3));
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
if (indices16bit)
{
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint16>()[index];
const int32 i1 = indicesData.Get<uint16>()[index + 1];
const int32 i2 = indicesData.Get<uint16>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
}
}
else
{
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint32>()[index];
const int32 i1 = indicesData.Get<uint32>()[index + 1];
const int32 i2 = indicesData.Get<uint32>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
}
}
}
// Update mesh vertices based on the cloth particles positions
if (auto* animatedModel = Cast<AnimatedModel>(GetParent()))
{
if (animatedModel->GraphInstance.NodesPose.IsEmpty())
@@ -828,7 +886,7 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
const float* paint = _paint.Count() >= particles.Length() ? _paint.Get() : nullptr;
for (uint32 i = 0; i < vbCount; i++)
{
VB0SkinnedElementType& vb0 = *(VB0SkinnedElementType*)vbData;
VB0SkinnedElementType& vb = *(VB0SkinnedElementType*)vbData;
vbData += vbStride;
// Skip fixed vertices
@@ -836,27 +894,27 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
continue;
// Calculate skinned vertex matrix from bones blending
const Float4 blendWeights = vb0.BlendWeights.ToFloat4();
const Float4 blendWeights = vb.BlendWeights.ToFloat4();
// TODO: optimize this or use _skinningData from AnimatedModel to access current mesh bones data directly
Matrix matrix;
const SkeletonBone& bone0 = skeleton.Bones[vb0.BlendIndices.R];
const SkeletonBone& bone0 = skeleton.Bones[vb.BlendIndices.R];
Matrix::Multiply(bone0.OffsetMatrix, pose[bone0.NodeIndex], matrix);
Matrix boneMatrix = matrix * blendWeights.X;
if (blendWeights.Y > 0.0f)
{
const SkeletonBone& bone1 = skeleton.Bones[vb0.BlendIndices.G];
const SkeletonBone& bone1 = skeleton.Bones[vb.BlendIndices.G];
Matrix::Multiply(bone1.OffsetMatrix, pose[bone1.NodeIndex], matrix);
boneMatrix += matrix * blendWeights.Y;
}
if (blendWeights.Z > 0.0f)
{
const SkeletonBone& bone2 = skeleton.Bones[vb0.BlendIndices.B];
const SkeletonBone& bone2 = skeleton.Bones[vb.BlendIndices.B];
Matrix::Multiply(bone2.OffsetMatrix, pose[bone2.NodeIndex], matrix);
boneMatrix += matrix * blendWeights.Z;
}
if (blendWeights.W > 0.0f)
{
const SkeletonBone& bone3 = skeleton.Bones[vb0.BlendIndices.A];
const SkeletonBone& bone3 = skeleton.Bones[vb.BlendIndices.A];
Matrix::Multiply(bone3.OffsetMatrix, pose[bone3.NodeIndex], matrix);
boneMatrix += matrix * blendWeights.W;
}
@@ -865,16 +923,43 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
Matrix boneMatrixInv;
Matrix::Invert(boneMatrix, boneMatrixInv);
Float3 pos = *(Float3*)&particles.Get()[i];
vb0.Position = Float3::Transform(pos, boneMatrixInv);
vb.Position = Float3::Transform(pos, boneMatrixInv);
}
if (_simulationSettings.ComputeNormals)
{
// Write normals
for (uint32 i = 0; i < vbCount; i++)
{
Float3 normal = normals.Get()[i];
normal.Normalize();
VB0SkinnedElementType& vb = *(VB0SkinnedElementType*)vbData;
vbData += vbStride;
RenderTools::CalculateTangentFrame(vb.Normal, vb.Tangent, normal);
}
}
}
else if (deformation.Type == MeshBufferType::Vertex0)
{
// Copy particle positions to the mesh data
ASSERT(vbStride == sizeof(VB0ElementType));
for (uint32 i = 0; i < vbCount; i++)
{
*(Float3*)vbData = *(Float3*)&particles.Get()[i];
vbData += vbStride;
}
}
else
{
// Write normals for the modified vertices by the cloth
ASSERT(vbStride == sizeof(VB1ElementType));
for (uint32 i = 0; i < vbCount; i++)
{
// Copy particle positions to the mesh data
*(Float3*)vbData = *(Float3*)&particles.Get()[i];
Float3 normal = normals.Get()[i];
normal.Normalize();
VB1ElementType& vb = *(VB1ElementType*)vbData;
vbData += vbStride;
RenderTools::CalculateTangentFrame(vb.Normal, vb.Tangent, normal);
}
}

View File

@@ -137,6 +137,11 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Cloth\"), ActorToolbox(\"Ph
/// </summary>
API_FIELD() float MaxDistance = 1000.0f;
/// <summary>
/// Enables automatic normal vectors computing for the cloth mesh, otherwise original mesh normals will be used.
/// </summary>
API_FIELD() bool ComputeNormals = true;
/// <summary>
/// Wind velocity vector (direction and magnitude) in world coordinates. A greater magnitude applies a stronger wind force. Ensure that Air Drag and Air Lift coefficients are non-zero in order to apply wind force.
/// </summary>

View File

@@ -3329,6 +3329,7 @@ void PhysicsBackend::RemoveVehicle(void* scene, WheeledVehicle* actor)
void* PhysicsBackend::CreateCloth(const PhysicsClothDesc& desc)
{
PROFILE_CPU();
#if USE_CLOTH_SANITY_CHECKS
{
// Sanity check
@@ -3565,6 +3566,7 @@ Span<const Float4> PhysicsBackend::GetClothParticles(void* cloth)
void PhysicsBackend::SetClothParticles(void* cloth, Span<const Float4> value, Span<const Float3> positions, Span<const float> invMasses)
{
PROFILE_CPU();
auto clothPhysX = (nv::cloth::Cloth*)cloth;
nv::cloth::MappedRange<PxVec4> range = clothPhysX->getCurrentParticles();
const uint32_t size = range.size();
@@ -3609,6 +3611,7 @@ void PhysicsBackend::SetClothParticles(void* cloth, Span<const Float4> value, Sp
void PhysicsBackend::SetClothPaint(void* cloth, Span<const float> value)
{
PROFILE_CPU();
auto clothPhysX = (nv::cloth::Cloth*)cloth;
if (value.IsValid())
{