// Copyright Epic Games, Inc. All Rights Reserved. #include "PhysicsNodes/GenerateSimpleCollisionNode.h" #include "Operations/MeshConvexHull.h" #include "DynamicMesh/MeshIndexUtil.h" #include "Async/ParallelFor.h" #include "DynamicSubmesh3.h" #include "Util/ProgressCancel.h" using namespace UE::Geometry; using namespace UE::GeometryFlow; namespace GenerateSimpleCollisionNodeHelpers { TArray SetIntersection(const TArray& A, const TArray& B) { TSet SA(A); TSet SB(B); return SA.Intersect(SB).Array(); } void GenerateConvexHulls(const FDynamicMesh3& Mesh, const FIndexSets& IndexData, const FGenerateSimpleCollisionSettings& Settings, FCollisionGeometry& Result, FProgressCancel* Progress) { int32 NumShapes = IndexData.IndexSets.Num(); TArray Convexes; TArray ConvexOk; if (NumShapes == 0) { // No index sets given. Create a single convex hull from the whole mesh. Convexes.SetNum(1); ConvexOk.SetNum(1); ConvexOk[0] = false; FMeshConvexHull Hull(&Mesh); if (Settings.ConvexHullSettings.bPrefilterVertices) { FMeshConvexHull::GridSample(Mesh, Settings.ConvexHullSettings.PrefilterGridResolution, Hull.VertexSet); } Hull.bPostSimplify = Settings.ConvexHullSettings.SimplifyToTriangleCount > 0; Hull.MaxTargetFaceCount = Settings.ConvexHullSettings.SimplifyToTriangleCount; if (Hull.Compute(Progress)) { FConvexShape3d NewConvex; NewConvex.Mesh = MoveTemp(Hull.ConvexHull); Convexes[0] = MoveTemp(NewConvex); ConvexOk[0] = true; } } else { // Create one hull for each index set. Convexes.SetNum(NumShapes); ConvexOk.SetNum(NumShapes); ParallelFor(NumShapes, [&](int32 k) { ConvexOk[k] = false; FMeshConvexHull Hull(&Mesh); if (Settings.ConvexHullSettings.bPrefilterVertices) { TArray SampledVertices; FMeshConvexHull::GridSample(Mesh, Settings.ConvexHullSettings.PrefilterGridResolution, SampledVertices); TArray IndexSetVertices; UE::Geometry::TriangleToVertexIDs(&Mesh, IndexData.IndexSets[k], IndexSetVertices); Hull.VertexSet = SetIntersection(SampledVertices, IndexSetVertices); } else { UE::Geometry::TriangleToVertexIDs(&Mesh, IndexData.IndexSets[k], Hull.VertexSet); } Hull.bPostSimplify = Settings.ConvexHullSettings.SimplifyToTriangleCount > 0; Hull.MaxTargetFaceCount = Settings.ConvexHullSettings.SimplifyToTriangleCount; if (Hull.Compute(Progress)) { FConvexShape3d NewConvex; NewConvex.Mesh = MoveTemp(Hull.ConvexHull); Convexes[k] = MoveTemp(NewConvex); ConvexOk[k] = true; } }); } Result.Geometry.Convexes = MoveTemp(Convexes); } void GenerateSubMeshes(const FDynamicMesh3& Mesh, const FIndexSets& TriangleIndexSets, TArray& OutSubMeshes) { if (TriangleIndexSets.IndexSets.Num() == 0) { OutSubMeshes.Add(Mesh); } else { for (const TArray& Set : TriangleIndexSets.IndexSets) { FDynamicSubmesh3 Sub(&Mesh, Set); OutSubMeshes.Add(Sub.GetSubmesh()); } } } FMeshSimpleShapeApproximation::EProjectedHullAxisMode FromUEnum(const EGeometryFlow_ProjectedHullAxisMode& Value) { return static_cast(Value); } } // namespace GenerateSimpleCollisionNodeHelpers EGeometryFlowResult FGenerateSimpleCollisionNode::EvaluateInternal(const FDynamicMesh3& Mesh, const FIndexSets& IndexData, const FGenerateSimpleCollisionSettings& Settings, TUniquePtr& EvaluationInfo, FCollisionGeometry& OutCollisionGeometry) { FMeshSimpleShapeApproximation ShapeApproximator; // Don't cache anything, just compute when asked to ShapeApproximator.bDetectSpheres = false; ShapeApproximator.bDetectBoxes = false; ShapeApproximator.bDetectCapsules = false; ShapeApproximator.bDetectConvexes = false; TArray SubMeshes; GenerateSimpleCollisionNodeHelpers::GenerateSubMeshes(Mesh, IndexData, SubMeshes); if (EvaluationInfo && EvaluationInfo->Progress && EvaluationInfo->Progress->Cancelled()) { return EGeometryFlowResult::OperationCancelled; } TArray SubMeshPointers; for (FDynamicMesh3& SubMesh : SubMeshes) { SubMeshPointers.Add(&SubMesh); } ShapeApproximator.InitializeSourceMeshes(SubMeshPointers); if (EvaluationInfo && EvaluationInfo->Progress && EvaluationInfo->Progress->Cancelled()) { return EGeometryFlowResult::OperationCancelled; } FProgressCancel* Progress = EvaluationInfo ? EvaluationInfo->Progress : nullptr; switch (Settings.Type) { case EGeometryFlow_SimpleCollisionGeometryType::AlignedBoxes: ShapeApproximator.bDetectBoxes = true; ShapeApproximator.Generate_AlignedBoxes(OutCollisionGeometry.Geometry); break; case EGeometryFlow_SimpleCollisionGeometryType::OrientedBoxes: ShapeApproximator.bDetectBoxes = true; ShapeApproximator.Generate_OrientedBoxes(OutCollisionGeometry.Geometry, Progress); break; case EGeometryFlow_SimpleCollisionGeometryType::MinimalSpheres: ShapeApproximator.bDetectSpheres = true; ShapeApproximator.Generate_MinimalSpheres(OutCollisionGeometry.Geometry); break; case EGeometryFlow_SimpleCollisionGeometryType::Capsules: ShapeApproximator.bDetectCapsules = true; ShapeApproximator.Generate_Capsules(OutCollisionGeometry.Geometry); break; case EGeometryFlow_SimpleCollisionGeometryType::ConvexHulls: GenerateSimpleCollisionNodeHelpers::GenerateConvexHulls(Mesh, IndexData, Settings, OutCollisionGeometry, Progress); break; case EGeometryFlow_SimpleCollisionGeometryType::SweptHulls: ShapeApproximator.bDetectConvexes = true; ShapeApproximator.bSimplifyHulls = Settings.SweptHullSettings.bSimplifyPolygons; ShapeApproximator.HullSimplifyTolerance = Settings.SweptHullSettings.HullTolerance; ShapeApproximator.Generate_ProjectedHulls(OutCollisionGeometry.Geometry, GenerateSimpleCollisionNodeHelpers::FromUEnum(Settings.SweptHullSettings.SweepAxis)); break; case EGeometryFlow_SimpleCollisionGeometryType::MinVolume: ShapeApproximator.Generate_MinVolume(OutCollisionGeometry.Geometry); break; case EGeometryFlow_SimpleCollisionGeometryType::None: break; } if (EvaluationInfo && EvaluationInfo->Progress && EvaluationInfo->Progress->Cancelled()) { return EGeometryFlowResult::OperationCancelled; } return EGeometryFlowResult::Ok; } void FGenerateSimpleCollisionNode::Evaluate( const FNamedDataMap& DatasIn, FNamedDataMap& DatasOut, TUniquePtr& EvaluationInfo) { if (ensure(DatasOut.Contains(OutParamGeometry()))) { bool bAllInputsValid = true; bool bRecomputeRequired = (IsOutputAvailable(OutParamGeometry()) == false); TSafeSharedPtr MeshArg = FindAndUpdateInputForEvaluate(InParamMesh(), DatasIn, bRecomputeRequired, bAllInputsValid); const FDynamicMesh3& Mesh = MeshArg->GetDataConstRef((int)EMeshProcessingDataTypes::DynamicMesh); TSafeSharedPtr TriSetsArg = FindAndUpdateInputForEvaluate(InParamIndexSets(), DatasIn, bRecomputeRequired, bAllInputsValid); const FIndexSets& IndexData = TriSetsArg->GetDataConstRef((int)EMeshProcessingDataTypes::IndexSets); TSafeSharedPtr SettingsArg = FindAndUpdateInputForEvaluate(InParamSettings(), DatasIn, bRecomputeRequired, bAllInputsValid); FGenerateSimpleCollisionSettings Settings; SettingsArg->GetDataCopy(Settings, FGenerateSimpleCollisionSettings::DataTypeIdentifier); if (bAllInputsValid) { if (bRecomputeRequired) { // if execution is interrupted by ProgressCancel, make sure this node is recomputed next time through ClearAllOutputs(); FCollisionGeometry CollisionGeometry; EGeometryFlowResult Result = EvaluateInternal(Mesh, IndexData, Settings, EvaluationInfo, CollisionGeometry); if (Result == EGeometryFlowResult::OperationCancelled) { return; } SetOutput(OutParamGeometry(), MakeMovableData(MoveTemp(CollisionGeometry))); } DatasOut.SetData(OutParamGeometry(), GetOutput(OutParamGeometry())); } } }