Files
2025-05-18 13:04:45 +08:00

217 lines
8.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "ShapeApproximation/SimpleShapeSet3.h"
#define UE_API DYNAMICMESH_API
class FProgressCancel;
namespace UE {
namespace Geometry {
/**
* EDetectedSimpleShapeType is used to identify auto-detected simple shapes for a mesh/etc
*/
enum class EDetectedSimpleShapeType
{
/** Object is not a simple shape */
None = 0,
/** Object has been identified as a sphere */
Sphere = 2,
/** Object has been identified as a box */
Box = 4,
/** Object has been identified as a capsule */
Capsule = 8,
/** Object has been identified as a Convex */
Convex = 16
};
/**
* FMeshSimpleShapeApproximation can calculate various "simple" shape approximations for a set of meshes,
* by fitting various primitives/hulls/etc to each mesh. The assumption is that the input mesh(es) are
* already partitioned into pieces.
*
* There are various Generate_X() functions which apply different strategies, generally to fit a containing
* simple shape or hull to the mesh. However in addition to these explicit strategies, input meshes that
* are very close to approximations of spheres/boxes/capsules (ie basically meshed versions of these primitives)
* can be identified and used directly, skipping the fitting process.
*
*/
class FMeshSimpleShapeApproximation
{
public:
//
// configuration parameters
//
/** Should spheres be auto-detected */
bool bDetectSpheres = true;
/** Should boxes be auto-detected */
bool bDetectBoxes = true;
/** Should capsules be auto-detected */
bool bDetectCapsules = true;
/** Should convex be auto-detected */
bool bDetectConvexes = true;
/** minimal dimension of fit shapes, eg thickness/radius/etc (currently only enforced in certain cases) */
double MinDimension = 0.0;
/** should hulls be simplified as a post-process */
bool bSimplifyHulls = true;
/** target number of triangles when simplifying 3D convex hulls */
int32 HullTargetFaceCount = 50;
/** simplification tolerance when simplifying 2D convex hulls, eg for swept/projected hulls */
double HullSimplifyTolerance = 1.0;
/** Whether to use apply an edge-length based simplification to the input before running convex decompositions. Useful for very dense input meshes where the decomposition will be slow to compute. */
bool bDecompositionPreSimplifyWithEdgeLength = false;
/** If enabled by the above bool flag, pre-simplify input geometry to this edge length before computing convex decompositions. */
double DecompositionPreSimplifyEdgeLength = 1.0;
/** How many convex pieces to target per mesh when creating convex decompositions. Ignored if < 1. If ErrorTolerance or ProtectNegativeSpace are used, can create fewer pieces. */
int32 ConvexDecompositionMaxPieces = 1;
/** Whether to use the above Max Pieces to drive the convex decomposition. Otherwise, will allow the error tolerances / negative space protection settings to drive the number of pieces generated. */
bool bUseConvexDecompositionMaxPieces = true;
/**
* How much additional decomposition decomposition + merging to do, as a fraction of max pieces. Larger values can help better-cover small features, while smaller values create a cleaner decomposition with less overlap between hulls.
* Note: Not used if bConvexDecompositionProtectNegativeSpace is true.
*/
float ConvexDecompositionSearchFactor = .5;
/** Error tolerance to guide convex decomposition (in cm); we stop adding new parts if the volume error is below the threshold. For volumetric errors, value will be cubed. */
double ConvexDecompositionErrorTolerance = 0;
/** Minimum part thickness for convex decomposition (in cm); hulls thinner than this will be merged into adjacent hulls, if possible. */
double ConvexDecompositionMinPartThickness = .1;
/** Whether to use 'navigation-driven' convex decomposition -- using NegativeSpaceTolerance and NegativeSpaceMinRadius to define space that the decomposition hulls cannot occupy */
bool bConvexDecompositionProtectNegativeSpace = false;
/** Negative space closer to the input than this tolerance distance can be filled in */
double NegativeSpaceTolerance = 3;
/** Minimum radius of negative space to protect; tunnels with radius smaller than this could be filled in */
double NegativeSpaceMinRadius = 10;
/** Whether to ignore negative space that is not accessible by traversing from the convex hull (via paths w/ radius of at least Negative Space Tolerance) */
bool bIgnoreInternalNegativeSpace = true;
bool bUseExactComputationForBox = false;
/** Level Set Grid resolution along longest axis */
int32 LevelSetGridResolution = 10;
//
// setup/initialization
//
/**
* Initialize internal mesh sets. This also detects/caches the precise simple shape fits controlled
* by bDetectSpheres/etc above, so those cannot be modified without calling InitializeSourceMeshes() again.
* The TSharedPtrs are stored, rather than making a copy of the input meshes
* @param bIsParallelSafe if true
*/
UE_API void InitializeSourceMeshes(const TArray<const FDynamicMesh3*>& InputMeshSet);
//
// approximation generators
//
/**
* Fit containing axis-aligned boxes to each input mesh and store in ShapeSetOut
*/
UE_API void Generate_AlignedBoxes(FSimpleShapeSet3d& ShapeSetOut);
/**
* Fit containing minimal-volume oriented boxes to each input mesh and store in ShapeSetOut
*/
UE_API void Generate_OrientedBoxes(FSimpleShapeSet3d& ShapeSetOut, FProgressCancel* Progress = nullptr);
/**
* Fit containing minimal-volume spheres to each input mesh and store in ShapeSetOut
*/
UE_API void Generate_MinimalSpheres(FSimpleShapeSet3d& ShapeSetOut);
/**
* Fit containing approximate-minimum-volume capsules to each input mesh and store in ShapeSetOut
* @warning the capsule is fit by first fitting a line to the vertices, and then containing the points, so the fit can deviate quite a bit from a truly "minimal" capsule
*/
UE_API void Generate_Capsules(FSimpleShapeSet3d& ShapeSetOut);
/**
* Calculate 3D Convex Hulls for each input mesh and store in ShapeSetOut.
* Each convex hull is stored as a triangle mesh, and optionally simplified if bSimplifyHulls=true
*/
UE_API void Generate_ConvexHulls(FSimpleShapeSet3d& ShapeSetOut, FProgressCancel* Progress = nullptr);
/**
* Calculate multiple 3D Convex Hulls for each input mesh and store in ShapeSetOut.
* Each convex hull is stored as a triangle mesh, and optionally simplified if bSimplifyHulls=true
*/
UE_API void Generate_ConvexHullDecompositions(FSimpleShapeSet3d& ShapeSetOut, FProgressCancel* Progress = nullptr);
/** Type/Mode for deciding 3D axis to use in Generate_ProjectedHulls() */
enum class EProjectedHullAxisMode
{
/** Use Unit X axis */
X = 0,
/** Use Unit Y axis */
Y = 1,
/** Use Unit Z axis */
Z = 2,
/** Use X/Y/Z axis with smallest axis-aligned-bounding-box dimension */
SmallestBoxDimension = 3,
/** Compute projected hull for each of X/Y/Z axes and use the one that has the smallest volume */
SmallestVolume = 4
};
/**
* Calculate Projected Convex Hulls for each input mesh and store in ShapeSetOut.
* A Projected Hull is computed by first projecting all the mesh vertices to a plane, computing a 2D convex hull polygon,
* and then sweeping the polygon in 3D to contain all the mesh vertices. The 2D convex hull polygons
* are optionally simplified if bSimplifyHulls=true.
* @param AxisMode which axis to use for the planar projection
*/
UE_API void Generate_ProjectedHulls(FSimpleShapeSet3d& ShapeSetOut, EProjectedHullAxisMode AxisMode);
UE_API void Generate_LevelSets(FSimpleShapeSet3d& ShapeSetOut, FProgressCancel* Progress = nullptr);
/**
* Fit containing axis-aligned box, oriented box, capsule, and sphere to each input mesh, and
* store the one with smallest volume in ShapeSetOut
*/
UE_API void Generate_MinVolume(FSimpleShapeSet3d& ShapeSetOut);
protected:
TArray<const FDynamicMesh3*> SourceMeshes;
struct FSourceMeshCache
{
EDetectedSimpleShapeType DetectedType = EDetectedSimpleShapeType::None;
FSphere3d DetectedSphere;
FOrientedBox3d DetectedBox;
FCapsule3d DetectedCapsule;
};
TArray<FSourceMeshCache> SourceMeshCaches;
UE_API void DetectAndCacheSimpleShapeType(const FDynamicMesh3* SourceMesh, FSourceMeshCache& CacheOut);
UE_API bool GetDetectedSimpleShape(const FSourceMeshCache& Cache,
FSimpleShapeSet3d& ShapeSetOut, FCriticalSection& ShapeSetLock);
};
} // end namespace UE::Geometry
} // end namespace UE
#undef UE_API