2020-09-01 14:07:48 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ShapeApproximation/MeshSimpleShapeApproximation.h"
# include "Async/ParallelFor.h"
# include "MinVolumeSphere3.h"
# include "MinVolumeBox3.h"
# include "FitCapsule3.h"
2022-04-01 16:04:37 -04:00
# include "CompGeom/ConvexDecomposition3.h"
2021-06-13 00:36:02 -04:00
//#include "DynamicMesh/DynamicMeshAABBTree3.h"
2022-05-30 16:48:48 -04:00
# include "Implicit/SweepingMeshSDF.h"
2020-09-01 14:07:48 -04:00
# include "ShapeApproximation/ShapeDetection3.h"
# include "MeshQueries.h"
# include "Operations/MeshConvexHull.h"
# include "Operations/MeshProjectionHull.h"
2021-02-01 14:43:20 -04:00
# include "Util/ProgressCancel.h"
2020-09-01 14:07:48 -04:00
2022-05-30 16:48:48 -04:00
# define LOCTEXT_NAMESPACE "MeshSimpleShapeApproximation"
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2023-04-25 13:27:03 -04:00
namespace FMeshSimpleShapeApproximationLocals
{
// Take the box, and while preserving its shape in space, swap its axes around so that they
// are pointed roughly toward the world x/y/z. This makes it behave more intuitively under
// transformations (for instance, scaling Z grows roughly in the Z direction rather than
// to the side if the original box was actually on its "side").
FOrientedBox3d ReparameterizeBoxCloserToWorldFrame ( const FOrientedBox3d & Box )
{
FVector3d Axes [ 3 ] { Box . AxisX ( ) , Box . AxisY ( ) , Box . AxisZ ( ) } ;
int32 BestZ = FMath : : Max3Index (
FMath : : Abs ( Axes [ 0 ] . Z ) ,
FMath : : Abs ( Axes [ 1 ] . Z ) ,
FMath : : Abs ( Axes [ 2 ] . Z ) ) ;
FVector3d NewZ = Axes [ BestZ ] * ( Axes [ BestZ ] . Z > 0 ? 1 : - 1 ) ;
// Pick the best Y out of the two remaining
double DotY [ 3 ] {
Axes [ 0 ] . Y ,
Axes [ 1 ] . Y ,
Axes [ 2 ] . Y
} ;
DotY [ BestZ ] = 0 ; // don't pick this one
int32 BestY = FMath : : Max3Index (
FMath : : Abs ( DotY [ 0 ] ) ,
FMath : : Abs ( DotY [ 1 ] ) ,
FMath : : Abs ( DotY [ 2 ] ) ) ;
FVector3d NewY = Axes [ BestY ] * ( DotY [ BestY ] > 0 ? 1 : - 1 ) ;
// Sum of BestY and BestZ will be either 0+1=1, 0+2=2, or 1+2=3, and the
// corresponding leftover index will be 2, 1, or 0.
int32 BestX = 3 - ( BestY + BestZ ) ;
// Static analyzer probably won't like that, so here's a clamp for safety
BestX = FMath : : Clamp ( BestX , 0 , 2 ) ;
// Sanity check to make sure we picked unique axes
if ( ! ensure ( BestX ! = BestY
& & BestY ! = BestZ
& & BestX ! = BestZ ) )
{
return Box ;
}
return FOrientedBox3d (
FFrame3d ( Box . Frame . Origin ,
NewY . Cross ( NewZ ) , // better to do this than risk picking the wrong direction for BestX
NewY ,
NewZ ) ,
FVector3d ( Box . Extents [ BestX ] , Box . Extents [ BestY ] , Box . Extents [ BestZ ] ) ) ;
}
}
2020-09-01 14:07:48 -04:00
void FMeshSimpleShapeApproximation : : DetectAndCacheSimpleShapeType ( const FDynamicMesh3 * SourceMesh , FSourceMeshCache & CacheOut )
{
if ( UE : : Geometry : : IsBoxMesh ( * SourceMesh , CacheOut . DetectedBox ) )
{
CacheOut . DetectedType = EDetectedSimpleShapeType : : Box ;
}
else if ( UE : : Geometry : : IsSphereMesh ( * SourceMesh , CacheOut . DetectedSphere ) )
{
CacheOut . DetectedType = EDetectedSimpleShapeType : : Sphere ;
}
else if ( UE : : Geometry : : IsCapsuleMesh ( * SourceMesh , CacheOut . DetectedCapsule ) )
{
CacheOut . DetectedType = EDetectedSimpleShapeType : : Capsule ;
}
}
void FMeshSimpleShapeApproximation : : InitializeSourceMeshes ( const TArray < const FDynamicMesh3 * > & InputMeshSet )
{
SourceMeshes = InputMeshSet ;
SourceMeshCaches . Reset ( ) ;
SourceMeshCaches . SetNum ( SourceMeshes . Num ( ) ) ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 k ) {
DetectAndCacheSimpleShapeType ( SourceMeshes [ k ] , SourceMeshCaches [ k ] ) ;
} ) ;
}
bool FMeshSimpleShapeApproximation : : GetDetectedSimpleShape (
const FSourceMeshCache & Cache ,
FSimpleShapeSet3d & ShapeSetOut ,
FCriticalSection & ShapeSetLock )
{
2023-04-25 13:27:03 -04:00
using namespace FMeshSimpleShapeApproximationLocals ;
2020-09-01 14:07:48 -04:00
if ( Cache . DetectedType = = EDetectedSimpleShapeType : : Sphere & & bDetectSpheres )
{
ShapeSetLock . Lock ( ) ;
ShapeSetOut . Spheres . Add ( Cache . DetectedSphere ) ;
ShapeSetLock . Unlock ( ) ;
return true ;
}
else if ( Cache . DetectedType = = EDetectedSimpleShapeType : : Box & & bDetectBoxes )
{
2023-04-25 13:27:03 -04:00
FOrientedBox3d BoxToUse = ReparameterizeBoxCloserToWorldFrame ( Cache . DetectedBox ) ;
2020-09-01 14:07:48 -04:00
ShapeSetLock . Lock ( ) ;
2023-04-25 13:27:03 -04:00
ShapeSetOut . Boxes . Add ( BoxToUse ) ;
2020-09-01 14:07:48 -04:00
ShapeSetLock . Unlock ( ) ;
return true ;
}
else if ( Cache . DetectedType = = EDetectedSimpleShapeType : : Capsule & & bDetectCapsules )
{
ShapeSetLock . Lock ( ) ;
ShapeSetOut . Capsules . Add ( Cache . DetectedCapsule ) ;
ShapeSetLock . Unlock ( ) ;
return true ;
}
return false ;
}
2021-08-26 09:31:28 -04:00
namespace UE {
namespace Geometry {
2020-09-01 14:07:48 -04:00
struct FSimpleShapeFitsResult
{
bool bHaveSphere = false ;
2021-09-22 10:01:48 -04:00
UE : : Geometry : : FSphere3d Sphere ;
2020-09-01 14:07:48 -04:00
bool bHaveBox = false ;
FOrientedBox3d Box ;
bool bHaveCapsule = false ;
2021-08-26 09:31:28 -04:00
FCapsule3d Capsule ;
2020-09-01 14:07:48 -04:00
bool bHaveConvex = false ;
FDynamicMesh3 Convex ;
} ;
static void ComputeSimpleShapeFits ( const FDynamicMesh3 & Mesh ,
2023-10-12 13:34:51 -04:00
bool bSphere , bool bBox , bool bCapsule , double MinDimension , bool bUseExactComputationForBox ,
2021-02-01 14:43:20 -04:00
FSimpleShapeFitsResult & FitResult ,
FProgressCancel * Progress = nullptr )
2020-09-01 14:07:48 -04:00
{
TArray < int32 > ToLinear , FromLinear ;
if ( bSphere | | bBox | | bCapsule )
{
FromLinear . SetNum ( Mesh . VertexCount ( ) ) ;
int32 LinearIndex = 0 ;
for ( int32 vid : Mesh . VertexIndicesItr ( ) )
{
FromLinear [ LinearIndex + + ] = vid ;
}
}
FitResult . bHaveBox = false ;
if ( bBox )
{
FMinVolumeBox3d MinBoxCalc ;
bool bMinBoxOK = MinBoxCalc . Solve ( FromLinear . Num ( ) ,
2021-02-01 14:43:20 -04:00
[ & ] ( int32 Index ) { return Mesh . GetVertex ( FromLinear [ Index ] ) ; } , bUseExactComputationForBox , Progress ) ;
2020-09-01 14:07:48 -04:00
if ( bMinBoxOK & & MinBoxCalc . IsSolutionAvailable ( ) )
{
FitResult . bHaveBox = true ;
MinBoxCalc . GetResult ( FitResult . Box ) ;
2023-10-12 13:34:51 -04:00
double MinHalfDimension = MinDimension * .5 ;
for ( int32 SubIdx = 0 ; SubIdx < 3 ; + + SubIdx )
{
FitResult . Box . Extents [ SubIdx ] = FMath : : Max ( MinHalfDimension , FitResult . Box . Extents [ SubIdx ] ) ;
}
2020-09-01 14:07:48 -04:00
}
}
FitResult . bHaveSphere = false ;
if ( bSphere )
{
FMinVolumeSphere3d MinSphereCalc ;
bool bMinSphereOK = MinSphereCalc . Solve ( FromLinear . Num ( ) ,
[ & ] ( int32 Index ) { return Mesh . GetVertex ( FromLinear [ Index ] ) ; } ) ;
if ( bMinSphereOK & & MinSphereCalc . IsSolutionAvailable ( ) )
{
FitResult . bHaveSphere = true ;
MinSphereCalc . GetResult ( FitResult . Sphere ) ;
2023-10-12 13:34:51 -04:00
FitResult . Sphere . Radius = FMath : : Max ( MinDimension * .5 , FitResult . Sphere . Radius ) ;
2020-09-01 14:07:48 -04:00
}
}
FitResult . bHaveCapsule = false ;
if ( bCapsule )
{
FitResult . bHaveCapsule = TFitCapsule3 < double > : : Solve ( FromLinear . Num ( ) ,
[ & ] ( int32 Index ) { return Mesh . GetVertex ( FromLinear [ Index ] ) ; } , FitResult . Capsule ) ;
2023-10-12 13:34:51 -04:00
if ( FitResult . bHaveCapsule )
2020-09-01 14:07:48 -04:00
{
2023-10-12 13:34:51 -04:00
FitResult . Capsule . Radius = FMath : : Max ( MinDimension * .5 , FitResult . Capsule . Radius ) ;
// Note: No need to clamp Length based on MinDimension; a capsule's min dimension can't be along its length since a capsule w/ length of zero is still a sphere ...
2020-09-01 14:07:48 -04:00
}
}
}
2021-08-26 09:31:28 -04:00
}
}
2020-09-01 14:07:48 -04:00
void FMeshSimpleShapeApproximation : : Generate_AlignedBoxes ( FSimpleShapeSet3d & ShapeSetOut )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
FAxisAlignedBox3d Bounds = SourceMeshes [ idx ] - > GetBounds ( ) ;
2021-12-09 15:02:36 -05:00
if ( ! Bounds . IsEmpty ( ) )
{
FBoxShape3d NewBox ;
NewBox . Box = FOrientedBox3d ( Bounds ) ;
2023-10-12 13:34:51 -04:00
for ( int32 SubIdx = 0 ; SubIdx < 3 ; + + SubIdx )
{
NewBox . Box . Extents [ SubIdx ] = FMath : : Max ( MinDimension * .5 , NewBox . Box . Extents [ SubIdx ] ) ;
}
2020-09-01 14:07:48 -04:00
2021-12-09 15:02:36 -05:00
GeometryLock . Lock ( ) ;
ShapeSetOut . Boxes . Add ( NewBox ) ;
GeometryLock . Unlock ( ) ;
}
2020-09-01 14:07:48 -04:00
} ) ;
}
2021-02-01 14:43:20 -04:00
void FMeshSimpleShapeApproximation : : Generate_OrientedBoxes ( FSimpleShapeSet3d & ShapeSetOut , FProgressCancel * Progress )
2020-09-01 14:07:48 -04:00
{
2023-04-25 13:27:03 -04:00
using namespace FMeshSimpleShapeApproximationLocals ;
2020-09-01 14:07:48 -04:00
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
FSimpleShapeFitsResult FitResult ;
2023-10-12 13:34:51 -04:00
ComputeSimpleShapeFits ( SourceMesh , false , true , false , MinDimension , bUseExactComputationForBox , FitResult , Progress ) ;
2021-02-01 14:43:20 -04:00
if ( Progress & & Progress - > Cancelled ( ) )
{
return ;
}
2020-09-01 14:07:48 -04:00
if ( FitResult . bHaveBox )
{
2023-04-25 13:27:03 -04:00
FOrientedBox3d BoxToUse = ReparameterizeBoxCloserToWorldFrame ( FitResult . Box ) ;
2020-09-01 14:07:48 -04:00
GeometryLock . Lock ( ) ;
2023-04-25 13:27:03 -04:00
ShapeSetOut . Boxes . Add ( FBoxShape3d ( BoxToUse ) ) ;
2020-09-01 14:07:48 -04:00
GeometryLock . Unlock ( ) ;
}
} ) ;
}
void FMeshSimpleShapeApproximation : : Generate_MinimalSpheres ( FSimpleShapeSet3d & ShapeSetOut )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
FSimpleShapeFitsResult FitResult ;
2023-10-12 13:34:51 -04:00
ComputeSimpleShapeFits ( SourceMesh , true , false , false , MinDimension , bUseExactComputationForBox , FitResult ) ;
2020-09-01 14:07:48 -04:00
if ( FitResult . bHaveSphere )
{
GeometryLock . Lock ( ) ;
ShapeSetOut . Spheres . Add ( FSphereShape3d ( FitResult . Sphere ) ) ;
GeometryLock . Unlock ( ) ;
}
} ) ;
}
void FMeshSimpleShapeApproximation : : Generate_Capsules ( FSimpleShapeSet3d & ShapeSetOut )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
FSimpleShapeFitsResult FitResult ;
2023-10-12 13:34:51 -04:00
ComputeSimpleShapeFits ( SourceMesh , false , false , true , MinDimension , bUseExactComputationForBox , FitResult ) ;
2020-09-01 14:07:48 -04:00
if ( FitResult . bHaveCapsule )
{
GeometryLock . Lock ( ) ;
2021-09-22 10:01:48 -04:00
ShapeSetOut . Capsules . Add ( UE : : Geometry : : FCapsuleShape3d ( FitResult . Capsule ) ) ;
2020-09-01 14:07:48 -04:00
GeometryLock . Unlock ( ) ;
}
} ) ;
}
2021-04-21 11:35:00 -04:00
void FMeshSimpleShapeApproximation : : Generate_ConvexHulls ( FSimpleShapeSet3d & ShapeSetOut , FProgressCancel * Progress )
2020-09-01 14:07:48 -04:00
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
FMeshConvexHull Hull ( & SourceMesh ) ;
Hull . bPostSimplify = bSimplifyHulls ;
Hull . MaxTargetFaceCount = HullTargetFaceCount ;
2023-10-12 13:34:51 -04:00
Hull . MinDimension = MinDimension ;
2020-09-01 14:07:48 -04:00
2021-04-21 11:35:00 -04:00
if ( Hull . Compute ( Progress ) )
2020-09-01 14:07:48 -04:00
{
GeometryLock . Lock ( ) ;
2022-04-01 16:04:37 -04:00
ShapeSetOut . Convexes . Emplace ( MoveTemp ( Hull . ConvexHull ) ) ;
GeometryLock . Unlock ( ) ;
}
} ) ;
}
void FMeshSimpleShapeApproximation : : Generate_ConvexHullDecompositions ( FSimpleShapeSet3d & ShapeSetOut , FProgressCancel * Progress )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
if ( Progress & & Progress - > Cancelled ( ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
// TODO: if (bSimplifyHulls), also consider simplifying the input?
FConvexDecomposition3 Decomposition ( SourceMesh ) ;
2023-11-21 18:01:32 -05:00
int32 NumAdditionalSplits = FMath : : FloorToInt32 ( float ( ConvexDecompositionMaxPieces ) * ConvexDecompositionSearchFactor ) ;
if ( bConvexDecompositionProtectNegativeSpace )
{
FNegativeSpaceSampleSettings Settings ;
Settings . bOnlyConnectedToHull = bIgnoreInternalNegativeSpace ;
Settings . MinRadius = NegativeSpaceMinRadius ;
Settings . ReduceRadiusMargin = NegativeSpaceTolerance ;
Settings . MinRadius = FMath : : Max ( 1 , ( NegativeSpaceMinRadius + NegativeSpaceTolerance ) * .5 ) ;
Settings . SampleMethod = FNegativeSpaceSampleSettings : : ESampleMethod : : VoxelSearch ;
Settings . bRequireSearchSampleCoverage = true ;
Settings . TargetNumSamples = 1 ; // let the sample coverage determine the number of spheres to place
Decomposition . InitializeNegativeSpace ( Settings ) ;
// Let negative space decide when to stop merging; target only 1 piece if negative space allows
NumAdditionalSplits + = ConvexDecompositionMaxPieces ;
ConvexDecompositionMaxPieces = 1 ;
}
2022-06-30 14:54:23 -04:00
Decomposition . Compute ( ConvexDecompositionMaxPieces , NumAdditionalSplits , ConvexDecompositionErrorTolerance , ConvexDecompositionMinPartThickness ) ;
2022-04-01 16:04:37 -04:00
for ( int32 HullIdx = 0 ; HullIdx < Decomposition . NumHulls ( ) ; HullIdx + + )
{
FDynamicMesh3 HullMesh = Decomposition . GetHullMesh ( HullIdx ) ;
2022-08-19 15:04:19 -04:00
if ( bSimplifyHulls & & FMeshConvexHull : : SimplifyHull ( HullMesh , HullTargetFaceCount , Progress ) = = false )
2022-04-01 16:04:37 -04:00
{
return ;
}
GeometryLock . Lock ( ) ;
ShapeSetOut . Convexes . Emplace ( MoveTemp ( HullMesh ) ) ;
2020-09-01 14:07:48 -04:00
GeometryLock . Unlock ( ) ;
}
} ) ;
}
void FMeshSimpleShapeApproximation : : Generate_ProjectedHulls ( FSimpleShapeSet3d & ShapeSetOut , EProjectedHullAxisMode AxisMode )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & Mesh = * SourceMeshes [ idx ] ;
FFrame3d ProjectionPlane ( FVector3d : : Zero ( ) , FVector3d : : UnitY ( ) ) ;
if ( AxisMode = = EProjectedHullAxisMode : : SmallestBoxDimension )
{
FAxisAlignedBox3d Bounds = Mesh . GetBounds ( ) ;
2021-03-17 19:32:44 -04:00
int32 AxisIndex = MinAbsElementIndex ( Bounds . Diagonal ( ) ) ;
check ( MinAbsElement ( Bounds . Diagonal ( ) ) = = Bounds . Diagonal ( ) [ AxisIndex ] ) ;
ProjectionPlane = FFrame3d ( FVector3d : : Zero ( ) , MakeUnitVector3 < double > ( AxisIndex ) ) ;
2020-09-01 14:07:48 -04:00
}
else if ( AxisMode = = EProjectedHullAxisMode : : SmallestVolume )
{
FMeshProjectionHull HullX ( & Mesh ) ;
HullX . ProjectionFrame = FFrame3d ( FVector3d : : Zero ( ) , FVector3d : : UnitX ( ) ) ;
HullX . MinThickness = FMathd : : Max ( MinDimension , 0 ) ;
bool bHaveX = HullX . Compute ( ) ;
FMeshProjectionHull HullY ( & Mesh ) ;
HullY . ProjectionFrame = FFrame3d ( FVector3d : : Zero ( ) , FVector3d : : UnitY ( ) ) ;
HullY . MinThickness = FMathd : : Max ( MinDimension , 0 ) ;
bool bHaveY = HullY . Compute ( ) ;
FMeshProjectionHull HullZ ( & Mesh ) ;
HullZ . ProjectionFrame = FFrame3d ( FVector3d : : Zero ( ) , FVector3d : : UnitZ ( ) ) ;
HullZ . MinThickness = FMathd : : Max ( MinDimension , 0 ) ;
bool bHaveZ = HullZ . Compute ( ) ;
int32 MinIdx = FMathd : : Min3Index (
( bHaveX ) ? TMeshQueries < FDynamicMesh3 > : : GetVolumeArea ( HullX . ConvexHull3D ) . X : TNumericLimits < double > : : Max ( ) ,
( bHaveY ) ? TMeshQueries < FDynamicMesh3 > : : GetVolumeArea ( HullY . ConvexHull3D ) . X : TNumericLimits < double > : : Max ( ) ,
( bHaveZ ) ? TMeshQueries < FDynamicMesh3 > : : GetVolumeArea ( HullZ . ConvexHull3D ) . X : TNumericLimits < double > : : Max ( ) ) ;
ProjectionPlane = ( MinIdx = = 0 ) ? HullX . ProjectionFrame : ( ( MinIdx = = 1 ) ? HullY . ProjectionFrame : HullZ . ProjectionFrame ) ;
}
else
{
2021-03-17 19:32:44 -04:00
ProjectionPlane = FFrame3d ( FVector3d : : Zero ( ) , MakeUnitVector3 < double > ( ( int32 ) AxisMode ) ) ;
2020-09-01 14:07:48 -04:00
}
FMeshProjectionHull Hull ( & Mesh ) ;
Hull . ProjectionFrame = ProjectionPlane ;
Hull . MinThickness = FMathd : : Max ( MinDimension , 0 ) ;
Hull . bSimplifyPolygon = bSimplifyHulls ;
Hull . MinEdgeLength = HullSimplifyTolerance ;
Hull . DeviationTolerance = HullSimplifyTolerance ;
if ( Hull . Compute ( ) )
{
FConvexShape3d NewConvex ;
NewConvex . Mesh = MoveTemp ( Hull . ConvexHull3D ) ;
GeometryLock . Lock ( ) ;
ShapeSetOut . Convexes . Add ( NewConvex ) ;
GeometryLock . Unlock ( ) ;
}
} ) ;
}
2022-05-30 16:48:48 -04:00
void FMeshSimpleShapeApproximation : : Generate_LevelSets ( FSimpleShapeSet3d & ShapeSetOut , FProgressCancel * Progress )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 MeshIndex )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ MeshIndex ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
2020-09-01 14:07:48 -04:00
2022-05-30 16:48:48 -04:00
const FAxisAlignedBox3d Bounds = SourceMeshes [ MeshIndex ] - > GetBounds ( ) ;
const double CellSize = Bounds . MaxDim ( ) / LevelSetGridResolution ;
TMeshAABBTree3 < FDynamicMesh3 > Spatial ( SourceMeshes [ MeshIndex ] ) ;
TSweepingMeshSDF < FDynamicMesh3 > SDF ;
SDF . Mesh = SourceMeshes [ MeshIndex ] ;
SDF . Spatial = & Spatial ;
SDF . ComputeMode = TSweepingMeshSDF < FDynamicMesh3 > : : EComputeModes : : NarrowBand_SpatialFloodFill ;
2022-06-30 14:54:23 -04:00
SDF . CellSize = ( float ) CellSize ;
2022-05-30 16:48:48 -04:00
SDF . NarrowBandMaxDistance = 2.0 * CellSize ;
2022-06-30 14:54:23 -04:00
SDF . ExactBandWidth = FMath : : CeilToInt32 ( SDF . NarrowBandMaxDistance / CellSize ) ;
2022-05-30 16:48:48 -04:00
SDF . ExpandBounds = 2.0 * CellSize * FVector3d : : One ( ) ;
if ( SDF . Compute ( Bounds ) )
{
FLevelSetShape3d NewLevelSet ;
NewLevelSet . GridTransform = FTransform ( ( FVector3d ) SDF . GridOrigin ) ;
NewLevelSet . Grid = MoveTemp ( SDF . Grid ) ;
NewLevelSet . CellSize = SDF . CellSize ;
GeometryLock . Lock ( ) ;
ShapeSetOut . LevelSets . Add ( MoveTemp ( NewLevelSet ) ) ;
GeometryLock . Unlock ( ) ;
}
else if ( Progress )
{
GeometryLock . Lock ( ) ;
Progress - > AddWarning ( LOCTEXT ( " Generate_LevelSets_Failed " , " Generating a new Level Set failed " ) , FProgressCancel : : EMessageLevel : : UserWarning ) ;
GeometryLock . Unlock ( ) ;
}
} ) ;
}
2020-09-01 14:07:48 -04:00
void FMeshSimpleShapeApproximation : : Generate_MinVolume ( FSimpleShapeSet3d & ShapeSetOut )
{
FCriticalSection GeometryLock ;
ParallelFor ( SourceMeshes . Num ( ) , [ & ] ( int32 idx )
{
if ( GetDetectedSimpleShape ( SourceMeshCaches [ idx ] , ShapeSetOut , GeometryLock ) )
{
return ;
}
const FDynamicMesh3 & SourceMesh = * SourceMeshes [ idx ] ;
FOrientedBox3d AlignedBox = FOrientedBox3d ( SourceMesh . GetBounds ( ) ) ;
2023-10-12 13:34:51 -04:00
double MinHalfDimension = MinDimension * .5 ;
for ( int32 SubIdx = 0 ; SubIdx < 3 ; + + SubIdx )
{
AlignedBox . Extents [ SubIdx ] = FMath : : Max ( MinHalfDimension , AlignedBox . Extents [ SubIdx ] ) ;
}
2020-09-01 14:07:48 -04:00
FSimpleShapeFitsResult FitResult ;
2023-10-12 13:34:51 -04:00
ComputeSimpleShapeFits ( SourceMesh , true , true , true , MinDimension , bUseExactComputationForBox , FitResult ) ;
2020-09-01 14:07:48 -04:00
double Volumes [ 4 ] ;
Volumes [ 0 ] = AlignedBox . Volume ( ) ;
Volumes [ 1 ] = ( FitResult . bHaveBox ) ? FitResult . Box . Volume ( ) : TNumericLimits < double > : : Max ( ) ;
Volumes [ 2 ] = ( FitResult . bHaveSphere ) ? FitResult . Sphere . Volume ( ) : TNumericLimits < double > : : Max ( ) ;
Volumes [ 3 ] = ( FitResult . bHaveCapsule ) ? FitResult . Capsule . Volume ( ) : TNumericLimits < double > : : Max ( ) ;
int32 MinVolIndex = 0 ;
for ( int32 k = 1 ; k < 4 ; + + k )
{
if ( Volumes [ k ] < Volumes [ MinVolIndex ] )
{
MinVolIndex = k ;
}
}
if ( Volumes [ MinVolIndex ] < TNumericLimits < double > : : Max ( ) )
{
GeometryLock . Lock ( ) ;
switch ( MinVolIndex )
{
case 0 :
ShapeSetOut . Boxes . Add ( FBoxShape3d ( AlignedBox ) ) ;
break ;
case 1 :
ShapeSetOut . Boxes . Add ( FBoxShape3d ( FitResult . Box ) ) ;
break ;
case 2 :
ShapeSetOut . Spheres . Add ( FSphereShape3d ( FitResult . Sphere ) ) ;
break ;
case 3 :
2021-09-22 10:01:48 -04:00
ShapeSetOut . Capsules . Add ( UE : : Geometry : : FCapsuleShape3d ( FitResult . Capsule ) ) ;
2020-09-01 14:07:48 -04:00
break ;
}
GeometryLock . Unlock ( ) ;
}
} ) ;
2022-05-30 16:48:48 -04:00
}
# undef LOCTEXT_NAMESPACE