// Copyright Epic Games, Inc. All Rights Reserved. #include "Chaos/TriangleMeshImplicitObject.h" #include "Containers/Array.h" #include "Containers/UnrealString.h" #include "Delegates/Delegate.h" #include "Engine/StaticMesh.h" #include "HAL/IConsoleManager.h" #include "Logging/LogCategory.h" #include "Logging/LogMacros.h" #include "Math/UnrealMathSSE.h" #include "PhysicsEngine/BodySetup.h" #include "Templates/SharedPointer.h" #include "Trace/Detail/Channel.h" #include "UObject/UObjectBaseUtility.h" #include "UObject/UObjectIterator.h" class FOutputDevice; class UWorld; namespace LyraEditorUtilities { ////////////////////////////////////////////////////////////////////////// // returns true if the mesh has one or more degenerate triangles bool CheckMeshDataForProblem(const Chaos::FTriangleMeshImplicitObject::ParticlesType& Particles, const Chaos::FTrimeshIndexBuffer& Elements) { // Internal helper because the index buffer type is templated auto CheckTris = [&](const auto& Elements, int32 NumTriangles) { using VecType = Chaos::FTriangleMeshImplicitObject::ParticleVecType; for (int32 FaceIdx = 0; FaceIdx < NumTriangles; ++FaceIdx) { const VecType& A = Particles.X(Elements[FaceIdx][0]); const VecType& B = Particles.X(Elements[FaceIdx][1]); const VecType& C = Particles.X(Elements[FaceIdx][2]); const VecType AB = B - A; const VecType AC = C - A; VecType Normal = VecType::CrossProduct(AB, AC); if (Normal.SafeNormalize() < SMALL_NUMBER) { return true; } } return false; }; const int32 NumTriangles = Elements.GetNumTriangles(); if (Elements.RequiresLargeIndices()) { return CheckTris(Elements.GetLargeIndexBuffer(), NumTriangles); } else { return CheckTris(Elements.GetSmallIndexBuffer(), NumTriangles); } } void CheckChaosMeshCollision(FOutputDevice& Ar) { for (UStaticMesh* MeshAsset : TObjectRange()) { if (UBodySetup* BodySetup = MeshAsset->GetBodySetup()) { for (const TSharedPtr& TriMesh : BodySetup->ChaosTriMeshes) { if (Chaos::FTriangleMeshImplicitObject* TriMeshData = TriMesh.Get()) { if (CheckMeshDataForProblem(TriMeshData->Particles(), TriMeshData->Elements())) { UE_LOG(LogConsoleResponse, Warning, TEXT("Mesh asset %s has one or more degenerate triangles in collision data"), *GetPathNameSafe(MeshAsset)); } } } } } } FAutoConsoleCommandWithWorldArgsAndOutputDevice GCheckChaosMeshCollisionCmd( TEXT("Lyra.CheckChaosMeshCollision"), TEXT("Usage:\n") TEXT(" Lyra.CheckChaosMeshCollision\n") TEXT("\n") TEXT("It will check Chaos collision data for all *loaded* static mesh assets for any degenerate triangles"), FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic( [](const TArray& Params, UWorld* World, FOutputDevice& Ar) { CheckChaosMeshCollision(Ar); })); ////////////////////////////////////////////////////////////////////////// }; // End of namespace