From 31943ca9bd4896f2247732d97086a1da743435c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 16 Jul 2023 12:02:33 +0200 Subject: [PATCH] Add normals computing to cloth mesh --- .../Graphics/Models/MeshDeformation.cpp | 2 +- .../Engine/Graphics/Models/MeshDeformation.h | 4 +- Source/Engine/Physics/Actors/Cloth.cpp | 105 ++++++++++++++++-- Source/Engine/Physics/Actors/Cloth.h | 5 + .../Physics/PhysX/PhysicsBackendPhysX.cpp | 3 + 5 files changed, 107 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Graphics/Models/MeshDeformation.cpp b/Source/Engine/Graphics/Models/MeshDeformation.cpp index 5a654459b..e1b2e2ddf 100644 --- a/Source/Engine/Graphics/Models/MeshDeformation.cpp +++ b/Source/Engine/Graphics/Models/MeshDeformation.cpp @@ -127,7 +127,7 @@ void MeshDeformation::RunDeformers(const MeshBase* mesh, MeshBufferType type, GP } if (!deformation) { - deformation = New(key, vertexStride); + deformation = New(key, type, vertexStride); deformation->VertexBuffer.Data.Resize(vertexBuffer->GetSize()); deformation->Bounds = mesh->GetBox(); _deformations.Add(deformation); diff --git a/Source/Engine/Graphics/Models/MeshDeformation.h b/Source/Engine/Graphics/Models/MeshDeformation.h index c33025926..9a51cad30 100644 --- a/Source/Engine/Graphics/Models/MeshDeformation.h +++ b/Source/Engine/Graphics/Models/MeshDeformation.h @@ -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")) { } diff --git a/Source/Engine/Physics/Actors/Cloth.cpp b/Source/Engine/Physics/Actors/Cloth.cpp index 40c058455..45e4df067 100644 --- a/Source/Engine/Physics/Actors/Cloth.cpp +++ b/Source/Engine/Physics/Actors/Cloth.cpp @@ -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 deformer; deformer.Bind(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 deformer; deformer.Bind(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 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 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()[index]; + const int32 i1 = indicesData.Get()[index + 1]; + const int32 i2 = indicesData.Get()[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()[index]; + const int32 i1 = indicesData.Get()[index + 1]; + const int32 i2 = indicesData.Get()[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(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); } } diff --git a/Source/Engine/Physics/Actors/Cloth.h b/Source/Engine/Physics/Actors/Cloth.h index 9ce500147..9a9f69577 100644 --- a/Source/Engine/Physics/Actors/Cloth.h +++ b/Source/Engine/Physics/Actors/Cloth.h @@ -137,6 +137,11 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Cloth\"), ActorToolbox(\"Ph /// API_FIELD() float MaxDistance = 1000.0f; + /// + /// Enables automatic normal vectors computing for the cloth mesh, otherwise original mesh normals will be used. + /// + API_FIELD() bool ComputeNormals = true; + /// /// 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. /// diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index e47fad0b3..b5aeb1175 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -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 PhysicsBackend::GetClothParticles(void* cloth) void PhysicsBackend::SetClothParticles(void* cloth, Span value, Span positions, Span invMasses) { + PROFILE_CPU(); auto clothPhysX = (nv::cloth::Cloth*)cloth; nv::cloth::MappedRange range = clothPhysX->getCurrentParticles(); const uint32_t size = range.size(); @@ -3609,6 +3611,7 @@ void PhysicsBackend::SetClothParticles(void* cloth, Span value, Sp void PhysicsBackend::SetClothPaint(void* cloth, Span value) { + PROFILE_CPU(); auto clothPhysX = (nv::cloth::Cloth*)cloth; if (value.IsValid()) {