UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SparseNarrowBandMeshSDF.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"
13#include "Async/ParallelFor.h"
17
18#include <atomic> // note: UE FGenericPlatformAtomics are deprecated.
19
20namespace UE
21{
22namespace Geometry
23{
24
25
47template <class TriangleMeshType>
49{
50public:
51 // INPUTS
52
53 const TriangleMeshType* Mesh = nullptr;
54 TMeshAABBTree3<TriangleMeshType>* Spatial = nullptr; // required for the NarrowBand_SpatialFloodFill method.
55 TUniqueFunction<bool(const FVector3d&)> IsInsideFunction; // if not supplied, the intersection counting method is used.
56
57 // Relates the grids index space, to mesh distance units.
58 float CellSize;
59
60 // Width of the band around triangles for which exact Distances are computed
61 // (In fact this is conservative, the band is often larger locally)
63
64 // Bounds of Grid will be expanded this much in positive and negative directions.
65 // Useful for if you want field to extend outwards.
67
68 // Most of this parallelizes very well, makes a huge speed difference
69 bool bUseParallel = true;
70
71 // If the number of cells in any dimension may exceed this, CellSize will be automatically increased to keep cell count reasonable
73
74 // The narrow band is always computed exactly, and the full Grid will be signed if bComputeSigns is true.
81
82 // how wide of narrow band should we compute. This value is
83 // currently only used NarrowBand_SpatialFloodFill, as
84 // we can efficiently explore the space
85 // (in that case ExactBandWidth is only used to initialize the grid extents, and this is used instead during the flood fill)
87
88 // If true, sign of full grid will be computed using either IsInsideFunction (if provided) or crossing as controlled by the InsideMode
89 bool bComputeSigns = true;
90
91 // What counts as "inside" the mesh. Crossing count does not use triangle
92 // Orientation, so inverted faces are fine, but overlapping shells or self intersections
93 // will be filled using even/odd rules (as seen along X axis...)
94 // Winding count is basically mesh winding number, handles overlap shells and
95 // self-intersections, but inverted shells are 'subtracted', and inverted faces are a disaster.
96 // Both modes handle internal cavities, neither handles open sheets.
103
104 // Implementation computes the triangle closest to each Grid cell, can
105 // return this Grid if desired (only reason not to is avoid hanging onto memory)
107
108 // Grid of per-cell crossing or parity counts
110
113 {
114 return false;
115 };
116
117 // OUTPUTS
118
121
122protected:
123 // grid of closest triangle indices; only available if bWantClosestTriGrid is set to true. Access via GetClosestTriGrid()
125 // grid of intersection counts; only available if bWantIntersectionsGrid is set to true. Access via GetIntersectionsGrid()
127
128
129protected:
130 // structs used in a scatter / gather of triangles into the grid blocks they overlap.
131
132 // Grid that holds one atomic<int> in each block
133 struct FScatterCounter : public TBlockedGrid3Layout<FBlockedGrid3f::BlockSize>
134 {
137 : MyLayout(Dims)
138 {
140 const int32 NumBlocks = BlockDims[0] * BlockDims[1] * BlockDims[2];
141 NumObjectsPerBlock.SetNum(NumBlocks);
142 std::atomic<int>* PerBlock = NumObjectsPerBlock.GetData();
143 ParallelFor(NumBlocks, [PerBlock](int32 id)
144 {
145 PerBlock[id] = 0;
146 }, !bInitParallel);
147 }
148
149 // increment the counter for the indicated block
151 {
152 std::atomic<int>& NumObjects = NumObjectsPerBlock.GetData()[BlockIndex];
153 NumObjects.fetch_add(1, std::memory_order_relaxed);
154 }
155
157 };
158
159 // sparse grid that stores array of TriIDs each blocks.
160 struct FTriIDBlockGrid : public TBlockedGrid3Layout<FBlockedGrid3f::BlockSize>
161 {
163
166 {
168 const int32 NumBlocks = DimAsBlocks.X * DimAsBlocks.Y * DimAsBlocks.Z;
169 TrisInBlock.SetNum(NumBlocks);
170 PerBlockNum.SetNum(NumBlocks);
171 const std::atomic<int>* PerBlockCounted = TriCounter.NumObjectsPerBlock.GetData();
172 std::atomic<int>* SizePerBlock = PerBlockNum.GetData();
174 {
176 const int32 n = PerBlockCounted[id];
178 SizePerBlock[id] = 0;
179 }, !bInitParallel);
180 }
181
182
183
184 void AddTriID(const int32& BlockIndex, const int32& TriId)
185 {
187 std::atomic<int>& Num = PerBlockNum.GetData()[BlockIndex];
188
189 int32 i = Num.fetch_add(1, std::memory_order_relaxed);;
190 Tris.GetData()[i] = TriId;
191 }
193 {
194 const int32 NumBlocks = TrisInBlock.Num();
196 for (int32 i = 0; i < NumBlocks; ++i)
197 {
198 if (TrisInBlock[i].Num() != 0)
199 {
201 }
202 }
203
206 for (int32 i = 0; i < NumBlocks; ++i)
207 {
208 if (TrisInBlock[i].Num() != 0)
209 {
210 TmpArray.Add(i);
211 }
212 }
213 return TmpArray;
214 };
215
218 };
219
220 struct FTriBBox
221 {
224
225 bool IsValid() const
226 {
227 return ((BoxMax[0] >= BoxMin[0]) && (BoxMax[1] >= BoxMin[1]) && (BoxMax[2] >= BoxMin[2]));
228 }
229
230 // reduce this box by clamping with specified min/max
232 {
233 BoxMin[0] = FMath::Clamp(BoxMin[0], OtherMin[0], OtherMax[0]);
234 BoxMin[1] = FMath::Clamp(BoxMin[1], OtherMin[1], OtherMax[1]);
235 BoxMin[2] = FMath::Clamp(BoxMin[2], OtherMin[2], OtherMax[2]);
236
237 BoxMax[0] = FMath::Clamp(BoxMax[0], OtherMin[0], OtherMax[0]);
238 BoxMax[1] = FMath::Clamp(BoxMax[1], OtherMin[1], OtherMax[1]);
239 BoxMax[2] = FMath::Clamp(BoxMax[2], OtherMin[2], OtherMax[2]);
240 }
241 };
242
243public:
244
251 const TriangleMeshType* Mesh = nullptr,
252 float CellSize = 1.f,
255 {
256 }
257
262
273 {
276 return ExactCells > 1 && EdgesPerBand >= .25;
277 }
278
279 bool Validate(const FAxisAlignedBox3d& Bounds)
280 {
281 if (Bounds.IsEmpty() || !FMath::IsFinite(Bounds.MaxDim()))
282 {
283 return false;
284 }
285 if (CellSize <= 0 || !FMath::IsFinite(CellSize))
286 {
287 return false;
288 }
290 {
291 return false;
292 }
293 return true;
294 }
295
302 {
303 if (!ensure(Validate(Bounds)))
304 {
305 return false;
306 }
307
308 if (!ensureMsgf(ExactBandWidth*2 < ApproxMaxCellsPerDimension, TEXT("Cannot request band wider than half the max cells per dimension")))
309 {
310 ExactBandWidth = FMath::Max(ApproxMaxCellsPerDimension/2-1, 1);
311 }
312
313 double MaxDim = MaxElement(Bounds.Max - Bounds.Min + ExpandBounds * 2.0);
314 if (!ensureMsgf(MaxDim / CellSize <= ApproxMaxCellsPerDimension - 2 * ExactBandWidth, TEXT("SDF resolution clamped to avoid excessive memory use")))
315 {
317 if (!ensure(CellSize > 0 && FMath::IsFinite(CellSize)))
318 {
319 return false;
320 }
321 }
322
325 {
327 }
330 int32 NI = (int32)((max.X - GridOrigin.X) / CellSize) + 1;
331 int32 NJ = (int32)((max.Y - GridOrigin.Y) / CellSize) + 1;
332 int32 NK = (int32)((max.Z - GridOrigin.Z) / CellSize) + 1;
333
334
336 {
337 if (!ensureMsgf(Spatial && NarrowBandMaxDistance != 0, TEXT("SweepingMeshSDF::Compute: must set Spatial data structure and band max distance")))
338 {
339 return false;
340 }
341 make_level_set3_parallel_floodfill(GridOrigin, CellSize, NI, NJ, NK, Grid, NarrowBandMaxDistance);
342 }
343 else // In general this path is faster than NarrowBand_SpatialFloodFill as it is lockless.
344 {
345 if (Spatial != nullptr)
346 {
347 make_level_set3_parallel_spatial(GridOrigin, CellSize, NI, NJ, NK, Grid, ExactBandWidth);
348 }
349 else
350 {
351 make_level_set3_parallel(GridOrigin, CellSize, NI, NJ, NK, Grid, ExactBandWidth);
352 }
353 }
354
355 if (bComputeSigns)
356 {
357 update_signs(GridOrigin, CellSize, NI, NJ, NK, Grid, IntersectionsGrid);
358 }
359
360
361 CleanupUnwanted();
362
363 return !CancelF();
364 }
365
366
369 {
370 return Grid.GetDimensions();
371 }
372
375 {
376 checkf(bWantClosestTriGrid, TEXT("Set bWantClosestTriGrid=true to return this value"));
377 return ClosestTriGrid;
378 }
379
382 {
383 checkf(bWantIntersectionsGrid, TEXT("Set bWantIntersectionsGrid=true to return this value"));
384 return IntersectionsGrid;
385 }
386
388 float At(int32 I, int32 J, int32 K) const
389 {
390 return Grid.GetValue(I, J, K);
391 }
392
394 float operator[](const FVector3i& ijk) const
395 {
396 return Grid.GetValue(ijk);
397 }
398
400 float GetValue(const FVector3i& ijk) const // TTriLinearGridInterpolant interface
401 {
402 return Grid.GetValue(ijk);
403 }
404
407 {
408 return cell_center(FVector3i(I, J, K));
409 }
410
411
412
413private:
414
415 // iterate over a bounded grid region applying a functor at each cell
416 template <typename FunctorType>
417 void inclusive_loop_3d(const FVector3i& start, const FVector3i& inclusive_end, FunctorType func)
418 {
419 FVector3i ijk;
420 for (ijk[2] = start[2]; ijk[2] <= inclusive_end[2]; ++ijk[2])
421 {
422 for (ijk[1] = start[1]; ijk[1] <= inclusive_end[1]; ++ijk[1])
423 {
424 for (ijk[0] = start[0]; ijk[0] <= inclusive_end[0]; ++ijk[0])
425 {
426 func(ijk);
427 }
428 }
429 }
430 }
431
432 FVector3f cell_center(const FVector3i& IJK) const
433 {
434 return FVector3f((float)IJK.X * CellSize + GridOrigin[0],
435 (float)IJK.Y * CellSize + GridOrigin[1],
436 (float)IJK.Z * CellSize + GridOrigin[2]);
437 }
438
439 float upper_bound(const FVector3i& GridDimensions, float DX) const
440 {
441 return float(GridDimensions[0] + GridDimensions[1] + GridDimensions[2]) * DX;
442 }
443
444 // if provided, uses IsInsideFunction to determine sign, otherwise uses a counting method.
445 void update_signs(const FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, FBlockedGrid3f& Distances, FBlockedGrid3i& IntersectionCountGrid)
446 {
447
449
450
451 if (IsInsideFunction) // Compute signs using inside/outside oracle.
452 {
453
454 TArray<FloatBlockData3Type*> DistanceBlocks = Distances.GetAllocatedBlocks();
456
457 // evaluate the inside/outside oracle at each cell within the allocated blocks.
458 ParallelFor(NumBlocksAllocated, [this, &DistanceBlocks, &Distances, Origin, DX](int32 AllocatedBlockIdx)
459 {
460
462
463 const FVector3i BlockIJK = Distances.BlockIndexToBlockIJK(DistanceBlock.Id);
464 const FVector3i BlockOrigin = FloatBlockData3Type::BlockSize * BlockIJK;
465
466 const int32 ElemCount = FloatBlockData3Type::ElemCount;
467 for (int32 LocalIndex = 0; LocalIndex < ElemCount; ++LocalIndex)
468 {
469 const FVector3i GridCoord = DistanceBlock.ToLocalIJK(LocalIndex) + BlockOrigin;
470 const FVector3d Pos((float)GridCoord.X * DX + Origin[0], (float)GridCoord.Y * DX + Origin[1], (float)GridCoord.Z * DX + Origin[2]);
471 if (this->IsInsideFunction(Pos))
472 {
473 float& dist = DistanceBlock.At(LocalIndex);
474 dist *= -1;
475 }
476 }
477 }, !this->bUseParallel);
478
479 if (CancelF())
480 {
481 return;
482 }
483
484 // update default value for unallocated blocks: evaluate the inside/outside oracle at the center of each.
485
486 const int32 NumBlocks = Distances.GetNumBlocks();
487 const FVector3d BlockCenter(0.5 * DX * (double)FloatBlockData3Type::BlockSize);
488 ParallelFor(NumBlocks, [this, &Distances, Origin, DX, BlockCenter](int32 BlockIndex)
489 {
490 const FVector3i BlockIJK = Distances.BlockIndexToBlockIJK(BlockIndex);
491 if (!Distances.IsBlockAllocated(BlockIJK))
492 {
493 const FVector3i BlockOrigin = FloatBlockData3Type::BlockSize * BlockIJK;
494 const FVector3d BlockOriginPos((float)BlockOrigin.X * DX + Origin.X, (float)BlockOrigin.Y * DX + Origin.Y, (float)BlockOrigin.Z * DX + Origin.Z);
495 const FVector3d Pos = BlockOriginPos + BlockCenter;
496 if (this->IsInsideFunction(Pos))
497 {
498 Distances.ProcessBlockDefaultValue(BlockIJK, [](float& value) {value = -value; });
499 }
500 }
501
502 }, !this->bUseParallel);
503
504 }
505 else // use intersection counts and the specified InsideMode to determine inside/outside regions.
506 {
507 // intersection_count(I,J,K) is # of tri intersections in (I-1,I]x{J}x{K}
508
509 compute_intersections(Origin, DX, NI, NJ, NK, Distances, IntersectionCountGrid);
510 if (CancelF())
511 {
512 return;
513 }
514
515 // then figure out signs (inside/outside) from intersection counts
516 compute_signs(NI, NJ, NK, IntersectionCountGrid, Distances);
517 }
518 } // update_signs
519
520 // compute AABBs for the tris in the mesh. the resulting TArray is indexed by the same TID as the mesh
521 // ( note, if the mesh doesn't have compact TIDs, the boxes that correspond to gaps in the TIDs will be in a state IsInvalid() == true.)
522 TArray<FTriBBox> compute_tri_bboxes(FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, int32 ExactBand)
523 {
524 const double invdx = 1.0 / DX;
525 const FVector3d org((double)Origin[0], (double)Origin[1], (double)Origin[2]);
526
528
529 auto ComputeTriBoundingBox = [this, ExactBand, NI, NJ, NK, invdx, org](const int32 TID, FVector3i& BBMin, FVector3i& BBMax)
530 {
532 Mesh->GetTriVertices(TID, xp, xq, xr);
533
534 // real IJK coordinates of xp/xq/xr
535
536 const FVector3d fp = (xp - org) * invdx;
537 const FVector3d fq = (xq - org) * invdx;
538 const FVector3d fr = (xr - org) * invdx;
539
540 // clamped integer bounding box of triangle plus/minus exact-band
541 BBMin[0] = FMath::Clamp(((int)FMath::Min3(fp[0], fq[0], fr[0])) - ExactBand, 0, NI - 1);
542 BBMin[1] = FMath::Clamp(((int)FMath::Min3(fp[1], fq[1], fr[1])) - ExactBand, 0, NJ - 1);
543 BBMin[2] = FMath::Clamp(((int)FMath::Min3(fp[2], fq[2], fr[2])) - ExactBand, 0, NK - 1);
544
545 BBMax[0] = FMath::Clamp(((int)FMath::Max3(fp[0], fq[0], fr[0])) + ExactBand + 1, 0, NI - 1);
546 BBMax[1] = FMath::Clamp(((int)FMath::Max3(fp[1], fq[1], fr[1])) + ExactBand + 1, 0, NJ - 1);
547 BBMax[2] = FMath::Clamp(((int)FMath::Max3(fp[2], fq[2], fr[2])) + ExactBand + 1, 0, NK - 1);
548 };
549
550 ParallelFor(Mesh->MaxTriangleID(), [this, &ComputeTriBoundingBox, &TriBBoxes](int TID)
551 {
552 if (!Mesh->IsTriangle(TID)) return; // skipped BBoxes will be left in 'bbox.IsInvalid() == true' state
553
554 FVector3i& BoxMin = TriBBoxes.GetData()[TID].BoxMin;
555 FVector3i& BoxMax = TriBBoxes.GetData()[TID].BoxMax;
556 ComputeTriBoundingBox(TID, BoxMin, BoxMax);
557 });
558
559 return TriBBoxes;
560 }
561
562 // scatter box ids (based on the overlap of bbox with grid block) to return a grid that holds IDs in block-level TArrays.
563 FTriIDBlockGrid scatter_tris(const TArray<FTriBBox>& TriBBoxes, int32 NI, int32 NJ, int32 NK)
564 {
565 bool bCancel = false;
566
567 // count number of tris that will scatter to each block.
568 FScatterCounter TriCounter(FVector3i(NI, NJ, NK), this->bUseParallel);
569 {
570 const int32 NumBBoxes = TriBBoxes.Num();
571 ParallelFor(NumBBoxes, [this, &TriBBoxes, &TriCounter, &bCancel](const int32 TID)
572 {
573 constexpr int32 BlockSize = FBlockedGrid3f::BlockSize;
574
575 const FTriBBox& TriBBox = TriBBoxes.GetData()[TID];
576 if (!TriBBox.IsValid())
577 {
578 return;
579 }
580 if (TID % 100 == 0)
581 {
582 bCancel = this->CancelF();
583 }
584 if (bCancel)
585 {
586 return;
587 }
588
589 // convert bbox to block-level ijk
590 FVector3i box_min(TriBBox.BoxMin[0] / BlockSize, TriBBox.BoxMin[1] / BlockSize, TriBBox.BoxMin[2] / BlockSize);
591 FVector3i box_max(TriBBox.BoxMax[0] / BlockSize, TriBBox.BoxMax[1] / BlockSize, TriBBox.BoxMax[2] / BlockSize);
592
593 // increment the count for each block this tri scatters to.
594 inclusive_loop_3d(box_min, box_max,
595 [&TriCounter](const FVector3i& block_ijk)
596 {
597 const int32 BlockIndex = TriCounter.BlockIJKToBlockIndex(block_ijk);
598 TriCounter.AtomicIncrement(BlockIndex);
599 });
600
601 }, !this->bUseParallel);
602 }
603
604 // scatter the tris into the TriIDBlockedGrid
605 FTriIDBlockGrid TriIDBlockGrid(TriCounter, this->bUseParallel);
606 {
607 const int32 NumBBoxes = TriBBoxes.Num();
608 ParallelFor(NumBBoxes, [this, &TriBBoxes, &TriIDBlockGrid, &bCancel](const int32 TID)
609 {
610 constexpr int32 BlockSize = FBlockedGrid3f::BlockSize;
611
612 const FTriBBox& TriBBox = TriBBoxes.GetData()[TID];
613 if (!TriBBox.IsValid())
614 {
615 return;
616 }
617 if (TID % 100 == 0)
618 {
619 bCancel = this->CancelF();
620 }
621 if (bCancel)
622 {
623 return;
624 }
625
626 // convert bbox to block-level ijk
627 FVector3i box_min(TriBBox.BoxMin[0] / BlockSize, TriBBox.BoxMin[1] / BlockSize, TriBBox.BoxMin[2] / BlockSize);
628 FVector3i box_max(TriBBox.BoxMax[0] / BlockSize, TriBBox.BoxMax[1] / BlockSize, TriBBox.BoxMax[2] / BlockSize);
629
630 // increment the count for each block this tri scatters to.
631 inclusive_loop_3d(box_min, box_max,
632 [&TriIDBlockGrid, TID](const FVector3i& block_ijk)
633 {
634 const int32 BlockIndex = TriIDBlockGrid.BlockIJKToBlockIndex(block_ijk);
635 TriIDBlockGrid.AddTriID(BlockIndex, TID); // blocks have already been allocated, and this is a threadsafe add to an array
636 });
637
638 }, !this->bUseParallel);
639 }
640
641 return TriIDBlockGrid;
642 }
643
644 // lockless method - uses a scatter/ gather pattern to implement a painters algorithm (rasterizing all triangles but retaining only the min).
645 void make_level_set3_parallel(const FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, FBlockedGrid3f& DistanceGrid, int32 ExactBand)
646 {
647 const float upper_bound_value = upper_bound(FVector3i(NI, NJ, NK), DX);
648
650 ClosestTriGrid.Reset(NI, NJ, NK, -1);
651
652 // bboxes for each tri.
653 const TArray<FTriBBox> TriBBoxes = compute_tri_bboxes(Origin, DX, NI, NJ, NK, ExactBand);
654
655 // blocked grid of tri ids - identifies the tris that overlap with each grid block.
656 const FTriIDBlockGrid TIDBlockGrid = scatter_tris(TriBBoxes, NI, NJ, NK);
657
658 // identify the blocks that are occupied by tris
659 const TArray<int32> OccupiedBlocks = TIDBlockGrid.GetOccupiedBlockIDs();
660
661 if (this->CancelF())
662 {
663 return;
664 }
665
666
667 const double invdx = 1.0 / DX;
668 const FVector3d org((double)Origin[0], (double)Origin[1], (double)Origin[2]);
669
670 // parallelize over occupied blocks.
671 // within each block, loop over the associated triangles, rasterizing distance (retaining the min).
672 {
673
674 // actually raster the tris into the distance and closest tri grid
675 const int32 NumOccupied = OccupiedBlocks.Num();
677 {
678 const FVector3i GridDimensions = DistanceGrid.GetDimensions();
679 const int32 BlockIndex = OccupiedBlocks.GetData()[OccupiedId];
681
682 const FVector3i BlockIJK = DistanceGrid.BlockIndexToBlockIJK(BlockIndex);
683 // allocates block on demand. doing so here assumes the parallel memory allocator is up to it..
686
687 // bounding box for this block [inclusive, inclusive]
688
689 const FVector3i BlockMin = FBlockedGrid3f::BlockSize * BlockIJK;
690 const FVector3i BlockMax( FMath::Min(BlockMin[0] + FBlockedGrid3f::BlockSize, GridDimensions[0]) -1,
691 FMath::Min(BlockMin[1] + FBlockedGrid3f::BlockSize, GridDimensions[1]) -1,
692 FMath::Min(BlockMin[2] + FBlockedGrid3f::BlockSize, GridDimensions[2]) -1);
693
694 for (const int32 TID : TrisToRasterize)
695 {
696
698 Mesh->GetTriVertices(TID, xp, xq, xr);
699
700 // real IJK coordinates of xp/xq/xr
701
702 const FVector3d fp = (xp - org) * invdx;
703 const FVector3d fq = (xq - org) * invdx;
704 const FVector3d fr = (xr - org) * invdx;
705
706 // clamped integer bounding box of triangle plus exact-band
707 FTriBBox tri_bbox = TriBBoxes.GetData()[TID];
708
709 // clamp against the block
710 tri_bbox.Clamp(BlockMin, BlockMax);
711
712 // iter over the bbox region, min-compositing the distance from this tri into the grid.
713 inclusive_loop_3d(tri_bbox.BoxMin, tri_bbox.BoxMax,
714 [&](const FVector3i& ijk)
715 {
716 const FVector3i local_coords = ijk - BlockMin;
717 const int32 local_index = DistanceBlock.ToLinear(local_coords[0], local_coords[1], local_coords[2]);
718 const FVector3d cellpos((double)ijk[0], (double)ijk[1], (double)ijk[2]);
719 float d = float(DX * PointTriangleDistance(cellpos, fp, fq, fr));
720
721 float& grid_dist = DistanceBlock.At(local_index);
722 if (d < grid_dist)
723 {
724 grid_dist = d;
725 ClosestTriBlock.At(local_index) = TID;
726 }
727 });
728 }
729
730 }, !this->bUseParallel);
731 };
732 } // make_level_set3_parallel
733
734 // lockless method - uses a scatter/ gather pattern along with a spatial acceleration structure to find the closest triangle (relative to each narrow band cell)
735 void make_level_set3_parallel_spatial(FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, FBlockedGrid3f& DistanceGrid, int32 ExactBand)
736 {
737 const float upper_bound_value = upper_bound(FVector3i(NI, NJ, NK), DX);
738
740 ClosestTriGrid.Reset(NI, NJ, NK, -1);
741
742 // bboxes for each tri.
743 const TArray<FTriBBox> TriBBoxes = compute_tri_bboxes(Origin, DX, NI, NJ, NK, ExactBand);
744
745 // blocked grid of tri IDs - identifies the tris that overlap with each grid block.
746 const FTriIDBlockGrid TIDBlockGrid = scatter_tris(TriBBoxes, NI, NJ, NK);
747
748 // identify the blocks that are occupied by tris
749 const TArray<int32> OccupiedBlocks = TIDBlockGrid.GetOccupiedBlockIDs();
750
751
752 if (this->CancelF())
753 {
754 return;
755 }
756
757 // parallelize over occupied blocks.
758 // within the block,
759 // 1st, mark overlapped cells by scattering the tri bboxes identified with block
760 // 2nd, gather the closest distance value into each overlapped cell using the spatial lookup.
761
762 {
763 const double max_dist = ExactBand * (DX * FMathd::Sqrt2);
764
765 // actually raster the tris bbox
768 {
769 const FVector3i GridDimensions = DistanceGrid.GetDimensions();
770 const int32 BlockIndex = OccupiedBlocks.GetData()[OccupiedId];
772
773 const FVector3i BlockIJK = DistanceGrid.BlockIndexToBlockIJK(BlockIndex);
774 const FVector3i BlockOrigin = FBlockedGrid3f::BlockData3Type::BlockSize * BlockIJK;
775 // allocates block on demand. doing so here assumes the parallel memory allocator is up to it..
776 FBlockedGrid3f::BlockData3Type& DistanceBlock = DistanceGrid.TouchBlockData(BlockIJK);
777 FBlockedGrid3i::BlockData3Type& ClosestTriBlock = ClosestTriGrid.TouchBlockData(BlockIJK);
778
779 FBlockedGrid3b::BlockData3Type BitBlock(false, -1);
780 // bounding box for this block [inclusive, inclusive]
781 const FVector3i BlockMin = FBlockedGrid3f::BlockSize * BlockIJK;
782 const FVector3i BlockMax( FMath::Min(BlockMin[0] + FBlockedGrid3f::BlockSize, GridDimensions[0]) - 1,
783 FMath::Min(BlockMin[1] + FBlockedGrid3f::BlockSize, GridDimensions[1]) - 1,
784 FMath::Min(BlockMin[2] + FBlockedGrid3f::BlockSize, GridDimensions[2]) - 1);
785
786 for (const int32 TID : TrisToRasterize)
787 {
789 Mesh->GetTriVertices(TID, xp, xq, xr);
790
791 // clamped integer bounding box of triangle plus exact-band
792 FTriBBox tri_bbox = TriBBoxes.GetData()[TID];
793
794 // clamp against the block
795 tri_bbox.Clamp(BlockMin, BlockMax);
796
797 inclusive_loop_3d(tri_bbox.BoxMin, tri_bbox.BoxMax,
798 [&](const FVector3i& ijk)
799 {
800 const FVector3i local_coords = ijk - BlockMin;
801 const int32 local_index = DistanceBlock.ToLinear(local_coords[0], local_coords[1], local_coords[2]);
802 BitBlock.At(local_index) = true;
803 });
804 }
805
806
807
808 for (int32 local_index = 0; local_index < FBlockedGrid3f::BlockData3Type::ElemCount; ++local_index)
809 {
810 if (BitBlock.At(local_index) == true)
811 {
812 const FVector3i GridCoord = DistanceBlock.ToLocalIJK(local_index) + BlockOrigin;
813
814 const FVector3d Pos((float)GridCoord.X* DX + Origin[0], (float)GridCoord.Y* DX + Origin[1], (float)GridCoord.Z* DX + Origin[2]);
815 double dsqr;
816 int near_tid = this->Spatial->FindNearestTriangle(Pos, dsqr, max_dist);
818 {
820 }
821 else
822 {
823 const float dist = (float)FMath::Sqrt(dsqr);
824 DistanceBlock.At(local_index) = dist;
826 }
827 }
828
829 }
830
831 }, !this->bUseParallel);
832 }
833 }
834
835 // use the mesh vertex points as seeds, and flood fill distance until reaching the exact band width. this uses some thread-locking, but can be fast if the width is not large compared to block size
836 void make_level_set3_parallel_floodfill(FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, FBlockedGrid3f& DistanceGrid, double NarrowBandWidth)
837 {
838
839 typedef FBlockedGrid3f::BlockData3Type FloatBlockData3Type;
840 typedef FBlockedGrid3b::BlockData3Type BoolBlockData3Type;
841 typedef FBlockedGrid3i::BlockData3Type IntBlockData3Type;
842
843 typedef BoolBlockData3Type::BitArrayConstIterator BitArrayConstIter;
844
845 const double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
846 const double invdx = 1.0 / DX;
847 const float upper_bound_value = upper_bound(FVector3i(NI, NJ, NK), DX);
848
850 ClosestTriGrid.Reset(NI, NJ, NK, -1);
851
852 TArray<FCriticalSection> BlockLocks; BlockLocks.SetNum(DistanceGrid.GetNumBlocks());
853 auto BlockLockProvider = [&BlockLocks](int32 block_id)->FCriticalSection&
854 {
855 return BlockLocks.GetData()[block_id];
856 };
857
858 // unsigned distance populate the distance grid and the closest tri grid using a limited flood fill, starting with
859 // the locations of the mesh verts.
860 {
864
865
866
867 // populate the CandidateGrid with all the mesh verts
868 {
869
871
872
873 bool bCancel = false;
874 ParallelFor(Mesh->MaxVertexID(), [this, &bCancel, &CandidateGrid, &BlockLockProvider, NI, NJ, NK, Origin, DX](const int32 VID)
875 {
876 const double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
877 const double invdx = 1.0 / DX;
878
879 if (!Mesh->IsVertex(VID))
880 {
881 return;
882 }
883 if (VID % 1000 == 0)
884 {
885 bCancel = this->CancelF();
886 }
887 if (bCancel)
888 {
889 return;
890 }
891 const FVector3d v = Mesh->GetVertex(VID);
892
893 // real IJK coordinates of v
894 const double fi = (v.X - ox) * invdx, fj = (v.Y - oy) * invdx, fk = (v.Z - oz) * invdx;
895
896 const FVector3i Idx( FMath::Clamp((int32)fi, 0, NI - 1),
897 FMath::Clamp((int32)fj, 0, NJ - 1),
898 FMath::Clamp((int32)fk, 0, NK - 1));
899
900 CandidateGrid.SetValueWithLock(Idx.X, Idx.Y, Idx.Z, true, BlockLockProvider);
901
902 }, !this->bUseParallel);
903
904 if (bCancel)
905 {
906 return;
907 }
908 }
909
910 const double max_dist = NarrowBandWidth;
911 const double max_query_dist = max_dist + (2.0 * DX * FMathd::Sqrt2);
912
913 int32 NumCandidateBlocks = CandidateGrids[CurCandidateID].GetAllocatedBlocks().Num();
914 while (NumCandidateBlocks > 0)
915 {
918
919 VisitedGrid.PreAllocateFromSourceGrid(CandidateGrid);
920 DistanceGrid.PreAllocateFromSourceGrid(CandidateGrid);
922 NextCandidateGrid.Reset(NI, NJ, NK, false);
923
924 // for each block with candidate locations, generate seed points and flood fill within the block and capture fill that exits the block
925 // in the NextCandidateGrid.
926
928
930 {
932 const int32 BlockIdx = CandidateBlock.Id;
933
937
938 const FVector3i BlockIJK = DistanceGrid.BlockIndexToBlockIJK(BlockIdx);
939 const FVector3i BlockOrigin = FloatBlockData3Type::BlockSize * BlockIJK;
940
941 // the candidates were scattered from neighboring blocks, but if this block has already been processed some
942 // of them may have been visited.
943
944 // make a mask that is the subtraction of the visited cells from the candidate cells.
945 BoolBlockData3Type::BlockDataBitMask Mask = CandidateBlock.BitArray;
946 Mask.CombineWithBitwiseXOR(VistedBlock.BitArray, EBitwiseOperatorFlags::MaintainSize);
947 Mask.CombineWithBitwiseAND(CandidateBlock.BitArray, EBitwiseOperatorFlags::MaintainSize);
948
949 // inter over unvisited candidate cells, updating result grids and creating a list of seed points.
952 {
953 const int32 LocalIndex = CIter.GetIndex();
954 VistedBlock.BitArray[LocalIndex] = true;
955
956 const FVector3i LocalCoords = DistanceBlock.ToLocalIJK(LocalIndex);
957 const FVector3i GridCoords = LocalCoords + BlockOrigin;
958 const FVector3d Pos(cell_center(GridCoords));
959
960 double dsqr;
961 const int32 near_tid = this->Spatial->FindNearestTriangle(Pos, dsqr, max_query_dist);
963 {
964 const float dist = (float)FMath::Sqrt(dsqr);
965 DistanceBlock.At(LocalIndex) = dist;
966 ClosestTriBlock.At(LocalIndex) = near_tid;
967
969 }
970 }
971
972 auto IsInBlock = [](const FVector3i& LocalCoords)
973 {
974 bool bInBlock = true;
975 bInBlock = LocalCoords.X > -1 && LocalCoords.Y > -1 && LocalCoords.Z > -1;
976 bInBlock = bInBlock && LocalCoords.X < FloatBlockData3Type::BlockSize&& LocalCoords.Y < FloatBlockData3Type::BlockSize && LocalCoords.Z < FloatBlockData3Type::BlockSize;
977 return bInBlock;
978 };
979
980 // flood fill from the seeds within the block. if flood fill exits the block, mark the locations in the NextCanidateGrid for the next pass.
981 while(SeedLocalCoords.Num() > 0)
982 {
983 const FVector3i Seed = SeedLocalCoords.Pop(EAllowShrinking::No);
984 for (const FVector3i& idx_offset : IndexUtil::GridOffsets26)
985 {
986 const FVector3i LocalCoords = Seed + idx_offset;
987 const FVector3i GridCoords = LocalCoords + BlockOrigin;
989 {
990 const int32 local_index = FloatBlockData3Type::ToLinear(LocalCoords.X, LocalCoords.Y, LocalCoords.Z);
991 if ((bool)VistedBlock.BitArray[local_index] == false)
992 {
993 VistedBlock.BitArray[local_index] = true;
994
995 const FVector3d Pos(cell_center(GridCoords));
996
997 double dsqr;
998 const int32 near_tid = this->Spatial->FindNearestTriangle(Pos, dsqr, max_query_dist);
1000 {
1001 const float dist = (float)FMath::Sqrt(dsqr);
1002 DistanceBlock.At(local_index) = dist;
1004
1006 }
1007
1008 }
1009 }
1010 else // outside this block - mark the location as a candidate for the next pass.
1011 {
1012 if (NextCandidateGrid.IsValidIJK(GridCoords))
1013 {
1014 const bool bIsCanidate = true;
1016 }
1017 }
1018 }
1019 }
1020
1021
1022 },
1023 !this->bUseParallel);
1024
1026 NumCandidateBlocks = CandidateGrids[CurCandidateID].GetAllocatedBlocks().Num();
1027
1028 if (CancelF())
1029 {
1030 return;
1031 }
1032 }
1033
1034 }
1035 } // end make_level_set_3
1036
1037
1038 void CleanupUnwanted()
1039 {
1040 if (!bWantClosestTriGrid)
1041 {
1042 ClosestTriGrid.Reset();
1043 }
1044
1045 if (!bWantIntersectionsGrid)
1046 {
1047 IntersectionsGrid.Reset();
1048 }
1049 }
1050
1051
1052
1053 // fill the intersection Grid w/ number of intersections in each cell
1054 void compute_intersections(FVector3f Origin, float DX, int32 NI, int32 NJ, int32 NK, const FBlockedGrid3f& DistanceGrid, FBlockedGrid3i& IntersectionCountGrid)
1055 {
1056
1057 // grid of atomic<int>, pre-allocated to match the topology of the distance grid.
1058 struct FAtomicGrid3i : public TBlockedGrid3Layout<FBlockedGrid3f::BlockSize>
1059 {
1060 typedef TStaticArray<std::atomic<int32>, FBlockedGrid3f::BlockElemCount> BlockAtomicData;
1061 typedef TBlockedGrid3Layout<FBlockedGrid3f::BlockSize> MyLayout;
1062
1063 FAtomicGrid3i(const FBlockedGrid3f& Grid3f, const bool bThreadedBuild)
1064 : MyLayout(Grid3f.GetDimensions())
1065 {
1066 const int32 NumBlocks = Grid3f.GetNumBlocks();
1067 Blocks.SetNum(NumBlocks);
1068 // we use a parallel memory allocator, so we allow allocation to occur inside this parallel loop.
1069 ParallelFor(NumBlocks, [this, &Grid3f](int32 i)
1070 {
1071 if (Grid3f.GetBlockData(i).IsValid())
1072 {
1073 Blocks[i] = MakeUnique<BlockAtomicData>();
1074 BlockAtomicData& Data= *Blocks[i];
1075 for (int32 j = 0; j < FBlockedGrid3i::BlockElemCount; ++j)
1076 {
1077 Data[j] = 0;
1078 }
1079 }
1080 }, !bThreadedBuild);
1081 }
1083 } AtomicGrid3i(DistanceGrid, this->bUseParallel);
1084
1085 bool bCancel= false;
1086
1087 // this is what we will do for each triangle. There are no Grid-reads, only Grid-writes,
1088 // since we use atomic_increment, it is always thread-safe
1089 ParallelFor(Mesh->MaxTriangleID(),
1090 [this, Origin, DX, NI, NJ, NK, &IntersectionCountGrid, &AtomicGrid3i, &bCancel](int32 TID)
1091 {
1092 double ox = (double)Origin[0], oy = (double)Origin[1], oz = (double)Origin[2];
1093 double invdx = 1.0 / DX;
1094
1095 if (!Mesh->IsTriangle(TID))
1096 {
1097 return;
1098 }
1099 if (TID % 100 == 0 && CancelF() == true)
1100 {
1101 bCancel = true;
1102 }
1103 if (bCancel)
1104 {
1105 return;
1106 }
1107
1109 Mesh->GetTriVertices(TID, xp, xq, xr);
1110
1111
1112 bool neg_x = false;
1113 if (InsideMode == EInsideModes::WindingCount)
1114 {
1115 FVector3d n = VectorUtil::NormalDirection(xp, xq, xr);
1116 neg_x = n.X > 0;
1117 }
1118
1119 // real IJK coordinates of xp/xq/xr
1120 double fip = (xp[0] - ox) * invdx, fjp = (xp[1] - oy) * invdx, fkp = (xp[2] - oz) * invdx;
1121 double fiq = (xq[0] - ox) * invdx, fjq = (xq[1] - oy) * invdx, fkq = (xq[2] - oz) * invdx;
1122 double fir = (xr[0] - ox) * invdx, fjr = (xr[1] - oy) * invdx, fkr = (xr[2] - oz) * invdx;
1123
1124 // recompute J/K integer bounds of triangle w/o exact band
1125 int32 j0 = FMath::Clamp(FMath::CeilToInt32(FMath::Min3(fjp, fjq, fjr)), 0, NJ - 1);
1126 int32 j1 = FMath::Clamp(FMath::FloorToInt32(FMath::Max3(fjp, fjq, fjr)), 0, NJ - 1);
1127 int32 k0 = FMath::Clamp(FMath::CeilToInt32(FMath::Min3(fkp, fkq, fkr)), 0, NK - 1);
1128 int32 k1 = FMath::Clamp(FMath::FloorToInt32(FMath::Max3(fkp, fkq, fkr)), 0, NK - 1);
1129
1130
1131 auto AtomicIncDec = [&AtomicGrid3i, neg_x](const int32 i, const int32 j, const int32 k)
1132 {
1133 int32 BlockIndex, LocalIndex;
1134 AtomicGrid3i.GetBlockAndLocalIndex(i, j, k, BlockIndex, LocalIndex);
1135
1136 // note the grid has been pre-allocated, so the blocks will exist
1137 auto& ABlock = *AtomicGrid3i.Blocks[BlockIndex];
1138 std::atomic<int32>& value = ABlock[LocalIndex];
1139 if (neg_x)
1140 {
1141 value.fetch_sub(1, std::memory_order_relaxed);
1142 }
1143 else
1144 {
1145 value.fetch_add(1, std::memory_order_relaxed);
1146 }
1147 };
1148
1149 // and do intersection counts
1150 for (int K = k0; K <= k1; ++K)
1151 {
1152 for (int J = j0; J <= j1; ++J)
1153 {
1154 double A, B, C;
1155 if (PointInTriangle2d(J, K, fjp, fkp, fjq, fkq, fjr, fkr, A, B, C))
1156 {
1157 double fi = A * fip + B * fiq + C * fir; // intersection I coordinate
1158 int i_interval = FMath::CeilToInt32(fi); // intersection is in (i_interval-1,i_interval]
1159 if (i_interval < 0)
1160 {
1161 AtomicIncDec(0, J, K);
1162 }
1163 else if (i_interval < NI)
1164 {
1165 AtomicIncDec(i_interval, J, K);
1166 }
1167 else
1168 {
1169 // we ignore intersections that are beyond the +x side of the Grid
1170 }
1171 }
1172 }
1173 }
1174 }, !this->bUseParallel);
1175
1176 IntersectionCountGrid.Reset(NI, NJ, NK, 0);
1177 // copy result back to the intersection grid
1178 const int32 NumBlocks = AtomicGrid3i.Blocks.Num();
1180 {
1182
1183 if (AtomicBlockPtr.IsValid())
1184 {
1185 FVector3i BlockIJK = AtomicGrid3i.BlockIndexToBlockIJK(blockid);
1186 FBlockedGrid3i::BlockData3Type& BlockData = IntersectionCountGrid.TouchBlockData(BlockIJK);
1187 const typename FAtomicGrid3i::BlockAtomicData& AtomicBlockData = *AtomicBlockPtr;
1188
1189 for (int32 j = 0; j < FBlockedGrid3i::BlockElemCount; ++j)
1190 {
1191 const int32 Count = AtomicBlockData[j];
1192 BlockData.At(j) = Count;
1193 }
1194
1195 // delete the atomic block after copying the data.
1197 }
1198
1199 }, !this->bUseParallel);
1200 }
1201
1202
1203 // iterate through each x-row of Grid and set unsigned distances to be negative (inside the mesh), based on the IntersectionCount
1204 void compute_signs(int32 NI, int32 NJ, int32 NK, const FBlockedGrid3i& IntersectionCountGrid, FBlockedGrid3f& Distances)
1205 {
1206 if (bUseParallel)
1207 {
1208 bool bCancel = false;
1209
1210 const FVector3i BlockDims = Distances.GetBlockDimensions();
1211 // can process each x block-row in parallel
1212 ParallelFor(BlockDims.Y * BlockDims.Z, [this, BlockDims, &Distances, &IntersectionCountGrid, &bCancel](int32 BlockRowID)
1213 {
1214 const FVector3i GridDims = Distances.GetDimensions();
1215 if (BlockRowID % 10 == 0 && CancelF() == true)
1216 {
1217 bCancel = true;
1218 }
1219
1220 if (bCancel)
1221 {
1222 return;
1223 }
1224
1225 constexpr int32 BlockSize = FBlockedGrid3f::BlockSize;
1227 for (int32 i = 0, I = BlockSize * BlockSize; i < I; ++i)
1228 {
1229 BlockCrossSection[i] = 0;
1230 }
1231
1232 // 2d array linearized as BlockRowID = BlockJ + BlockDims[1]*BlockK
1233 const int32 BlockJ = BlockRowID % BlockDims[1];
1234 const int32 BlockK = BlockRowID / BlockDims[1];
1235 FVector3i BlockIJK(0, BlockJ, BlockK);
1236 for ( BlockIJK[0] = 0; BlockIJK[0] < BlockDims[0]; ++BlockIJK[0]) {
1237
1238 if (Distances.IsBlockAllocated(BlockIJK))
1239 {
1240 checkSlow(IntersectionCountGrid.IsBlockAllocated(BlockIJK));
1241
1242 FBlockedGrid3f::BlockData3Type& DistanceBlockData = Distances.TouchBlockData(BlockIJK);
1243 const FBlockedGrid3i::BlockData3Type& IntersectionCountBlockData = *IntersectionCountGrid.GetBlockData(BlockIJK);
1244
1245 // note, some data values stored in the blocks are formally outside the dimensions of the grids..
1246 // so this loop potentially touches some regions that will never be accessed.
1247 for (int32 k = 0; k < BlockSize; ++k)
1248 {
1249 for (int32 j = 0; j < BlockSize; ++j)
1250 {
1251 int32& total_count = BlockCrossSection[j + k * BlockSize];
1252
1253 for (int32 i = 0; i < BlockSize; ++i)
1254 {
1255 const int32 LocalIndex = TBlockData3Layout<BlockSize>::ToLinear(i, j, k);
1256
1257 total_count += IntersectionCountBlockData.At(LocalIndex);
1258
1259 if (
1260 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1261 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1262 )
1263 {
1264 // we are inside the mesh
1265 float& d = DistanceBlockData.At(LocalIndex);
1266 d = -FMath::Abs(d);
1267 }
1268
1269 }
1270 }
1271 }
1272
1273 }
1274 else
1275 {
1276 checkSlow(!IntersectionCountGrid.IsBlockAllocated(BlockIJK));
1277
1278 // the block isn't allocated - so it should all share the same state (inside or outside) for water-tight meshes,
1279 // but the counting method used for sign assignment may produce odd results for non water-tight meshes where a block
1280 // far from the narrow band may have both inside and outside elements.
1281
1282 // count the number of "inside" cells on the (already processed) neighbor block face adjacent to BlockIJK
1284 {
1285 for (int32 index = 0, IndexMax = BlockSize * BlockSize; index < IndexMax; ++index)
1286 {
1287 const int32 total_count = BlockCrossSection[index];
1288 if (
1289 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1290 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1291 )
1292 {
1294 }
1295 }
1296
1297 }
1298
1299 switch (NumInsideCells)
1300 {
1301 case 0:
1302 {
1303 // neighbor is all outside - this unallocated block can be represented by existing positive value ( do nothing)
1304 break;
1305 }
1306 case (BlockSize * BlockSize):
1307 {
1308 // neighbor is all inside - the unallocated block can be represented by single negative
1309 // we are inside the mesh
1310 Distances.ProcessBlockDefaultValue(BlockIJK, [](float& value) {value = -FMath::Abs(value); });
1311 break;
1312 }
1313 default:
1314 {
1315 // allocate the block (unique blocks can be allocated in parallel)
1316 FBlockedGrid3f::BlockData3Type& DistanceBlockData = Distances.TouchBlockData(BlockIJK);
1317
1318 for (int32 k = 0; k < BlockSize; ++k)
1319 {
1320 for (int32 j = 0; j < BlockSize; ++j)
1321 {
1322 const int32 total_count = BlockCrossSection[j + k * BlockSize];
1323
1324 for (int32 i = 0; i < BlockSize; ++i)
1325 {
1326 const int32 LocalIndex = TBlockData3Layout<BlockSize>::ToLinear(i, j, k);
1327
1328 if (
1329 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1330 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1331 )
1332 {
1333 // we are inside the mesh
1334 float& d = DistanceBlockData.At(LocalIndex);
1335 d = -FMath::Abs(d);
1336 }
1337
1338 }
1339 }
1340 }
1341
1342 break;
1343 }
1344 }
1345 }
1346
1347 }
1348 }, !this->bUseParallel);
1349
1350 }
1351 else
1352 {
1353 for (int K = 0; K < NK; ++K)
1354 {
1355 if (CancelF())
1356 {
1357 return;
1358 }
1359
1360 for (int J = 0; J < NJ; ++J)
1361 {
1362 int total_count = 0;
1363 for (int I = 0; I < NI; ++I)
1364 {
1365 total_count += IntersectionCountGrid.GetValue(I, J, K);
1366 if (
1367 (InsideMode == EInsideModes::WindingCount && total_count > 0) ||
1368 (InsideMode == EInsideModes::CrossingCount && total_count % 2 == 1)
1369 )
1370 {
1371 // we are inside the mesh
1372 Distances.ProcessValue(I, J, K, [](float& value){value = -value;});
1373 }
1374 }
1375 }
1376 }
1377 }
1378 }
1379};
1380
1381
1382} // end namespace UE::Geometry
1383} // 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::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
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
UE::Math::TVector< float > FVector3f
Definition MathFwd.h:73
@ Num
Definition MetalRHIPrivate.h:234
const bool
Definition NetworkReplayStreaming.h:178
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
int BlockIndex
Definition binka_ue_decode_test.cpp:38
if(Failed) console_printf("Failed.\n")
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUninitialized()
Definition Array.h:1664
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT ElementType * GetData() UE_LIFETIMEBOUND
Definition Array.h:1027
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition AndroidPlatformMisc.h:14
Definition StaticArray.h:26
Definition FunctionFwd.h:19
Definition UniquePtr.h:107
void Reset(T *InPtr=nullptr)
Definition UniquePtr.h:346
bool PreAllocateFromSourceGrid(const TBasicBlockedDenseGrid3< OtherElemType, BlockSize > &SourceGrid)
Definition BlockedDenseGrid3.h:383
ElemType GetValue(int32 I, int32 J, int32 K) const
Definition BlockedDenseGrid3.h:319
void Reset()
Definition BlockedDenseGrid3.h:301
Definition BlockedLayout3.h:50
const FVector3i & GetDimensions() const
Definition BlockedLayout3.h:89
const FVector3i & GetBlockDimensions() const
Definition BlockedLayout3.h:97
TUniquePtr< BlockData3Type > & GetBlockData(const FVector3i &BlockIJK)
Definition BlockedDenseGrid3.h:632
BlockData3Type & TouchBlockData(const FVector3i &BlockIJK)
Definition BlockedDenseGrid3.h:622
TBlockData3< float, BlockSize_ > BlockData3Type
Definition BlockedDenseGrid3.h:436
static constexpr int32 BlockSize
Definition BlockedDenseGrid3.h:439
Definition MeshAABBTree3.h:61
virtual int FindNearestTriangle(const FVector3d &P, double &NearestDistSqr, const FQueryOptions &Options=FQueryOptions()) const override
Definition MeshAABBTree3.h:159
Definition SparseNarrowBandMeshSDF.h:49
const TriangleMeshType * Mesh
Definition SparseNarrowBandMeshSDF.h:53
FVector3i Dimensions() const
Definition SparseNarrowBandMeshSDF.h:368
float operator[](const FVector3i &ijk) const
Definition SparseNarrowBandMeshSDF.h:394
EInsideModes InsideMode
Definition SparseNarrowBandMeshSDF.h:102
FVector3f GridOrigin
Definition SparseNarrowBandMeshSDF.h:119
int ApproxMaxCellsPerDimension
Definition SparseNarrowBandMeshSDF.h:72
EComputeModes
Definition SparseNarrowBandMeshSDF.h:76
@ NarrowBandOnly
Definition SparseNarrowBandMeshSDF.h:77
@ NarrowBand_SpatialFloodFill
Definition SparseNarrowBandMeshSDF.h:78
TMeshAABBTree3< TriangleMeshType > * Spatial
Definition SparseNarrowBandMeshSDF.h:54
bool bComputeSigns
Definition SparseNarrowBandMeshSDF.h:89
TUniqueFunction< bool(const FVector3d &)> IsInsideFunction
Definition SparseNarrowBandMeshSDF.h:55
bool bWantClosestTriGrid
Definition SparseNarrowBandMeshSDF.h:106
const FBlockedGrid3i & GetClosestTriGrid() const
Definition SparseNarrowBandMeshSDF.h:374
FBlockedGrid3f Grid
Definition SparseNarrowBandMeshSDF.h:120
double NarrowBandMaxDistance
Definition SparseNarrowBandMeshSDF.h:86
int ExactBandWidth
Definition SparseNarrowBandMeshSDF.h:62
TTriLinearGridInterpolant< TSparseNarrowBandMeshSDF > MakeInterpolant()
Definition SparseNarrowBandMeshSDF.h:258
float CellSize
Definition SparseNarrowBandMeshSDF.h:58
static bool ShouldUseSpatial(int32 ExactCells, double CellSize, double AvgEdgeLen)
Definition SparseNarrowBandMeshSDF.h:272
const FBlockedGrid3i & GetIntersectionsGrid() const
Definition SparseNarrowBandMeshSDF.h:381
EInsideModes
Definition SparseNarrowBandMeshSDF.h:98
@ WindingCount
Definition SparseNarrowBandMeshSDF.h:100
@ CrossingCount
Definition SparseNarrowBandMeshSDF.h:99
TSparseNarrowBandMeshSDF(const TriangleMeshType *Mesh=nullptr, float CellSize=1.f, TMeshAABBTree3< TriangleMeshType > *Spatial=nullptr)
Definition SparseNarrowBandMeshSDF.h:250
float At(int32 I, int32 J, int32 K) const
Definition SparseNarrowBandMeshSDF.h:388
bool bUseParallel
Definition SparseNarrowBandMeshSDF.h:69
EComputeModes ComputeMode
Definition SparseNarrowBandMeshSDF.h:80
FBlockedGrid3i ClosestTriGrid
Definition SparseNarrowBandMeshSDF.h:124
TFunction< bool()> CancelF
Definition SparseNarrowBandMeshSDF.h:112
FVector3f CellCenter(int32 I, int32 J, int32 K) const
Definition SparseNarrowBandMeshSDF.h:406
bool bWantIntersectionsGrid
Definition SparseNarrowBandMeshSDF.h:109
float GetValue(const FVector3i &ijk) const
Definition SparseNarrowBandMeshSDF.h:400
bool Compute(FAxisAlignedBox3d Bounds)
Definition SparseNarrowBandMeshSDF.h:301
bool Validate(const FAxisAlignedBox3d &Bounds)
Definition SparseNarrowBandMeshSDF.h:279
FVector3d ExpandBounds
Definition SparseNarrowBandMeshSDF.h:66
FBlockedGrid3i IntersectionsGrid
Definition SparseNarrowBandMeshSDF.h:126
Definition GridInterpolant.h:29
constexpr int InvalidID
Definition IndexTypes.h:13
Definition IndexUtil.h:18
GEOMETRYCORE_API const FVector3i GridOffsets26[26]
Definition IndexUtil.cpp:32
void AtomicIncDec(FDenseGrid2i &Grid, int32 i, int32 j, bool bDecrement=false)
Definition DenseGrid2.h:207
constexpr T MaxElement(const UE::Math::TVector< T > &Vector)
Definition VectorTypes.h:298
TBlockedGrid3< bool, 8 > FBlockedGrid3b
Definition BlockedDenseGrid3.h:718
TBlockedGrid3< int, 8 > FBlockedGrid3i
Definition BlockedDenseGrid3.h:715
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
TBlockedGrid3< float, 8 > FBlockedGrid3f
Definition BlockedDenseGrid3.h:713
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
RealType MaxDim() const
Definition BoxTypes.h:598
TVector< RealType > Min
Definition BoxTypes.h:248
Definition SparseNarrowBandMeshSDF.h:134
void AtomicIncrement(const int32 BlockIndex)
Definition SparseNarrowBandMeshSDF.h:150
FScatterCounter(const FVector3i &Dims, bool bInitParallel)
Definition SparseNarrowBandMeshSDF.h:136
TArray< std::atomic< int > > NumObjectsPerBlock
Definition SparseNarrowBandMeshSDF.h:156
TBlockedGrid3Layout< FBlockedGrid3f::BlockSize > MyLayout
Definition SparseNarrowBandMeshSDF.h:135
Definition SparseNarrowBandMeshSDF.h:221
FVector3i BoxMax
Definition SparseNarrowBandMeshSDF.h:223
FVector3i BoxMin
Definition SparseNarrowBandMeshSDF.h:222
void Clamp(const FVector3i &OtherMin, const FVector3i &OtherMax)
Definition SparseNarrowBandMeshSDF.h:231
bool IsValid() const
Definition SparseNarrowBandMeshSDF.h:225
Definition SparseNarrowBandMeshSDF.h:161
TBlockedGrid3Layout< FBlockedGrid3f::BlockSize > MyLayout
Definition SparseNarrowBandMeshSDF.h:162
TArray< TArray< int32 > > TrisInBlock
Definition SparseNarrowBandMeshSDF.h:217
FTriIDBlockGrid(const FScatterCounter &TriCounter, bool bInitParallel)
Definition SparseNarrowBandMeshSDF.h:164
TArray< int32 > GetOccupiedBlockIDs() const
Definition SparseNarrowBandMeshSDF.h:192
void AddTriID(const int32 &BlockIndex, const int32 &TriId)
Definition SparseNarrowBandMeshSDF.h:184
TArray< std::atomic< int > > PerBlockNum
Definition SparseNarrowBandMeshSDF.h:216
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