UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Blend.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3// Port of geometry3Sharp / gsShapeModels MeshVoxelBlendOp
4
5#pragma once
6
10
12
13#include "Async/ParallelFor.h"
14
15namespace UE
16{
17namespace Geometry
18{
19
20using namespace UE::Math;
21
22template<typename TriangleMeshType>
24{
25public:
26
28 {
29 }
30
34
37
38 // exponent used in blend; controls shape (larger number == sharper blend)
39 double BlendPower = 2.0;
40
41 // blend distance
42 double BlendFalloff = 5.0;
43
44 // size of the cells used when sampling the distance field
45 double GridCellSize = 1.0;
46
47 // size of the cells used when meshing the output (marching cubes' cube size)
48 double MeshCellSize = 1.0;
49
50 // if true, perform a smooth subtraction instead of a smooth union
51 bool bSubtract = false;
52
53 // Set cell sizes to hit the target voxel counts along the max dimension of the bounds
60
62 TFunction<bool(void)> CancelF = []()
63 {
64 return false;
65 };
66
67protected:
68
70
72
77
78public:
79
80 bool Validate()
81 {
82 bool bHasSourcesWithBounds = Sources.Num() > 0 && Sources.Num() == SourceBounds.Num();
83 for (int SourceIdx = 0; SourceIdx < Sources.Num(); SourceIdx++)
84 {
85 if (Sources[SourceIdx] == nullptr)
86 {
87 return false;
88 }
89 }
90
91 bool bValidParams = BlendPower > 0 && BlendFalloff > 0 && GridCellSize > 0 && MeshCellSize > 0 && FMath::IsFinite(GridCellSize) && FMath::IsFinite(MeshCellSize);
93 }
94
99 {
101 if (!ensure(Validate()))
102 {
103 // give up and return and empty result on invalid parameters
104 return MarchingCubes;
105 }
106
109 return MarchingCubes;
110 }
111
112protected:
113
122
124 {
125 ComputedSpatials.SetNum(Sources.Num());
126 ParallelFor(Sources.Num(), [this, bReuseComputed](int SourceIdx)
127 {
128 if (!bReuseComputed || ComputedSpatials[SourceIdx].GetMesh() != Sources[SourceIdx] || !ComputedSpatials[SourceIdx].IsValid(false))
129 {
130 ComputedSpatials[SourceIdx].SetMesh(Sources[SourceIdx], true);
131 }
132 }
133 );
134 }
135
137 {
138 ComputeSpatials(bReuseComputed);
139
140 if (!bReuseComputed || ComputedSDFs.Num() != Sources.Num())
141 {
142 ComputedSDFs.Reserve(Sources.Num());
143 SDFMaxDistances.Reserve(Sources.Num());
144 for (int i = 0; i < Sources.Num(); i++)
145 {
146 ComputedSDFs.Emplace(Sources[i], GridCellSize, &ComputedSpatials[i], false); // not auto-building here, to share code w/ need-rebuild path below
147 SDFMaxDistances.Add(0.0);
148 }
149 }
150
151 double NeedDistance = BlendFalloff;
152 ParallelFor(Sources.Num(), [this, bReuseComputed, NeedDistance](int SourceIdx)
153 {
154 // previously computed sdf has signs computed out to (at least) the required distance, no need to recompute
155 if (bReuseComputed && NeedDistance <= SDFMaxDistances[SourceIdx])
156 {
157 return;
158 }
159
160 // TODO: if we do have a previously computed sdf, and want to reuse computed, but need more distance
161 // we could expand the sdf here instead of throwing it out and fully recomputing.
162
163 float UseMaxOffset = (float)BlendFalloff;
164
165 ComputedSDFs[SourceIdx].MaxOffsetDistance = UseMaxOffset;
166 ComputedSDFs[SourceIdx].CellSize = GridCellSize;
167
168 ComputedSDFs[SourceIdx].CancelF = CancelF;
169 ComputedSDFs[SourceIdx].Initialize();
170
171 SDFMaxDistances[SourceIdx] = UseMaxOffset;
172 });
173 }
174
176 {
177 if (CancelF())
178 {
179 return;
180 }
181
182 ComputeSpatials(bReuseComputed);
183
184 if (CancelF())
185 {
186 return;
187 }
188
189 ComputeLazySDFs(bReuseComputed);
190
191 if (CancelF())
192 {
193 return;
194 }
195
197 Blend.BlendPower = BlendPower;
198 Blend.bSubtract = bSubtract;
199 Blend.Children.Reserve(Sources.Num());
202 for (int SourceIdx = 0; SourceIdx < Sources.Num(); SourceIdx++)
203 {
204 Interpolants.Add(ComputedSDFs[SourceIdx].MakeInterpolant());
205 SkeletalFields.Emplace(&Interpolants[SourceIdx], BlendFalloff);
206 Blend.Children.Add(&SkeletalFields[SourceIdx]);
207 }
208
209 MarchingCubes.CancelF = CancelF;
210
211 MarchingCubes.CubeSize = MeshCellSize;
212
213 MarchingCubes.IsoValue = TDistanceFieldToSkeletalField<TBoundedImplicitFunction3<double>, double>::ZeroIsocontour;
214 MarchingCubes.Bounds = CombinedBounds;
215 MarchingCubes.Bounds.Expand(BlendFalloff);
216 MarchingCubes.RootMode = ERootfindingModes::LerpSteps;
217 MarchingCubes.RootModeSteps = 3;
218
219 if (CancelF())
220 {
221 return;
222 }
223
225 for (const TriangleMeshType* Source : Sources)
226 {
227 for (int VID = 0; VID < Source->MaxVertexID(); VID++)
228 {
229 if (!Source->IsVertex(VID))
230 {
231 continue;
232 }
233
234 FVector3d Seed = Source->GetVertex(VID);
235 // Only add vertices that are inside the spatial bounds (only vertices that are not on any triangles will be outside)
236 if (MarchingCubes.Bounds.Contains(Seed))
237 {
238 Seeds.Add(Seed);
239 }
240 }
241 }
242
243 MarchingCubes.Implicit = [&Blend](const FVector3d& Pt) {return Blend.Value(Pt);}; //-V1047 - This lambda is cleared before routine exit
244
245 MarchingCubes.GenerateContinuation(Seeds);
246
247 if (Seeds.Num() > 0 && MarchingCubes.Triangles.Num() == 0)
248 {
249 // fall back to full generation; seeds failed!
250 MarchingCubes.Generate();
251 }
252
253 // TODO: refactor FMarchingCubes to not retain the implicit function, or refactor this function so the implicit function isn't invalid after returning,
255 MarchingCubes.Implicit = nullptr;
256 }
257};
258
259
260} // end namespace UE::Geometry
261} // end namespace UE
#define ensure( InExpression)
Definition AssertionMacros.h:464
void ParallelFor(int32 Num, TFunctionRef< void(int32)> Body, bool bForceSingleThread, bool bPumpRenderingThread=false)
Definition ParallelFor.h:481
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
const bool
Definition NetworkReplayStreaming.h:178
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition AndroidPlatformMisc.h:14
Definition MarchingCubes.h:51
Definition MeshShapeGenerator.h:19
void Reset()
Definition MeshShapeGenerator.h:80
Definition Blend.h:24
const FMeshShapeGenerator & Generate(bool bReuseComputed=false)
Definition Blend.h:98
FAxisAlignedBox3d CombinedBounds
Intermediate.
Definition Blend.h:73
void ComputeBounds()
Definition Blend.h:114
TArray< double > SDFMaxDistances
Definition Blend.h:76
TArray< const TriangleMeshType * > Sources
Definition Blend.h:35
double GridCellSize
Definition Blend.h:45
TArray< FAxisAlignedBox3d > SourceBounds
Definition Blend.h:36
void ComputeSpatials(bool bReuseComputed)
Definition Blend.h:123
bool bSubtract
Definition Blend.h:51
void SetCellSizesAndFalloff(FAxisAlignedBox3d Bounds, double BlendFalloffIn, int TargetInputVoxelCount, int TargetOutputVoxelCount)
Definition Blend.h:54
void ComputeLazySDFs(bool bReuseComputed)
Definition Blend.h:136
FMarchingCubes MarchingCubes
Definition Blend.h:69
double BlendFalloff
Definition Blend.h:42
double BlendPower
Definition Blend.h:39
void GenerateBlendAnalytic(bool bReuseComputed)
Definition Blend.h:175
TArray< TCachingMeshSDF< TriangleMeshType > > ComputedSDFs
Definition Blend.h:75
TFunction< bool(void)> CancelF
Definition Blend.h:62
bool Validate()
Definition Blend.h:80
double MeshCellSize
Definition Blend.h:48
TArray< TMeshAABBTree3< TriangleMeshType > > ComputedSpatials
Definition Blend.h:74
virtual ~TImplicitBlend()
Definition Blend.h:27
Definition Sphere.cpp:10
Definition AdvancedWidgetsModule.cpp:13
static TAxisAlignedBox3< double > Empty()
Definition BoxTypes.h:382
void Contain(const TVector< RealType > &V)
Definition BoxTypes.h:438
RealType MaxDim() const
Definition BoxTypes.h:598
Definition ImplicitFunctions.h:135
TAxisAlignedBox3< RealType > Bounds()
Definition ImplicitFunctions.h:146
Definition ImplicitFunctions.h:177
RealType BlendPower
Definition ImplicitFunctions.h:179