diff --git a/Source/Engine/Physics/Actors/Cloth.cpp b/Source/Engine/Physics/Actors/Cloth.cpp index 5bf8d264e..3b9c1cf1c 100644 --- a/Source/Engine/Physics/Actors/Cloth.cpp +++ b/Source/Engine/Physics/Actors/Cloth.cpp @@ -133,7 +133,7 @@ Array Cloth::GetParticles() const void Cloth::SetParticles(Span value) { PROFILE_CPU(); -#if !BUILD_RELEASE +#if USE_CLOTH_SANITY_CHECKS { // Sanity check const Float3* src = value.Get(); @@ -162,6 +162,16 @@ Span Cloth::GetPaint() const void Cloth::SetPaint(Span value) { PROFILE_CPU(); +#if USE_CLOTH_SANITY_CHECKS + { + // Sanity check + const float* src = value.Get(); + bool allValid = true; + for (int32 i = 0; i < value.Length(); i++) + allValid &= !isnan(src[i]) && !isinf(src[i]); + ASSERT(allValid); + } +#endif if (value.IsInvalid()) { // Remove paint when set to empty @@ -174,16 +184,6 @@ void Cloth::SetPaint(Span value) #endif return; } -#if !BUILD_RELEASE - { - // Sanity check - const float* src = value.Get(); - bool allValid = true; - for (int32 i = 0; i < value.Length(); i++) - allValid &= !isnan(src[i]) && !isinf(src[i]); - ASSERT(allValid); - } -#endif _paint.Set(value.Get(), value.Length()); #if WITH_CLOTH if (_cloth) @@ -295,6 +295,17 @@ void Cloth::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) DESERIALIZE_MEMBER(Fabric, _fabricSettings); DESERIALIZE_MEMBER(Paint, _paint); +#if USE_CLOTH_SANITY_CHECKS + { + // Sanity check + const float* data = _paint.Get(); + bool allValid = true; + for (int32 i = 0; i < _paint.Count(); i++) + allValid &= !isnan(data[i]) && !isinf(data[i]); + ASSERT(allValid); + } +#endif + // Refresh cloth when settings were changed if (IsDuringPlay()) Rebuild(); @@ -588,6 +599,7 @@ void Cloth::CalculateInvMasses(Array& invMasses) // Sum triangle area for each influenced particle invMasses.Resize(verticesCount); + invMasses.SetAll(0.0f); for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++) { const int32 index = triangleIndex * 3; @@ -621,6 +633,10 @@ void Cloth::CalculateInvMasses(Array& invMasses) for (int32 i = 0; i < verticesCount; i++) { float& mass = invMasses[i]; +#if USE_CLOTH_SANITY_CHECKS + // Sanity check + ASSERT(!isnan(mass) && !isinf(mass) && mass >= 0.0f); +#endif const float maxDistance = _paint[i]; if (maxDistance < 0.01f) { @@ -649,6 +665,17 @@ void Cloth::CalculateInvMasses(Array& invMasses) } } } + +#if USE_CLOTH_SANITY_CHECKS + { + // Sanity check + const float* data = invMasses.Get(); + bool allValid = true; + for (int32 i = 0; i < invMasses.Count(); i++) + allValid &= !isnan(data[i]) && !isinf(data[i]); + ASSERT(allValid); + } +#endif #endif } diff --git a/Source/Engine/Physics/Actors/Cloth.h b/Source/Engine/Physics/Actors/Cloth.h index ac87bf5ee..b31c12984 100644 --- a/Source/Engine/Physics/Actors/Cloth.h +++ b/Source/Engine/Physics/Actors/Cloth.h @@ -5,6 +5,9 @@ #include "Engine/Level/Actor.h" #include "Engine/Level/Actors/ModelInstanceActor.h" +// Used internally to validate cloth data against invalid nan/inf values +#define USE_CLOTH_SANITY_CHECKS (BUILD_DEBUG) + /// /// Physical simulation actor for cloth objects made of vertices that are simulated as cloth particles with physical properties, forces, and constraints to affect cloth behavior. /// diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index da393f529..a1072f540 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -3328,6 +3328,32 @@ void PhysicsBackend::RemoveVehicle(void* scene, WheeledVehicle* actor) void* PhysicsBackend::CreateCloth(const PhysicsClothDesc& desc) { +#if USE_CLOTH_SANITY_CHECKS + { + // Sanity check + bool allValid = true; + for (int32 i = 0; i < desc.VerticesCount; i++) + allValid &= !(*(Float3*)((byte*)desc.VerticesData + i * desc.VerticesStride)).IsNanOrInfinity(); + if (desc.InvMassesData) + { + for (int32 i = 0; i < desc.VerticesCount; i++) + { + float v = *(float*)((byte*)desc.InvMassesData + i * desc.InvMassesStride); + allValid &= !isnan(v) && !isinf(v); + } + } + if (desc.MaxDistancesData) + { + for (int32 i = 0; i < desc.VerticesCount; i++) + { + float v = *(float*)((byte*)desc.MaxDistancesData + i * desc.MaxDistancesStride); + allValid &= !isnan(v) && !isinf(v); + } + } + ASSERT(allValid); + } +#endif + // Lazy-init NvCloth if (ClothFactory == nullptr) {