UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SweepingMeshSDF.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3// Port of geometry3Sharp MeshSignedDistanceGrid
4
5#pragma once
6
7#include "MathUtil.h"
8#include "MeshQueries.h"
9#include "Util/IndexUtil.h"
11#include "Spatial/DenseGrid3.h"
12#include "Async/ParallelFor.h"
13#include "Misc/ScopeLock.h"
15#include "Math/NumericLimits.h"
16
17namespace UE
18{
19namespace Geometry
20{
21
22
44template <class TriangleMeshType, bool bScalarCellSize = true>
46{
47public:
48
49 // Type to use for CellSize
50 using CellSizeType = std::conditional_t<bScalarCellSize, float, FVector3f>;
51 // Type to use for CellSize, but in double precision
52 using CellSizeTyped = std::conditional_t<bScalarCellSize, double, FVector3d>;
53 // Unit cube, in CellSizeType
54 static inline CellSizeType UnitCellSize() { return CellSizeType(1); };
55
56 // INPUTS
57
61
66
67 // Width of the band around triangles for which exact Distances are computed
68 // (In fact this is conservative, the band is often larger locally)
70
71 // Bounds of Grid will be expanded this much in positive and negative directions.
72 // Useful for if you want field to extend outwards.
74
75 // Most of this parallelizes very well, makes a huge speed difference
76 bool bUseParallel = true;
77
78 // If the number of cells in any dimension may exceed this, CellSize will be automatically increased to keep cell count reasonable
80
81
82 // The narrow band is always computed exactly, and the full Grid is always signed.
83 // Can also fill in the rest of the full Grid with fast sweeping. This is
84 // quite computationally intensive, though, and not parallelizable
85 // (time only depends on Grid resolution)
93
94 // how wide of narrow band should we compute. This value is
95 // currently only used NarrowBand_SpatialFloodFill, as
96 // we can efficiently explore the space
97 // (in that case ExactBandWidth is only used to initialize the grid extents, and this is used instead during the flood fill)
99
100 // should we try to compute signs? if not, Grid remains unsigned
101 bool bComputeSigns = true;
102
103 // What counts as "inside" the mesh. Crossing count does not use triangle
104 // Orientation, so inverted faces are fine, but overlapping shells or self intersections
105 // will be filled using even/odd rules (as seen along X axis...)
106 // Winding count is basically mesh winding number, handles overlap shells and
107 // self-intersections, but inverted shells are 'subtracted', and inverted faces are a disaster.
108 // Both modes handle internal cavities, neither handles open sheets.
115
116 // Implementation computes the triangle closest to each Grid cell, can
117 // return this Grid if desired (only reason not to is avoid hanging onto memory)
119
120 // Grid of per-cell crossing or parity counts
122
125 {
126 return false;
127 };
128
129 // OUTPUTS
130
133
134protected:
135 // grid of closest triangle indices; only available if bWantClosestTriGrid is set to true. Access via GetClosestTriGrid()
137 // grid of intersection counts; only available if bWantIntersectionsGrid is set to true. Access via GetIntersectionsGrid()
139
140public:
141
150
160
161 // Make an interpolating sampler of the sdf data. Note the lifetime is dependent on this class.
162 template<typename InterpolantRealType = double>
168
178 static bool ShouldUseSpatial(int ExactCells, double CellSize, double AvgEdgeLen)
179 {
182 return ExactCells > 1 && EdgesPerBand >= .25;
183 }
184
185 bool Validate(const FAxisAlignedBox3d& Bounds)
186 {
187 if (Bounds.IsEmpty() || !FMath::IsFinite(Bounds.MaxDim()))
188 {
189 return false;
190 }
191 if (!IsValid(CellSize))
192 {
193 return false;
194 }
196 {
197 return false;
198 }
199 return true;
200 }
201
208 {
209 if (!ensure(Validate(Bounds)))
210 {
211 return false;
212 }
213
214 if (!ensureMsgf(ExactBandWidth*2 < ApproxMaxCellsPerDimension, TEXT("Cannot request band wider than half the max cells per dimension")))
215 {
216 ExactBandWidth = FMath::Max(ApproxMaxCellsPerDimension/2-1, 1);
217 }
218
219 FVector3d ExpandedDiagonal = (Bounds.Diagonal() + ExpandBounds * 2.0);
222 if (!ensureMsgf(ApproxVoxelsPerDim[MaxVoxelsDim] <= ApproxMaxCellsPerDimension - 2 * ExactBandWidth, TEXT("SDF resolution clamped to avoid excessive memory use")))
223 {
226 if (!ensure(IsValid(CellSize)))
227 {
228 return false;
229 }
230 }
231
234 {
235 if constexpr (bScalarCellSize)
236 {
238 }
239 else
240 {
241 for (int32 Idx = 0; Idx < 3; ++Idx)
242 {
243 fBufferWidth = FMath::Max(fBufferWidth, float(NarrowBandMaxDistance));
244 }
245 }
246 }
250 (int)((max.X - UseOrigin.X) / GetDim(CellSize, 0)) + 1,
251 (int)((max.Y - UseOrigin.Y) / GetDim(CellSize, 1)) + 1,
252 (int)((max.Z - UseOrigin.Z) / GetDim(CellSize, 2)) + 1
253 );
254
255 return Compute(UseOrigin, UseDims);
256 }
257
265 {
267 int NI = InDimensions.X;
268 int NJ = InDimensions.Y;
269 int NK = InDimensions.Z;
270
271 // dimensions must be positive
272 if (NI < 0 || NJ < 0 || NK < 0)
273 {
274 return false;
275 }
276
278 {
279 if (!ensureMsgf(Spatial && NarrowBandMaxDistance != 0, TEXT("SweepingMeshSDF::Compute: must set Spatial data structure and band max distance")))
280 {
281 return false;
282 }
283 make_level_set3_parallel_floodfill(GridOrigin, CellSize, NI, NJ, NK, Grid);
284 }
285 else
286 {
287 if (bUseParallel)
288 {
289 if (Spatial != nullptr) // TODO: is this path better than NarrowBand_SpatialFloodFill path sometimes? when?
290 {
291 make_level_set3_parallel_spatial(GridOrigin, CellSize, NI, NJ, NK, Grid, ExactBandWidth);
292 }
293 else
294 {
295 make_level_set3_parallel(GridOrigin, CellSize, NI, NJ, NK, Grid, ExactBandWidth);
296 }
297 }
298 else
299 {
300 make_level_set3(GridOrigin, CellSize, NI, NJ, NK, Grid, ExactBandWidth);
301 }
302 }
303
304 CleanupUnwanted();
305
306 return !CancelF();
307 }
308
309
310
312 {
313 return Grid.GetDimensions();
314 }
315
316
318 {
319 checkf(bWantClosestTriGrid, TEXT("Set bWantClosestTriGrid=true to return this value"));
320 return ClosestTriGrid;
321 }
323 {
324 checkf(bWantIntersectionsGrid, TEXT("Set bWantIntersectionsGrid=true to return this value"));
325 return IntersectionsGrid;
326 }
327
328 float At(int I, int J, int K) const
329 {
330 return Grid.GetValue(I, J, K);
331 }
332
333 float operator[](const FVector3i& Idx) const
334 {
335 return Grid.GetValue(Idx);
336 }
337
338 FVector3f CellCenter(int I, int J, int K) const
339 {
340 return cell_center(FVector3i(I, J, K));
341 }
342
343 float GetValue(FVector3i Idx) const // TTriLinearGridInterpolant interface
344 {
345 return Grid.GetValue(Idx);
346 }
347
348private:
349 FVector3f cell_center(FVector3i IJK) const
350 {
351 return FVector3f((float)IJK.X * GetDim(CellSize, 0) + GridOrigin[0],
352 (float)IJK.Y * GetDim(CellSize, 1) + GridOrigin[1],
353 (float)IJK.Z * GetDim(CellSize, 2) + GridOrigin[2]);
354 }
355
356 float upper_bound(const FDenseGrid3f& GridIn) const
357 {
358 return float(GridIn.GetDimensions().X) * GetDim(CellSize, 0) + float(GridIn.GetDimensions().Y) * GetDim(CellSize, 1) + float(GridIn.GetDimensions().Z) * GetDim(CellSize, 2);
359 }
360
361 float cell_tri_dist(const FVector3i& Idx, int TID) const
362 {
363 FVector3d xp, xq, xr;
364 FVector3d c = cell_center(Idx);
365 Mesh->GetTriVertices(TID, xp, xq, xr);
366 return (float)PointTriangleDistance(c, xp, xq, xr);
367 }
368
369
370 void make_level_set3(FVector3f Origin, CellSizeType DX, int NI, int NJ, int NK, FDenseGrid3f& Distances, int ExactBand)
371 {
372 Distances.Resize(NI, NJ, NK);
373 Distances.Assign(upper_bound(Distances)); // upper bound on distance
374
375 // closest triangle id for each Grid cell
379
380 // intersection_count(I,J,K) is # of tri intersections in (I-1,I]x{J}x{K}
384
385 // Compute narrow-band Distances. For each triangle, we find its Grid-coord-bbox,
386 // and compute exact Distances within that box. The intersection_count Grid
387 // is also filled in this computation
389 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
390 FVector3d xp, xq, xr;
391 for (int TID = 0; TID < Mesh->MaxTriangleID(); TID++)
392 {
393 if (!Mesh->IsTriangle(TID))
394 {
395 continue;
396 }
397 if (TID % 100 == 0 && CancelF())
398 {
399 break;
400 }
401 Mesh->GetTriVertices(TID, xp, xq, xr);
402
403 // real IJK coordinates of xp/xq/xr
404 double fip = (xp[0] - ox) / GetDim(ddx, 0), fjp = (xp[1] - oy) / GetDim(ddx, 1), fkp = (xp[2] - oz) / GetDim(ddx, 2);
405 double fiq = (xq[0] - ox) / GetDim(ddx, 0), fjq = (xq[1] - oy) / GetDim(ddx, 1), fkq = (xq[2] - oz) / GetDim(ddx, 2);
406 double fir = (xr[0] - ox) / GetDim(ddx, 0), fjr = (xr[1] - oy) / GetDim(ddx, 1), fkr = (xr[2] - oz) / GetDim(ddx, 2);
407
408 // clamped integer bounding box of triangle plus exact-band
409 int i0 = FMath::Clamp(((int)FMath::Min3(fip, fiq, fir)) - ExactBand, 0, NI - 1);
410 int i1 = FMath::Clamp(((int)FMath::Max3(fip, fiq, fir)) + ExactBand + 1, 0, NI - 1);
411 int j0 = FMath::Clamp(((int)FMath::Min3(fjp, fjq, fjr)) - ExactBand, 0, NJ - 1);
412 int j1 = FMath::Clamp(((int)FMath::Max3(fjp, fjq, fjr)) + ExactBand + 1, 0, NJ - 1);
413 int k0 = FMath::Clamp(((int)FMath::Min3(fkp, fkq, fkr)) - ExactBand, 0, NK - 1);
414 int k1 = FMath::Clamp(((int)FMath::Max3(fkp, fkq, fkr)) + ExactBand + 1, 0, NK - 1);
415
416 // compute distance for each tri inside this bounding box
417 // note: this can be very conservative if the triangle is large and on diagonal to Grid axes
418 for (int K = k0; K <= k1; ++K) {
419 for (int J = j0; J <= j1; ++J) {
420 for (int I = i0; I <= i1; ++I) {
421 FVector3d gx((float)I * GetDim(DX, 0) + Origin[0], (float)J * GetDim(DX, 1) + Origin[1], (float)K * GetDim(DX, 2) + Origin[2]);
422 float d = (float)PointTriangleDistance(gx, xp, xq, xr);
423 if (d < Distances.At(I, J, K)) {
424 Distances.At(I, J, K) = d;
425 closest_tri.At(I, J, K) = TID;
426 }
427 }
428 }
429 }
430 }
431 if (CancelF())
432 {
433 return;
434 }
435
436 if (bComputeSigns == true)
437 {
438 compute_intersections(Origin, DX, NI, NJ, NK, intersection_count);
439 if (CancelF())
440 {
441 return;
442 }
443
445 {
446 // and now we fill in the rest of the Distances with fast sweeping
447 for (int pass = 0; pass < 2; ++pass)
448 {
449 sweep_pass(Origin, DX, Distances, closest_tri);
450 if (CancelF())
451 {
452 return;
453 }
454 }
455 }
456 else
457 {
458 // nothing!
459 }
460
461
462 // then figure out signs (inside/outside) from intersection counts
463 compute_signs(NI, NJ, NK, Distances, intersection_count);
464 if (CancelF())
465 {
466 return;
467 }
468
470 {
471 // empty grid
473 }
474 }
475
477 {
478 // empty grid
479 closest_tri.Resize(0,0,0,EAllowShrinking::Yes);
480 }
481
482 } // end make_level_set_3
483
484
485
486
487
488
489 void make_level_set3_parallel(FVector3f Origin, CellSizeType DX, int NI, int NJ, int NK, FDenseGrid3f& Distances, int ExactBand)
490 {
491 Distances.Resize(NI, NJ, NK);
492 Distances.Assign(upper_bound(Grid)); // upper bound on distance
493
494 // closest triangle id for each Grid cell
498
499 // intersection_count(I,J,K) is # of tri intersections in (I-1,I]x{J}x{K}
503
504 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
506
507 // Compute narrow-band Distances. For each triangle, we find its Grid-coord-bbox,
508 // and compute exact Distances within that box.
509
510 // To compute in parallel, we need to safely update Grid cells. Current strategy is
511 // to use a critical section to control access to Grid. Partitioning the Grid into a few regions,
512 // each w/ a separate lock, improves performance somewhat.
513 int wi = NI / 2, wj = NJ / 2, wk = NK / 2;
515
516 bool bAbort = false;
517 ParallelFor(Mesh->MaxTriangleID(), [this, &Origin, DX, NI, NJ, NK, &Distances, ExactBand, &closest_tri, &intersection_count, ox, oy, oz, invdx, wi, wj, wk, &GridSections, &bAbort](int TID)
518 {
519 if (!Mesh->IsTriangle(TID))
520 {
521 return;
522 }
523 if (TID % 100 == 0)
524 {
525 bAbort = CancelF();
526 }
527 if (bAbort)
528 {
529 return;
530 }
531
533 Mesh->GetTriVertices(TID, xp, xq, xr);
534
535 // real IJK coordinates of xp/xq/xr
536 double fip = (xp[0] - ox) * GetDim(invdx, 0), fjp = (xp[1] - oy) * GetDim(invdx, 1), fkp = (xp[2] - oz) * GetDim(invdx, 2);
537 double fiq = (xq[0] - ox) * GetDim(invdx, 0), fjq = (xq[1] - oy) * GetDim(invdx, 1), fkq = (xq[2] - oz) * GetDim(invdx, 2);
538 double fir = (xr[0] - ox) * GetDim(invdx, 0), fjr = (xr[1] - oy) * GetDim(invdx, 1), fkr = (xr[2] - oz) * GetDim(invdx, 2);
539
540 // clamped integer bounding box of triangle plus exact-band
541 int i0 = FMath::Clamp(((int)FMath::Min3(fip, fiq, fir)) - ExactBand, 0, NI - 1);
542 int i1 = FMath::Clamp(((int)FMath::Max3(fip, fiq, fir)) + ExactBand + 1, 0, NI - 1);
543 int j0 = FMath::Clamp(((int)FMath::Min3(fjp, fjq, fjr)) - ExactBand, 0, NJ - 1);
544 int j1 = FMath::Clamp(((int)FMath::Max3(fjp, fjq, fjr)) + ExactBand + 1, 0, NJ - 1);
545 int k0 = FMath::Clamp(((int)FMath::Min3(fkp, fkq, fkr)) - ExactBand, 0, NK - 1);
546 int k1 = FMath::Clamp(((int)FMath::Max3(fkp, fkq, fkr)) + ExactBand + 1, 0, NK - 1);
547
548 // compute distance for each tri inside this bounding box
549 // note: this can be very conservative if the triangle is large and on diagonal to Grid axes
550 for (int K = k0; K <= k1; ++K) {
551 for (int J = j0; J <= j1; ++J) {
552 int base_idx = ((J < wj) ? 0 : 1) | ((K < wk) ? 0 : 2); // construct index into spinlocks array
553
554 for (int I = i0; I <= i1; ++I) {
555 FVector3d gx((float)I * GetDim(DX, 0) + Origin[0], (float)J * GetDim(DX, 1) + Origin[1], (float)K * GetDim(DX, 2) + Origin[2]);
556 float d = (float)PointTriangleDistance(gx, xp, xq, xr);
557 if (d < Distances.At(I, J, K)) {
558 int lock_idx = base_idx | ((I < wi) ? 0 : 4);
559
561 if (d < Distances.At(I, J, K)) { // have to check again in case Grid changed in another thread...
562 Distances.At(I, J, K) = d;
563 closest_tri.At(I, J, K) = TID;
564 }
565 }
566 }
567
568 }
569 }
570 });
571 if (CancelF())
572 {
573 return;
574 }
575
576
577 if (bComputeSigns == true)
578 {
579 compute_intersections(Origin, DX, NI, NJ, NK, intersection_count);
580 if (CancelF())
581 {
582 return;
583 }
584
586 // and now we fill in the rest of the Distances with fast sweeping
587 for (int pass = 0; pass < 2; ++pass) {
588 sweep_pass(Origin, DX, Distances, closest_tri);
589 if (CancelF())
590 return;
591 }
592 }
593 else
594 {
595 // nothing!
596 }
597
598 // then figure out signs (inside/outside) from intersection counts
599 compute_signs(NI, NJ, NK, Distances, intersection_count);
600 if (CancelF())
601 {
602 return;
603 }
604
606 {
607 // empty grid
609 }
610 }
611
613 {
614 // empty grid
615 closest_tri.Resize(0, 0, 0, EAllowShrinking::Yes);
616 }
617 } // end make_level_set_3
618
619
620
621 void make_level_set3_parallel_spatial(FVector3f Origin, CellSizeType DX, int NI, int NJ, int NK, FDenseGrid3f& Distances, int ExactBand)
622 {
623 Distances.Resize(NI, NJ, NK);
624 float upper_bound = this->upper_bound(Distances);
625 Distances.Assign(upper_bound); // upper bound on distance
626
627 // closest triangle id for each Grid cell
628 FDenseGrid3i& closest_tri = ClosestTriGrid;
629 ClosestTriGrid.Resize(NI, NJ, NK);
630 ClosestTriGrid.Assign(-1);
631
632 // intersection_count(I,J,K) is # of tri intersections in (I-1,I]x{J}x{K}
633 FDenseGrid3i& intersection_count = IntersectionsGrid;
634 IntersectionsGrid.Resize(NI, NJ, NK);
635 IntersectionsGrid.Assign(0);
636
637 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
638 CellSizeTyped invdx = CellSizeTyped(UnitCellSize()) / (CellSizeTyped)DX;
639
640 // Compute narrow-band Distances. For each triangle, we find its Grid-coord-bbox,
641 // and compute exact Distances within that box.
642
643 // To compute in parallel, we need to safely update Grid cells. Current strategy is
644 // to use a spinlock to control access to Grid. Partitioning the Grid into a few regions,
645 // each w/ a separate spinlock, improves performance somewhat. Have also tried having a
646 // separate spinlock per-row, this resulted in a few-percent performance improvement.
647 // Also tried pre-sorting triangles into disjoint regions, this did not help much except
648 // on "perfect" cases like a sphere.
649 bool bAbort = false;
650 ParallelFor(Mesh->MaxTriangleID(), [this, &Origin, DX, NI, NJ, NK, &Distances, ExactBand, upper_bound, &closest_tri, &intersection_count, ox, oy, oz, invdx, &bAbort](int TID)
651 {
652 if (!Mesh->IsTriangle(TID))
653 {
654 return;
655 }
656 if (TID % 100 == 0)
657 {
658 bAbort = CancelF();
659 }
660 if (bAbort)
661 {
662 return;
663 }
664
666 Mesh->GetTriVertices(TID, xp, xq, xr);
667
668 // real IJK coordinates of xp/xq/xr
669 double fip = (xp[0] - ox) * GetDim(invdx, 0), fjp = (xp[1] - oy) * GetDim(invdx, 1), fkp = (xp[2] - oz) * GetDim(invdx, 2);
670 double fiq = (xq[0] - ox) * GetDim(invdx, 0), fjq = (xq[1] - oy) * GetDim(invdx, 1), fkq = (xq[2] - oz) * GetDim(invdx, 2);
671 double fir = (xr[0] - ox) * GetDim(invdx, 0), fjr = (xr[1] - oy) * GetDim(invdx, 1), fkr = (xr[2] - oz) * GetDim(invdx, 2);
672
673 // clamped integer bounding box of triangle plus exact-band
674 int i0 = FMath::Clamp(((int)FMath::Min3(fip, fiq, fir)) - ExactBand, 0, NI - 1);
675 int i1 = FMath::Clamp(((int)FMath::Max3(fip, fiq, fir)) + ExactBand + 1, 0, NI - 1);
676 int j0 = FMath::Clamp(((int)FMath::Min3(fjp, fjq, fjr)) - ExactBand, 0, NJ - 1);
677 int j1 = FMath::Clamp(((int)FMath::Max3(fjp, fjq, fjr)) + ExactBand + 1, 0, NJ - 1);
678 int k0 = FMath::Clamp(((int)FMath::Min3(fkp, fkq, fkr)) - ExactBand, 0, NK - 1);
679 int k1 = FMath::Clamp(((int)FMath::Max3(fkp, fkq, fkr)) + ExactBand + 1, 0, NK - 1);
680
681 // compute distance for each tri inside this bounding box
682 // note: this can be very conservative if the triangle is large and on diagonal to Grid axes
683 for (int K = k0; K <= k1; ++K) {
684 for (int J = j0; J <= j1; ++J) {
685 for (int I = i0; I <= i1; ++I) {
686 Distances.At(I, J, K) = 1;
687 }
688 }
689 }
690 });
691
692 double max_dist = ExactBand * (double)GetDiagLength(DX);
693 ParallelFor(Grid.Size(), [this, &Origin, DX, NI, NJ, NK, &Distances, max_dist, upper_bound, &closest_tri](int LinearIdx)
694 {
695 FVector3i Idx = Grid.ToIndex(LinearIdx);
696 if (Distances[Idx] == 1) {
697 int I = Idx.X, J = Idx.Y, K = Idx.Z;
698 FVector3d p((float)I * GetDim(DX, 0) + Origin[0], (float)J * GetDim(DX, 1) + Origin[1], (float)K * GetDim(DX, 2) + Origin[2]);
699 double dsqr;
700 int near_tid = Spatial->FindNearestTriangle(p, dsqr, max_dist);
701 if (near_tid == IndexConstants::InvalidID) {
702 Distances[Idx] = upper_bound;
703 return;
704 }
705 Distances[Idx] = (float)FMath::Sqrt(dsqr);
706 closest_tri[Idx] = near_tid;
707 }
708 });
709
710
711
712 if (CancelF())
713 {
714 return;
715 }
716
717 if (bComputeSigns == true)
718 {
719 compute_intersections(Origin, DX, NI, NJ, NK, intersection_count);
720 if (CancelF())
721 {
722 return;
723 }
724
725 if (ComputeMode == EComputeModes::FullGrid) {
726 // and now we fill in the rest of the Distances with fast sweeping
727 for (int pass = 0; pass < 2; ++pass) {
728 sweep_pass(Origin, DX, Distances, closest_tri);
729 if (CancelF())
730 return;
731 }
732 }
733 else {
734 // nothing!
735 }
736
737 // then figure out signs (inside/outside) from intersection counts
738 compute_signs(NI, NJ, NK, Distances, intersection_count);
739 if (CancelF())
740 {
741 return;
742 }
743
744 if (!bWantIntersectionsGrid)
745 {
746 // empty grid
748 }
749 }
750
751 if (!bWantClosestTriGrid)
752 {
753 // empty grid
754 closest_tri.Resize(0, 0, 0, EAllowShrinking::Yes);
755 }
756
757 } // end make_level_set_3
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772 void make_level_set3_parallel_floodfill(FVector3f Origin, CellSizeType DX, int NI, int NJ, int NK, FDenseGrid3f& Distances)
773 {
774 Distances.Resize(NI, NJ, NK);
775 float upper_bound = this->upper_bound(Distances);
776 Distances.Assign(upper_bound); // upper bound on distance
777
778 // closest triangle id for each Grid cell
779 FDenseGrid3i& closest_tri = ClosestTriGrid;
780 ClosestTriGrid.Resize(NI, NJ, NK);
781 ClosestTriGrid.Assign(-1);
782
783 // intersection_count(I,J,K) is # of tri intersections in (I-1,I]x{J}x{K}
784 FDenseGrid3i& intersection_count = IntersectionsGrid;
785 IntersectionsGrid.Resize(NI, NJ, NK);
786 IntersectionsGrid.Assign(0);
787
788 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
789 CellSizeTyped invdx = CellSizeTyped(UnitCellSize()) / (CellSizeTyped)DX;
790
791 // the steps below that populate the grid will potentially touch the same cells at the
792 // same time, so we need to lock them. However locking the entire grid for each cell
793 // basically means the threads are constantly fighting. So we split the grid up into
794 // chunks for locking
795 int32 NumSections = 256; // a bit arbitrary, stopped seeing improvement on a 64-core machine at this point
797 GridSections.SetNum(NumSections);
799 int64 SectionSize = FMath::CeilToInt(float(TotalGridCellCount) / (float)NumSections);
800
801 // this returns the FCriticalSection to use for the given span of values
802 auto GetGridSectionLock = [this, SectionSize, &Distances, &GridSections](FVector3i CellGridIndex) -> FCriticalSection*
803 {
804 int64 CellLinearIndex = Distances.ToLinear(CellGridIndex);
805 int64 SectionIndex = CellLinearIndex / SectionSize;
806 checkSlow(SectionIndex <= MAX_int32);
807 return &GridSections[(int32)SectionIndex];
808 };
809
810 // per-grid-chunk queue, each one of these cooresponds to a GridSections lock
812 SectionQueues.SetNum(NumSections);
814 {
815 int64 SectionIndex = CellLinearIndex / SectionSize;
816 checkSlow(SectionIndex <= MAX_int32);
819 };
820
821
823 TArray<bool> done;
824 done.SetNumZeroed(Distances.Size());
825
826 // this lambda combines the per-chunk queues into a single queue which we can pass to a ParallelFor
828 {
829 PendingCellQueue.Reset();
831 {
832 for (int Value : SectionQueue)
833 {
835 }
836 SectionQueue.Reset();
837 }
838 };
839
840 // compute values at vertices
841 bool bAbort = false;
842 {
844 ParallelFor(Mesh->MaxVertexID(), [&](int vid)
845 {
846 if (!Mesh->IsVertex(vid))
847 {
848 return;
849 }
850 if (vid % 100 == 0)
851 {
852 bAbort = CancelF();
853 }
854 if (bAbort)
855 {
856 return;
857 }
858
859 FVector3d v = Mesh->GetVertex(vid);
860 // real IJK coordinates of v
861 double fi = (v.X - ox) * GetDim(invdx, 0), fj = (v.Y - oy) * GetDim(invdx, 1), fk = (v.Z - oz) * GetDim(invdx, 2);
862 FVector3i Idx(
863 FMath::Clamp((int)fi, 0, NI - 1),
864 FMath::Clamp((int)fj, 0, NJ - 1),
865 FMath::Clamp((int)fk, 0, NK - 1));
866
867 {
869 if (Distances[Idx] < upper_bound)
870 {
871 return;
872 }
873 FVector3d p = (FVector3d)cell_center(Idx);
874 double dsqr;
875 int near_tid = Spatial->FindNearestTriangle(p, dsqr);
876 Distances[Idx] = (float)FMathd::Sqrt(dsqr);
877 closest_tri[Idx] = near_tid;
878 int idx_linear = Distances.ToLinear(Idx);
879 if (done[idx_linear] == false)
880 {
881 done[idx_linear] = true;
883 }
884 }
885 }, !bUseParallel);
886 }
887 if (CancelF())
888 {
889 return;
890 }
891
893
894 // we could do this parallel w/ some kind of producer-consumer...
895 FAxisAlignedBox3i Bounds = Distances.BoundsInclusive();
896 double max_dist = NarrowBandMaxDistance;
897 double max_query_dist = max_dist + GetDiagLength(DX);
898 {
900 while (PendingCellQueue.Num() > 0)
901 {
902 ParallelFor(PendingCellQueue.Num(), [&](int QIdx)
903 {
904 int cur_linear_index = PendingCellQueue[QIdx];
905 FVector3i cur_idx = Distances.ToIndex(cur_linear_index);
906 for (FVector3i idx_offset : IndexUtil::GridOffsets26)
907 {
908 FVector3i nbr_idx = cur_idx + idx_offset;
909 if (Bounds.Contains(nbr_idx) == false)
910 {
911 continue;
912 }
913 int nbr_linear_idx = Distances.ToLinear(nbr_idx);
914
915 // Note: this is technically unsafe to do because other threads might be writing to this memory location
916 // which could in theory mean that the value being read is garbage or stale. However the only possible values are true and false.
917 // If we get an incorrect false, we will just do an extra unnecessary spatial query, but because we lock before
918 // any writes, that value will just be discarded. If we get an incorrect true, we potentially skip a grid cell
919 // we need. However in this context we are doing a flood-fill and so except at the very edges of the populated cells,
920 // each cell will be considered multiple times as we touch it's neighbours. In practice we have not observed
921 // this to be a problem, however locking here dramatically slows the algorithm down (eg by 3-5x on a 64-core machine)
922 if (done[nbr_linear_idx])
923 {
924 continue;
925 }
926
927 FVector3d p = (FVector3d)cell_center(nbr_idx);
928 double dsqr;
929 int near_tid = Spatial->FindNearestTriangle(p, dsqr, max_query_dist);
930 if (near_tid == -1)
931 {
932 FScopeLock GridLock(GetGridSectionLock(nbr_idx));
933 done[nbr_linear_idx] = true;
934 continue;
935 }
936 double dist = FMathd::Sqrt(dsqr);
937
938 {
939 // locked section -- if index not already done, update the grid
940 FScopeLock GridLock(GetGridSectionLock(nbr_idx));
941 if (done[nbr_linear_idx] == false)
942 {
943 Distances[nbr_linear_idx] = (float)dist;
944 closest_tri[nbr_linear_idx] = near_tid;
945 done[nbr_linear_idx] = true;
946 if (dist < max_dist)
947 {
948 AddToSectionQueue(nbr_linear_idx);
949 }
950 }
951 }
952 }
953 }, !bUseParallel);
954
956 }
957 }
958 if (CancelF())
959 {
960 return;
961 }
962
963
964 if (bComputeSigns == true)
965 {
967 compute_intersections(Origin, DX, NI, NJ, NK, intersection_count);
968 if (CancelF())
969 {
970 return;
971 }
972
973 if (ComputeMode == EComputeModes::FullGrid)
974 {
975 // and now we fill in the rest of the Distances with fast sweeping
976 for (int pass = 0; pass < 2; ++pass)
977 {
978 sweep_pass(Origin, DX, Distances, closest_tri);
979 if (CancelF())
980 {
981 return;
982 }
983 }
984 }
985 else
986 {
987 // nothing!
988 }
989
990 // then figure out signs (inside/outside) from intersection counts
991 compute_signs(NI, NJ, NK, Distances, intersection_count);
992 if (CancelF())
993 {
994 return;
995 }
996
997 }
998
999 } // end make_level_set_3
1000
1001
1002 void CleanupUnwanted()
1003 {
1004 if (!bWantIntersectionsGrid)
1005 {
1006 IntersectionsGrid.Resize(0, 0, 0, EAllowShrinking::Yes);
1007 }
1008 if (!bWantClosestTriGrid)
1009 {
1010 ClosestTriGrid.Resize(0, 0, 0, EAllowShrinking::Yes);
1011 }
1012 }
1013
1014
1015
1016
1017 // sweep through Grid in different directions, Distances and closest tris
1018 void sweep_pass(FVector3f Origin, CellSizeType DX, FDenseGrid3f& Distances, FDenseGrid3i& closest_tri)
1019 {
1020 sweep(Distances, closest_tri, Origin, DX, +1, +1, +1);
1021 if (CancelF()) return;
1022 sweep(Distances, closest_tri, Origin, DX, -1, -1, -1);
1023 if (CancelF()) return;
1024 sweep(Distances, closest_tri, Origin, DX, +1, +1, -1);
1025 if (CancelF()) return;
1026 sweep(Distances, closest_tri, Origin, DX, -1, -1, +1);
1027 if (CancelF()) return;
1028 sweep(Distances, closest_tri, Origin, DX, +1, -1, +1);
1029 if (CancelF()) return;
1030 sweep(Distances, closest_tri, Origin, DX, -1, +1, -1);
1031 if (CancelF()) return;
1032 sweep(Distances, closest_tri, Origin, DX, +1, -1, -1);
1033 if (CancelF()) return;
1034 sweep(Distances, closest_tri, Origin, DX, -1, +1, +1);
1035 }
1036
1037
1038 // single sweep pass
1039 void sweep(FDenseGrid3f& phi, FDenseGrid3i& closest_tri, FVector3f Origin, CellSizeType DX, int di, int dj, int dk)
1040 {
1041 int i0, i1;
1042 if (di > 0) { i0 = 1; i1 = phi.GetDimensions().X; }
1043 else { i0 = phi.GetDimensions().X - 2; i1 = -1; }
1044 int j0, j1;
1045 if (dj > 0) { j0 = 1; j1 = phi.GetDimensions().Y; }
1046 else { j0 = phi.GetDimensions().Y - 2; j1 = -1; }
1047 int k0, k1;
1048 if (dk > 0) { k0 = 1; k1 = phi.GetDimensions().Z; }
1049 else { k0 = phi.GetDimensions().Z - 2; k1 = -1; }
1050 for (int K = k0; K != k1; K += dk)
1051 {
1052 if (CancelF())
1053 {
1054 return;
1055 }
1056 for (int J = j0; J != j1; J += dj)
1057 {
1058 for (int I = i0; I != i1; I += di)
1059 {
1060 FVector3d gx(float(I) * GetDim(DX, 0) + Origin[0], float(J) * GetDim(DX, 1) + Origin[1], float(K) * GetDim(DX, 2) + Origin[2]);
1061 check_neighbour(phi, closest_tri, gx, I, J, K, I - di, J, K);
1062 check_neighbour(phi, closest_tri, gx, I, J, K, I, J - dj, K);
1063 check_neighbour(phi, closest_tri, gx, I, J, K, I - di, J - dj, K);
1064 check_neighbour(phi, closest_tri, gx, I, J, K, I, J, K - dk);
1065 check_neighbour(phi, closest_tri, gx, I, J, K, I - di, J, K - dk);
1066 check_neighbour(phi, closest_tri, gx, I, J, K, I, J - dj, K - dk);
1067 check_neighbour(phi, closest_tri, gx, I, J, K, I - di, J - dj, K - dk);
1068 }
1069 }
1070 }
1071 }
1072
1073
1074
1075 void check_neighbour(FDenseGrid3f& phi, FDenseGrid3i& closest_tri, const FVector3d& gx, int i0, int j0, int k0, int i1, int j1, int k1)
1076 {
1077 if (closest_tri.At(i1, j1, k1) >= 0)
1078 {
1079 FVector3d xp, xq, xr;
1080 Mesh->GetTriVertices(closest_tri.At(i1, j1, k1), xp, xq, xr);
1081 float d = (float)PointTriangleDistance(gx, xp, xq, xr);
1082 if (d < phi.At(i0, j0, k0)) {
1083 phi.At(i0, j0, k0) = d;
1084 closest_tri.At(i0, j0, k0) = closest_tri.At(i1, j1, k1);
1085 }
1086 }
1087 }
1088
1089
1090
1091
1092 // fill the intersection Grid w/ number of intersections in each cell
1093 void compute_intersections(FVector3f Origin, CellSizeType DX, int32 NI, int32 NJ, int32 NK, FDenseGrid3i& IntersectionCount)
1094 {
1095 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
1096 CellSizeTyped invdx = CellSizeTyped(UnitCellSize()) / (CellSizeTyped)DX;
1097
1098 bool cancelled = false;
1099
1100 // this is what we will do for each triangle. There are no Grid-reads, only Grid-writes,
1101 // since we use atomic_increment, it is always thread-safe
1102 ParallelFor(Mesh->MaxTriangleID(),
1103 [this, &Origin, &NI, &NJ, &NK,
1105 {
1106 if (!Mesh->IsTriangle(TID))
1107 {
1108 return;
1109 }
1110 if (TID % 100 == 0 && CancelF() == true)
1111 {
1112 cancelled = true;
1113 }
1114 if (cancelled)
1115 {
1116 return;
1117 }
1118
1120 Mesh->GetTriVertices(TID, xp, xq, xr);
1121
1122
1123 bool neg_x = false;
1124 if (InsideMode == EInsideModes::WindingCount)
1125 {
1126 FVector3d n = VectorUtil::NormalDirection(xp, xq, xr);
1127 neg_x = n.X > 0;
1128 }
1129
1130 // real IJK coordinates of xp/xq/xr
1131 double fip = (xp[0] - ox) * GetDim(invdx, 0), fjp = (xp[1] - oy) * GetDim(invdx, 1), fkp = (xp[2] - oz) * GetDim(invdx, 2);
1132 double fiq = (xq[0] - ox) * GetDim(invdx, 0), fjq = (xq[1] - oy) * GetDim(invdx, 1), fkq = (xq[2] - oz) * GetDim(invdx, 2);
1133 double fir = (xr[0] - ox) * GetDim(invdx, 0), fjr = (xr[1] - oy) * GetDim(invdx, 1), fkr = (xr[2] - oz) * GetDim(invdx, 2);
1134
1135 // recompute J/K integer bounds of triangle w/o exact band
1136 int32 j0 = FMath::Clamp(FMath::CeilToInt32(FMath::Min3(fjp, fjq, fjr)), 0, NJ - 1);
1137 int32 j1 = FMath::Clamp(FMath::FloorToInt32(FMath::Max3(fjp, fjq, fjr)), 0, NJ - 1);
1138 int32 k0 = FMath::Clamp(FMath::CeilToInt32(FMath::Min3(fkp, fkq, fkr)), 0, NK - 1);
1139 int32 k1 = FMath::Clamp(FMath::FloorToInt32(FMath::Max3(fkp, fkq, fkr)), 0, NK - 1);
1140
1141 // and do intersection counts
1142 for (int K = k0; K <= k1; ++K)
1143 {
1144 for (int J = j0; J <= j1; ++J)
1145 {
1146 double A, B, C;
1147 if (PointInTriangle2d(J, K, fjp, fkp, fjq, fkq, fjr, fkr, A, B, C))
1148 {
1149 double fi = A * fip + B * fiq + C * fir; // intersection I coordinate
1150 int i_interval = FMath::CeilToInt32(fi); // intersection is in (i_interval-1,i_interval]
1151 if (i_interval < 0)
1152 {
1153 DenseGrid::AtomicIncDec(IntersectionCount, 0, J, K, neg_x);
1154 }
1155 else if (i_interval < NI)
1156 {
1157 DenseGrid::AtomicIncDec(IntersectionCount, i_interval, J, K, neg_x);
1158 }
1159 else
1160 {
1161 // we ignore intersections that are beyond the +x side of the Grid
1162 }
1163 }
1164 }
1165 }
1166 }, !bUseParallel);
1167 }
1168
1169
1170
1171
1172
1173 // iterate through each x-row of Grid and set unsigned distances to be negative
1174 // inside the mesh, based on the IntersectionCount
1175 void compute_signs(int NI, int NJ, int NK, FDenseGrid3f& Distances, FDenseGrid3i& IntersectionCount)
1176 {
1177 if (bUseParallel)
1178 {
1179 // can process each x-row in parallel
1180 ParallelFor(NJ * NK, [this, &NI, &NJ, &NK, &Distances, &IntersectionCount](int32 vi)
1181 {
1182 if (CancelF())
1183 {
1184 return;
1185 }
1186
1187 int32 J = vi % NJ, K = vi / NJ;
1188 int32 total_count = 0;
1189 for (int I = 0; I < NI; ++I) {
1190 total_count += IntersectionCount.At(I, J, K);
1191 if (
1192 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1193 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1194 )
1195 {
1196 Distances.At(I, J, K) = -Distances.At(I, J, K); // we are inside the mesh
1197 }
1198 }
1199 }, !bUseParallel);
1200
1201 }
1202 else
1203 {
1204 for (int K = 0; K < NK; ++K)
1205 {
1206 if (CancelF())
1207 {
1208 return;
1209 }
1210
1211 for (int J = 0; J < NJ; ++J)
1212 {
1213 int total_count = 0;
1214 for (int I = 0; I < NI; ++I)
1215 {
1216 total_count += IntersectionCount.At(I, J, K);
1217 if (
1218 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1219 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1220 )
1221 {
1222 Distances.At(I, J, K) = -Distances.At(I, J, K); // we are inside the mesh
1223 }
1224 }
1225 }
1226 }
1227 }
1228 }
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241public:
1242
1243 // calculate twice signed area of triangle (0,0)-(X1,Y1)-(X2,Y2)
1244 // return an SOS-determined sign (-1, +1, or 0 only if it's a truly degenerate triangle)
1245 static int Orientation(double X1, double Y1, double X2, double Y2, double& TwiceSignedArea)
1246 {
1247 TwiceSignedArea = Y1 * X2 - X1 * Y2;
1248 if (TwiceSignedArea > 0) return 1;
1249 else if (TwiceSignedArea < 0) return -1;
1250 else if (Y2 > Y1) return 1;
1251 else if (Y2 < Y1) return -1;
1252 else if (X1 > X2) return 1;
1253 else if (X1 < X2) return -1;
1254 else return 0; // only true when X1==X2 and Y1==Y2
1255 }
1256
1257
1258 // robust test of (X0,Y0) in the triangle (X1,Y1)-(X2,Y2)-(X3,Y3)
1259 // if true is returned, the barycentric coordinates are set in A,B,C.
1260 static bool PointInTriangle2d(double X0, double Y0, double X1, double Y1, double X2, double Y2, double X3, double Y3, double& A, double& B, double& C)
1261 {
1262 A = B = C = 0;
1263 X1 -= X0; X2 -= X0; X3 -= X0;
1264 Y1 -= Y0; Y2 -= Y0; Y3 -= Y0;
1265 int signa = Orientation(X2, Y2, X3, Y3, A);
1266 if (signa == 0) return false;
1267 int signb = Orientation(X3, Y3, X1, Y1, B);
1268 if (signb != signa) return false;
1269 int signc = Orientation(X1, Y1, X2, Y2, C);
1270 if (signc != signa) return false;
1271 double sum = A + B + C;
1272 // if the SOS signs match and are nonzero, there's no way all of A, B, and C are zero.
1273 // TODO: is this mathematically impossible? can we just remove the check?
1274 checkf(sum != 0, TEXT("TCachingMeshSDF::PointInTriangle2d: impossible config?"));
1275 A /= sum;
1276 B /= sum;
1277
1278 C /= sum;
1279 return true;
1280 }
1281
1282 // find distance x0 is from segment x1-x2
1283 static float PointSegmentDistance(const FVector3f& x0, const FVector3f& x1, const FVector3f& x2)
1284 {
1285 FVector3f DX = x2 - x1;
1286 float m2 = DX.SquaredLength();
1287 // find parameter value of closest point on segment
1288 float s12 = (DX.Dot(x2 - x0) / m2);
1289 if (s12 < 0)
1290 {
1291 s12 = 0;
1292 }
1293 else if (s12 > 1)
1294 {
1295 s12 = 1;
1296 }
1297 // and find the distance
1298 return Distance(x0, s12*x1 + (1.0 - s12)*x2);
1299 }
1300
1301
1302 // find distance x0 is from segment x1-x2
1303 static double PointSegmentDistance(const FVector3d& x0, const FVector3d& x1, const FVector3d& x2)
1304 {
1305 FVector3d DX = x2 - x1;
1306 double m2 = DX.SquaredLength();
1307 // find parameter value of closest point on segment
1308 double s12 = (DX.Dot(x2 - x0) / m2);
1309 if (s12 < 0)
1310 {
1311 s12 = 0;
1312 }
1313 else if (s12 > 1)
1314 {
1315 s12 = 1;
1316 }
1317 // and find the distance
1318 return Distance(x0, s12*x1 + (1.0 - s12)*x2);
1319 }
1320
1321
1322
1323 // find distance x0 is from triangle x1-x2-x3
1324 static float PointTriangleDistance(const FVector3f& x0, const FVector3f& x1, const FVector3f& x2, const FVector3f& x3)
1325 {
1326 // first find barycentric coordinates of closest point on infinite plane
1327 FVector3f x13 = (x1 - x3);
1328 FVector3f x23 = (x2 - x3);
1329 FVector3f x03 = (x0 - x3);
1330 float m13 = x13.SquaredLength(), m23 = x23.SquaredLength(), d = x13.Dot(x23);
1331 float invdet = 1.0f / FMath::Max(m13 * m23 - d * d, 1e-30f);
1332 float a = x13.Dot(x03), b = x23.Dot(x03);
1333 // the barycentric coordinates themselves
1334 float w23 = invdet * (m23 * a - d * b);
1335 float w31 = invdet * (m13 * b - d * a);
1336 float w12 = 1 - w23 - w31;
1337 if (w23 >= 0 && w31 >= 0 && w12 >= 0) // if we're inside the triangle
1338 {
1339 return Distance(x0, w23*x1 + w31*x2 + w12*x3);
1340 }
1341 else // we have to clamp to one of the edges
1342 {
1343 if (w23 > 0) // this rules out edge 2-3 for us
1344 {
1345 return FMath::Min(PointSegmentDistance(x0, x1, x2), PointSegmentDistance(x0, x1, x3));
1346 }
1347 else if (w31 > 0) // this rules out edge 1-3
1348 {
1349 return FMath::Min(PointSegmentDistance(x0, x1, x2), PointSegmentDistance(x0, x2, x3));
1350 }
1351 else // w12 must be >0, ruling out edge 1-2
1352 {
1353 return FMath::Min(PointSegmentDistance(x0, x1, x3), PointSegmentDistance(x0, x2, x3));
1354 }
1355 }
1356 }
1357
1358
1359 // find distance x0 is from triangle x1-x2-x3
1360 static double PointTriangleDistance(const FVector3d& x0, const FVector3d& x1, const FVector3d& x2, const FVector3d& x3)
1361 {
1362 // first find barycentric coordinates of closest point on infinite plane
1363 FVector3d x13 = (x1 - x3);
1364 FVector3d x23 = (x2 - x3);
1365 FVector3d x03 = (x0 - x3);
1366 double m13 = x13.SquaredLength(), m23 = x23.SquaredLength(), d = x13.Dot(x23);
1367 double invdet = 1.0 / FMath::Max(m13 * m23 - d * d, 1e-30);
1368 double a = x13.Dot(x03), b = x23.Dot(x03);
1369 // the barycentric coordinates themselves
1370 double w23 = invdet * (m23 * a - d * b);
1371 double w31 = invdet * (m13 * b - d * a);
1372 double w12 = 1 - w23 - w31;
1373 if (w23 >= 0 && w31 >= 0 && w12 >= 0) // if we're inside the triangle
1374 {
1375 return Distance(x0, w23*x1 + w31*x2 + w12*x3);
1376 }
1377 else // we have to clamp to one of the edges
1378 {
1379 if (w23 > 0) // this rules out edge 2-3 for us
1380 {
1381 return FMath::Min(PointSegmentDistance(x0, x1, x2), PointSegmentDistance(x0, x1, x3));
1382 }
1383 else if (w31 > 0) // this rules out edge 1-3
1384 {
1385 return FMath::Min(PointSegmentDistance(x0, x1, x2), PointSegmentDistance(x0, x2, x3));
1386 }
1387 else // w12 must be >0, ruling out edge 1-2
1388 {
1389 return FMath::Min(PointSegmentDistance(x0, x1, x3), PointSegmentDistance(x0, x2, x3));
1390 }
1391 }
1392 }
1393
1394 // Helper methods to access CellSize in either scalar or vector mode
1395 inline static double GetDim(CellSizeTyped CellSize, int32 Axis)
1396 {
1397 if constexpr (bScalarCellSize)
1398 {
1399 return (double)CellSize;
1400 }
1401 else
1402 {
1403 return (double)CellSize[Axis];
1404 }
1405 }
1406 inline static float GetDim(CellSizeType CellSize, int32 Axis)
1407 {
1408 if constexpr (bScalarCellSize)
1409 {
1410 return (float)CellSize;
1411 }
1412 else
1413 {
1414 return (float)CellSize[Axis];
1415 }
1416 }
1417 inline static float GetDiagLength(CellSizeType CellSize)
1418 {
1419 if constexpr (bScalarCellSize)
1420 {
1421 return CellSize * FMathf::Sqrt3;
1422 }
1423 else
1424 {
1425 return CellSize.Length();
1426 }
1427 }
1428 inline static bool IsValid(CellSizeType CellSize)
1429 {
1430 if constexpr (std::is_floating_point_v<CellSizeType>)
1431 {
1432 return CellSize > 0 && FMath::IsFinite(CellSize);
1433 }
1434 else
1435 {
1436 return CellMinDim(CellSize) > 0 && FMath::IsFinite(CellSize[0]) && FMath::IsFinite(CellSize[1]) && FMath::IsFinite(CellSize[2]);
1437 }
1438 }
1439};
1440
1441
1442} // end namespace UE::Geometry
1443} // end namespace UE
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define ensureMsgf( InExpression, InFormat,...)
Definition AssertionMacros.h:465
#define ensure( InExpression)
Definition AssertionMacros.h:464
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
void ParallelFor(int32 Num, TFunctionRef< void(int32)> Body, bool bForceSingleThread, bool bPumpRenderingThread=false)
Definition ParallelFor.h:481
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
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
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
UE::Math::TVector< float > FVector3f
Definition MathFwd.h:73
UE::Math::TVector< double > FVector3d
Definition MathFwd.h:60
@ Compute
Definition MetalRHIPrivate.h:232
const bool
Definition NetworkReplayStreaming.h:178
#define MAX_int32
Definition NumericLimits.h:25
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
if(Failed) console_printf("Failed.\n")
Definition ScopeLock.h:141
Definition Array.h:670
void SetNumZeroed(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2340
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
Definition AndroidPlatformMisc.h:14
static RealType Sqrt(const RealType Value)
Definition MathUtil.h:342
ElemType GetValue(int32 X, int32 Y, int32 Z) const
Definition DenseGrid3.h:136
const FVector3i & GetDimensions() const
Definition DenseGrid3.h:56
void Assign(ElemType Value)
Definition DenseGrid3.h:73
void Resize(int DimX, int DimY, int DimZ, EAllowShrinking AllowShrinking=EAllowShrinking::Default)
Definition DenseGrid3.h:61
Definition MeshAABBTree3.h:61
Definition SweepingMeshSDF.h:46
EComputeModes ComputeMode
Definition SweepingMeshSDF.h:92
FVector3i Dimensions() const
Definition SweepingMeshSDF.h:311
static int Orientation(double X1, double Y1, double X2, double Y2, double &TwiceSignedArea)
Definition SweepingMeshSDF.h:1245
FDenseGrid3i IntersectionsGrid
Definition SweepingMeshSDF.h:138
const FDenseGrid3i & GetIntersectionsGrid() const
Definition SweepingMeshSDF.h:322
bool Compute(FAxisAlignedBox3d Bounds)
Definition SweepingMeshSDF.h:207
static bool PointInTriangle2d(double X0, double Y0, double X1, double Y1, double X2, double Y2, double X3, double Y3, double &A, double &B, double &C)
Definition SweepingMeshSDF.h:1260
std::conditional_t< bScalarCellSize, double, FVector3d > CellSizeTyped
Definition SweepingMeshSDF.h:52
TMeshAABBTree3< TriangleMeshType > * Spatial
Definition SweepingMeshSDF.h:59
FDenseGrid3f Grid
Definition SweepingMeshSDF.h:132
static float PointSegmentDistance(const FVector3f &x0, const FVector3f &x1, const FVector3f &x2)
Definition SweepingMeshSDF.h:1283
bool bComputeSigns
Definition SweepingMeshSDF.h:101
bool Validate(const FAxisAlignedBox3d &Bounds)
Definition SweepingMeshSDF.h:185
static float PointTriangleDistance(const FVector3f &x0, const FVector3f &x1, const FVector3f &x2, const FVector3f &x3)
Definition SweepingMeshSDF.h:1324
static double GetDim(CellSizeTyped CellSize, int32 Axis)
Definition SweepingMeshSDF.h:1395
TSweepingMeshSDF(const TriangleMeshType *Mesh=nullptr, CellSizeType InCellSize=UnitCellSize(), TMeshAABBTree3< TriangleMeshType > *Spatial=nullptr)
Definition SweepingMeshSDF.h:147
static CellSizeType UnitCellSize()
Definition SweepingMeshSDF.h:54
static double PointSegmentDistance(const FVector3d &x0, const FVector3d &x1, const FVector3d &x2)
Definition SweepingMeshSDF.h:1303
float operator[](const FVector3i &Idx) const
Definition SweepingMeshSDF.h:333
float GetValue(FVector3i Idx) const
Definition SweepingMeshSDF.h:343
static float GetDiagLength(CellSizeType CellSize)
Definition SweepingMeshSDF.h:1417
void Empty()
Definition SweepingMeshSDF.h:154
void SetCellSize(float InCellSize)
Definition SweepingMeshSDF.h:62
bool bWantClosestTriGrid
Definition SweepingMeshSDF.h:118
int ApproxMaxCellsPerDimension
Definition SweepingMeshSDF.h:79
static float GetDim(CellSizeType CellSize, int32 Axis)
Definition SweepingMeshSDF.h:1406
TTriLinearGridInterpolant< FDenseGrid3f, InterpolantRealType, bScalarCellSize > MakeInterpolant()
Definition SweepingMeshSDF.h:163
CellSizeType CellSize
Definition SweepingMeshSDF.h:60
FDenseGrid3i ClosestTriGrid
Definition SweepingMeshSDF.h:136
TFunction< bool()> CancelF
Definition SweepingMeshSDF.h:124
const FDenseGrid3i & GetClosestTriGrid() const
Definition SweepingMeshSDF.h:317
EInsideModes InsideMode
Definition SweepingMeshSDF.h:114
bool bWantIntersectionsGrid
Definition SweepingMeshSDF.h:121
double NarrowBandMaxDistance
Definition SweepingMeshSDF.h:98
static double PointTriangleDistance(const FVector3d &x0, const FVector3d &x1, const FVector3d &x2, const FVector3d &x3)
Definition SweepingMeshSDF.h:1360
EComputeModes
Definition SweepingMeshSDF.h:87
@ NarrowBand_SpatialFloodFill
Definition SweepingMeshSDF.h:90
@ NarrowBandOnly
Definition SweepingMeshSDF.h:89
@ FullGrid
Definition SweepingMeshSDF.h:88
float At(int I, int J, int K) const
Definition SweepingMeshSDF.h:328
FVector3f CellCenter(int I, int J, int K) const
Definition SweepingMeshSDF.h:338
bool bUseParallel
Definition SweepingMeshSDF.h:76
static bool ShouldUseSpatial(int ExactCells, double CellSize, double AvgEdgeLen)
Definition SweepingMeshSDF.h:178
static bool IsValid(CellSizeType CellSize)
Definition SweepingMeshSDF.h:1428
bool Compute(FVector3f InGridOrigin, FVector3i InDimensions)
Definition SweepingMeshSDF.h:264
FVector3f GridOrigin
Definition SweepingMeshSDF.h:131
EInsideModes
Definition SweepingMeshSDF.h:110
@ CrossingCount
Definition SweepingMeshSDF.h:111
@ WindingCount
Definition SweepingMeshSDF.h:112
const TriangleMeshType * Mesh
Definition SweepingMeshSDF.h:58
FVector3d ExpandBounds
Definition SweepingMeshSDF.h:73
int ExactBandWidth
Definition SweepingMeshSDF.h:69
std::conditional_t< bScalarCellSize, float, FVector3f > CellSizeType
Definition SweepingMeshSDF.h:50
Definition GridInterpolant.h:29
int Max3Index(const ValueVecType &Vector3)
Definition VectorUtil.h:194
GEOMETRYCORE_API bool PointInTriangle2d(double X0, double Y0, double X1, double Y1, double X2, double Y2, double X3, double Y3, double &A, double &B, double &C)
Definition SDFCalculationUtils.cpp:84
TDenseGrid3< int > FDenseGrid3i
Definition DenseGrid3.h:235
TDenseGrid3< float > FDenseGrid3f
Definition DenseGrid3.h:233
GEOMETRYCORE_API double PointTriangleDistance(const FVector3d &x0, const FVector3d &x1, const FVector3d &x2, const FVector3d &x3)
Definition SDFCalculationUtils.cpp:9
RealType PointSegmentDistance(const TVector< RealType > &x0, const TVector< RealType > &x1, const TVector< RealType > &x2)
Definition SDFCalculationUtils.h:28
Definition AdvancedWidgetsModule.cpp:13
float v
Definition radaudio_mdct.cpp:62
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static constexpr UE_FORCEINLINE_HINT T Min3(const T A, const T B, const T C)
Definition UnrealMathUtility.h:558
static constexpr UE_FORCEINLINE_HINT T Max3(const T A, const T B, const T C)
Definition UnrealMathUtility.h:551
Definition IntVectorTypes.h:252
TVector< RealType > Max
Definition BoxTypes.h:249
bool IsEmpty() const
Definition BoxTypes.h:613
TVector< RealType > Diagonal() const
Definition BoxTypes.h:608
RealType MaxDim() const
Definition BoxTypes.h:598
TVector< RealType > Min
Definition BoxTypes.h:248
static TVector< float > One()
Definition Vector.h:115
T Z
Definition Vector.h:68
static TVector< double > Zero()
Definition Vector.h:112
T Y
Definition Vector.h:65
T X
Definition Vector.h:62
T SquaredLength() const
Definition Vector.h:1734
UE_FORCEINLINE_HINT T Dot(const TVector< T > &V) const
Definition Vector.h:1553