UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Morphology.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3// Port of geometry3Sharp / gsShapeModels MeshMorphologyOp
4
5#pragma once
6
7#include "MeshAdapter.h"
8
10
14
16#include "MeshQueries.h"
17
18namespace UE
19{
20namespace Geometry
21{
22
23template<typename TriangleMeshType>
25{
26public:
28 enum class EMorphologyOp
29 {
31 Dilate = 0,
32
34 Contract = 1,
35
37 Close = 2,
38
40 Open = 3
41 };
42
46 const TriangleMeshType* Source = nullptr;
49
50 // Distance of offset; should be positive
51 double Distance = 1.0;
52
53 // size of the cells used when sampling the distance field
54 double GridCellSize = 1.0;
55
56 // size of the cells used when meshing the output (marching cubes' cube size)
57 double MeshCellSize = 1.0;
58
60 bool bUseCustomBounds = false;
61
64
65 // Set cell sizes to hit the target voxel counts along the max dimension of the bounds
72
73 // Set input grid cell size to hit the target voxel counts along the max dimension of the bounds
75 {
76 int UseTargetVoxelCount = FMath::Min(MaxTargetVoxelCount, TargetInputVoxelCount);
78 }
79
80 // Set output meshing cell size to hit the target voxel counts along the max dimension of the bounds
82 {
83 int UseTargetVoxelCount = FMath::Min(MaxTargetVoxelCount, TargetInputVoxelCount);
85 }
86
88 TFunction<bool(void)> CancelF = []()
89 {
90 return false;
91 };
92
93protected:
94 // Stores result (returned as a const FMeshShapeGenerator)
96
97 // computed in first pass, re-used in second
99
100public:
101 bool Validate()
102 {
103 bool bValidMeshAndSpatial = Source != nullptr && SourceSpatial != nullptr && SourceSpatial->IsValid(false);
104 bool bValidParams = Distance > 0 && GridCellSize > 0 && MeshCellSize > 0 && FMath::IsFinite(MeshCellSize);
106 }
107
109 {
111 if ((Source && Source->TriangleCount() == 0) || !ensure(Validate()))
112 {
113 // return an empty result if input is empty or parameters are not valid
114 return MarchingCubes;
115 }
116
119 switch (MorphologyOp)
120 {
124 break;
125 }
126
128
130 {
132 }
133
134 return MarchingCubes;
135 }
136
137protected:
138
139 template<typename MeshType>
141
143 {
145
147
150
151 ComputedSDF.Spatial = SourceSpatial;
152 ComputedSDF.ComputeMode = MeshSDFType::EComputeModes::NarrowBand_SpatialFloodFill;
153
154 double UseGridCellSize = GetSafeCellSize(2*UnsignedOffset + SourceSpatial->GetBoundingBox().MaxDim(), GridCellSize, 2);
157 ComputedSDF.NarrowBandMaxDistance = NarrowBandMaxDistance;
158 ComputedSDF.ExactBandWidth = FMath::CeilToInt32(ComputedSDF.NarrowBandMaxDistance / ComputedSDF.CellSize);
159
160 // for meshes with long triangles relative to the width of the narrow band, don't use the AABB tree
162 if (!ComputedSDF.ShouldUseSpatial(ComputedSDF.ExactBandWidth, ComputedSDF.CellSize, AvgEdgeLen))
163 {
164 ComputedSDF.Spatial = nullptr;
165 ComputedSDF.ComputeMode = MeshSDFType::EComputeModes::NarrowBandOnly;
166 }
167
168 {
170 ComputedSDF.Compute(SourceSpatial->GetBoundingBox());
171 }
172
173 TTriLinearGridInterpolant<MeshSDFType> Interpolant = ComputedSDF.MakeInterpolant();
174
177 {
179 }
180 else
181 {
182 MarchingCubes.Bounds = SourceSpatial->GetBoundingBox();
184 if (MarchingCubes.IsoValue < 0)
185 {
186 MarchingCubes.Bounds.Expand(ComputedSDF.NarrowBandMaxDistance);
187 }
188 }
191
193
194 if (CancelF())
195 {
196 return;
197 }
198
199 MarchingCubes.Implicit = [Interpolant](const FVector3d& Pt)
200 {
201 return -Interpolant.Value(Pt);
202 };
204
205 {
208 }
209
210 // TODO: refactor FMarchingCubes to not retain the implicit function, or refactor this function so the implicit function isn't invalid after returning,
212 MarchingCubes.Implicit = nullptr;
213 }
214
216 {
218
220
221 if (MarchingCubes.Triangles.Num() == 0)
222 {
224 return;
225 }
226
229
231 if (!bUseCustomBounds)
232 {
233 Bounds.Expand(MeshCellSize); // (because mesh may spill one cell over bounds)
234 }
235
237 SecondSDF.Mesh = &MCAdapter;
238
239 // Adjust cell size to not overflow w/ the added UnsignedOffset
240 double UseGridCellSize = GetSafeCellSize(2*UnsignedOffset + Bounds.MaxDim(), GridCellSize, 2);
241
242 SecondSDF.CellSize = (float)UseGridCellSize;
243 SecondSDF.Spatial = nullptr;
244
245
246 SecondSDF.NarrowBandMaxDistance = UnsignedOffset + SecondSDF.CellSize;
247 SecondSDF.ExactBandWidth = FMath::CeilToInt32(SecondSDF.NarrowBandMaxDistance / SecondSDF.CellSize);
248
249 if (SecondSDF.ExactBandWidth > 1) // for larger band width, prefer using the AABB tree to do one distance per cell. TODO: tune?
250 {
252 SecondSpatial.Build();
253 SecondSDF.Spatial = &SecondSpatial;
254 SecondSDF.ComputeMode = MeshSDFType::EComputeModes::NarrowBand_SpatialFloodFill;
255 if (!bUseCustomBounds)
256 {
257 Bounds = SecondSpatial.GetBoundingBox(); // Use the tighter bounds from the AABB tree since we have it
258 }
259 }
260 else
261 {
262 SecondSDF.ComputeMode = MeshSDFType::EComputeModes::NarrowBandOnly;
263 }
264
265
266 if (CancelF())
267 {
268 return;
269 }
270
271 {
273 SecondSDF.Compute(Bounds);
274 }
275 TTriLinearGridInterpolant<MeshSDFType> Interpolant = SecondSDF.MakeInterpolant();
276
279 MarchingCubes.Bounds = Bounds;
280 if (!bUseCustomBounds)
281 {
283 if (MarchingCubes.IsoValue < 0)
284 {
286 }
287 // Make sure the CubeSize is still safe after expanding the bounds
289 }
290
291
292 if (CancelF())
293 {
294 return;
295 }
296
297 MarchingCubes.Implicit = [Interpolant](const FVector3d& Pt)
298 {
299 return -Interpolant.Value(Pt);
300 };
302
303 {
306 }
307
308 // TODO: refactor FMarchingCubes to not retain the implicit function, or refactor this function so the implicit function isn't invalid after returning,
310 MarchingCubes.Implicit = nullptr;
311 }
312
313private:
314 // Set a max target voxel count s.t. the VoxelCount^3 does not overflow int32 linearized grid indices
315 // (and to reduce the chance of running out of memory for a very dense grid, generally)
316 static constexpr int32 MaxTargetVoxelCount = 1200;
317
318 // Adjust cell size so that a cell count based on (BoundsWidth/InitialCellSize + ExtraCellCount) should not
319 // (too far) exceed MaxTargetVoxelCount. (Assumes ExtraCellCount is smaller MaxTargetVoxelCount)
320 static double GetSafeCellSize(double BoundsWidth, double InitialCellSize, int32 ExtraCellCount)
321 {
322 if (BoundsWidth + (double)ExtraCellCount * InitialCellSize > InitialCellSize * (double)MaxTargetVoxelCount)
323 {
324 return BoundsWidth / (double)(MaxTargetVoxelCount - ExtraCellCount);
325 }
326 else
327 {
328 return InitialCellSize;
329 }
330 }
331};
332
333
334} // end namespace UE::Geometry
335} // end namespace UE
#define ensure( InExpression)
Definition AssertionMacros.h:464
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define TRACE_CPUPROFILER_EVENT_SCOPE(Name)
Definition CpuProfilerTrace.h:528
const bool
Definition NetworkReplayStreaming.h:178
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
Definition AndroidPlatformMisc.h:14
static RealType Abs(const RealType Value)
Definition MathUtil.h:215
Definition MarchingCubes.h:51
TFunction< bool(void)> CancelF
Definition MarchingCubes.h:106
double CubeSize
Definition MarchingCubes.h:74
FMeshShapeGenerator & Generate() override
Definition MarchingCubes.h:134
double IsoValue
Definition MarchingCubes.h:62
ERootfindingModes RootMode
Definition MarchingCubes.h:97
TAxisAlignedBox3< double > Bounds
Definition MarchingCubes.h:68
TFunction< double(TVector< double >)> Implicit
Definition MarchingCubes.h:56
bool bEnableValueCaching
Definition MarchingCubes.h:87
Definition MeshShapeGenerator.h:19
void Reset()
Definition MeshShapeGenerator.h:80
TArray< FIndex3i > Triangles
Definition MeshShapeGenerator.h:35
TArray< FVector3d > Vertices
Definition MeshShapeGenerator.h:22
Definition Morphology.h:25
double GridCellSize
Definition Morphology.h:54
EMorphologyOp
Definition Morphology.h:29
EMorphologyOp MorphologyOp
Definition Morphology.h:48
void SetMeshCellSize(FAxisAlignedBox3d Bounds, double DistanceIn, int TargetInputVoxelCount)
Definition Morphology.h:81
double NarrowBandMaxDistance
Definition Morphology.h:98
const TriangleMeshType * Source
Definition Morphology.h:46
TFunction< bool(void)> CancelF
Definition Morphology.h:88
FAxisAlignedBox3d CustomBounds
Definition Morphology.h:63
bool Validate()
Definition Morphology.h:101
double MeshCellSize
Definition Morphology.h:57
void ComputeFirstPass(double UnsignedOffset, double SignedOffset)
Definition Morphology.h:142
void SetGridCellSize(FAxisAlignedBox3d Bounds, double DistanceIn, int TargetInputVoxelCount)
Definition Morphology.h:74
double Distance
Definition Morphology.h:51
const FMeshShapeGenerator & Generate()
Definition Morphology.h:108
TMeshAABBTree3< TriangleMeshType > * SourceSpatial
Definition Morphology.h:47
void SetCellSizesAndDistance(FAxisAlignedBox3d Bounds, double DistanceIn, int TargetInputVoxelCount, int TargetOutputVoxelCount)
Definition Morphology.h:66
FMarchingCubes MarchingCubes
Definition Morphology.h:95
bool bUseCustomBounds
Definition Morphology.h:60
void ComputeSecondPass(double UnsignedOffset, double SignedOffset)
Definition Morphology.h:215
Definition MeshAABBTree3.h:61
static double AverageEdgeLength(const TriangleMeshType &Mesh)
Compute the mean edge length for the given mesh.
Definition MeshQueries.h:483
Definition SparseNarrowBandMeshSDF.h:49
const TriangleMeshType * Mesh
Definition SparseNarrowBandMeshSDF.h:53
Definition GridInterpolant.h:29
RealType Value(const TVector< RealType > &Pt) const
Definition GridInterpolant.h:71
Definition AdvancedWidgetsModule.cpp:13
void Expand(RealType Radius)
Definition BoxTypes.h:618
RealType MaxDim() const
Definition BoxTypes.h:598
Definition MeshAdapter.h:395