UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
MarchingCubes.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3// Port of geometry3Sharp MarchingCubesPro
4
5#pragma once
6
7#include "Async/ParallelFor.h"
8#include "BoxTypes.h"
10#include "Containers/Array.h"
13#include "Containers/Map.h"
14#include "HAL/CriticalSection.h"
15#include "IndexTypes.h"
16#include "IntBoxTypes.h"
17#include "IntVectorTypes.h"
18#include "Math/UnrealMathSSE.h"
20#include "Math/Vector.h"
21#include "MathUtil.h"
22#include "MeshShapeGenerator.h"
24#include "Misc/ScopeLock.h"
27#include "Spatial/DenseGrid3.h"
28#include "Templates/Function.h"
29#include "Templates/TypeHash.h"
31#include "Util/IndexUtil.h"
32#include "VectorTypes.h"
33
34#include <atomic>
35
36namespace UE
37{
38namespace Geometry
39{
40
41using namespace UE::Math;
42
43enum class /*GEOMETRYCORE_API*/ ERootfindingModes
44{
48};
49
50class /*GEOMETRYCORE_API*/ FMarchingCubes : public FMeshShapeGenerator
51{
52public:
57
62 double IsoValue = 0;
63
69
74 double CubeSize = 0.1;
75
80 bool bParallelCompute = true;
81
88
93
98
103
104
106 TFunction<bool(void)> CancelF = []() { return false; };
107
108 /*
109 * Outputs
110 */
111
112 // cube indices range from [Origin,CellDimensions)
114
115
121
123 {
124 }
125
126 bool Validate()
127 {
128 return CubeSize > 0 && FMath::IsFinite(CubeSize) && !Bounds.IsEmpty() && FMath::IsFinite(Bounds.MaxDim());
129 }
130
135 {
137
138 if (!ensure(Validate()))
139 {
140 return *this;
141 }
142
144 GridBounds = FAxisAlignedBox3i(FVector3i::Zero(), CellDimensions - FVector3i(1,1,1)); // grid bounds are inclusive
145
147 {
149 }
151 ResetMesh();
152
153 if (bParallelCompute)
154 {
156 }
157 else
158 {
160 }
161
162 // finalize mesh
163 BuildMesh();
164
165 return *this;
166 }
167
168
170 {
172
173 if (!ensure(Validate()))
174 {
175 return *this;
176 }
177
179 GridBounds = FAxisAlignedBox3i(FVector3i::Zero(), CellDimensions - FVector3i(1,1,1)); // grid bounds are inclusive
180
182 ResetMesh();
183
185 {
187 {
189 }
191 {
193 }
194 }
195 else
196 {
198 {
200 }
202 {
204 }
205 }
206
207 if (bParallelCompute)
208 {
210 }
211 else
212 {
214 }
215
216 // finalize mesh
217 BuildMesh();
218
220
221 return *this;
222 }
223
224
225protected:
226
227
230
231
232 // we pass Cells around, this makes code cleaner
234 {
235 // TODO we do not actually need to store i, we just need the min-corner!
236 FVector3i i[8]; // indices of corners of cell
237 double f[8]; // field values at corners
238 };
239
241 {
242 int NX = (int)(Bounds.Width() / CubeSize) + 1;
243 int NY = (int)(Bounds.Height() / CubeSize) + 1;
244 int NZ = (int)(Bounds.Depth() / CubeSize) + 1;
245 int MaxDim = FMath::Max3(NX, NY, NZ);
246 if (!ensure(MaxDim <= SafetyMaxDimension))
247 {
249 NX = (int)(Bounds.Width() / CubeSize) + 1;
250 NY = (int)(Bounds.Height() / CubeSize) + 1;
251 NZ = (int)(Bounds.Depth() / CubeSize) + 1;
252 }
254 }
255
257 {
258 Pos.X = Bounds.Min.X + CubeSize * IJK.X;
259 Pos.Y = Bounds.Min.Y + CubeSize * IJK.Y;
260 Pos.Z = Bounds.Min.Z + CubeSize * IJK.Z;
261 }
263 {
264 return TVector<double>(Bounds.Min.X + CubeSize * IJK.X,
265 Bounds.Min.Y + CubeSize * IJK.Y,
266 Bounds.Min.Z + CubeSize * IJK.Z);
267 }
269 {
270 return FVector3i(
271 (int)((Pos.X - Bounds.Min.X) / CubeSize),
272 (int)((Pos.Y - Bounds.Min.Y) / CubeSize),
273 (int)((Pos.Z - Bounds.Min.Z) / CubeSize));
274 }
275
276
277
278 //
279 // corner and edge hash functions, these pack the coordinate
280 // integers into 16-bits, so max of 65536 in any dimension.
281 //
282
283
285 {
286 return ((int64)Idx.X&0xFFFF) | (((int64)Idx.Y&0xFFFF) << 16) | (((int64)Idx.Z&0xFFFF) << 32);
287 }
288 int64 corner_hash(int X, int Y, int Z)
289 {
290 return ((int64)X & 0xFFFF) | (((int64)Y & 0xFFFF) << 16) | (((int64)Z & 0xFFFF) << 32);
291 }
292
293 const int64 EDGE_X = int64(1) << 60;
294 const int64 EDGE_Y = int64(1) << 61;
295 const int64 EDGE_Z = int64(1) << 62;
296
298 {
299 if ( Idx1.X != Idx2.X )
300 {
301 int xlo = FMath::Min(Idx1.X, Idx2.X);
302 return corner_hash(xlo, Idx1.Y, Idx1.Z) | EDGE_X;
303 }
304 else if ( Idx1.Y != Idx2.Y )
305 {
306 int ylo = FMath::Min(Idx1.Y, Idx2.Y);
307 return corner_hash(Idx1.X, ylo, Idx1.Z) | EDGE_Y;
308 }
309 else
310 {
311 int zlo = FMath::Min(Idx1.Z, Idx2.Z);
312 return corner_hash(Idx1.X, Idx1.Y, zlo) | EDGE_Z;
313 }
314 }
315
316
317
318 //
319 // Hash table for edge vertices
320 //
321
325
327 {
328 int32 SectionIndex = (int32)(hash % (NumEdgeVertexSections - 1));
329 FScopeLock Lock(&EdgeVertexSectionLocks[SectionIndex]);
330 int* Found = EdgeVertexSections[SectionIndex].Find(hash);
331 return (Found != nullptr) ? *Found : IndexConstants::InvalidID;
332 }
333
335 {
336 int32 SectionIndex = (int32)(hash % (NumEdgeVertexSections - 1));
337 FScopeLock Lock(&EdgeVertexSectionLocks[SectionIndex]);
338 int* FoundVID = EdgeVertexSections[SectionIndex].Find(hash);
339 if (FoundVID != nullptr)
340 {
341 return *FoundVID;
342 }
343 int NewVID = append_vertex(Pos, hash);
344 EdgeVertexSections[SectionIndex].Add(hash, NewVID);
345 return NewVID;
346 }
347
348
349 int edge_vertex_id(const FVector3i& Idx1, const FVector3i& Idx2, double F1, double F2)
350 {
351 int64 hash = edge_hash(Idx1, Idx2);
352
353 int foundvid = FindVertexID(hash);
355 {
356 return foundvid;
357 }
358
359 // ok this is a bit messy. We do not want to lock the entire hash table
360 // while we do find_iso. However it is possible that during this time we
361 // are unlocked we have re-entered with the same edge. So when we
362 // re-acquire the lock we need to check again that we have not already
363 // computed this edge, otherwise we will end up with duplicate vertices!
364
369 find_iso(pa, pb, F1, F2, Pos);
370
371 return AppendOrFindVertexID(hash, Pos);
372 }
373
374
375
376
377
378
379
380
381 //
382 // store corner values in pre-allocated grid that has
383 // FMathf::MaxReal as sentinel.
384 // (note this is float grid, not double...)
385 //
386
388
390 {
391 // note: it's possible to have a race here, where multiple threads might both
392 // GetValue, see that the value is invalid, and compute and set it. Since Implicit(V)
393 // is (intended to be) determinstic, they will compute the same value, so this doesn't cause an error,
394 // it just wastes a bit of computation time. Since it is common for multiple corners to be
395 // in the same grid-block, and locking is on the block level, it is (or was in some testing)
396 // better to not lock the entire block while Implicit(V) computed, at the cost of
397 // some wasted evals in some cases.
398
399 float CurrentValue = BlockedCornerValuesGrid.GetValueThreadSafe(Idx.X, Idx.Y, Idx.Z);
400 if (CurrentValue != FMathf::MaxReal)
401 {
402 return (double)CurrentValue;
403 }
404
406 CurrentValue = (float)Implicit(V);
407
408 BlockedCornerValuesGrid.SetValueThreadSafe(Idx.X, Idx.Y, Idx.Z, CurrentValue);
409
410 return (double)CurrentValue;
411 }
412 double corner_value_grid(const FVector3i& Idx)
413 {
415 {
416 return corner_value_grid_parallel(Idx);
417 }
418
419 float CurrentValue = BlockedCornerValuesGrid.GetValue(Idx.X, Idx.Y, Idx.Z);
420 if (CurrentValue != FMathf::MaxReal)
421 {
422 return (double)CurrentValue;
423 }
424
426 CurrentValue = (float)Implicit(V);
427
428 BlockedCornerValuesGrid.SetValue(Idx.X, Idx.Y, Idx.Z, CurrentValue);
429
430 return (double)CurrentValue;
431 }
432
434 {
435 if (Shift)
436 {
437 Cell.f[1] = corner_value_grid(Cell.i[1]);
438 Cell.f[2] = corner_value_grid(Cell.i[2]);
439 Cell.f[5] = corner_value_grid(Cell.i[5]);
440 Cell.f[6] = corner_value_grid(Cell.i[6]);
441 }
442 else
443 {
444 for (int i = 0; i < 8; ++i)
445 {
446 Cell.f[i] = corner_value_grid(Cell.i[i]);
447 }
448 }
449 }
450
451
452
453 //
454 // explicitly compute corner values as necessary
455 //
456 //
457
458 double corner_value_nohash(const FVector3i& Idx)
459 {
461 return Implicit(V);
462 }
464 {
465 if (Shift)
466 {
467 Cell.f[1] = corner_value_nohash(Cell.i[1]);
468 Cell.f[2] = corner_value_nohash(Cell.i[2]);
469 Cell.f[5] = corner_value_nohash(Cell.i[5]);
470 Cell.f[6] = corner_value_nohash(Cell.i[6]);
471 }
472 else
473 {
474 for (int i = 0; i < 8; ++i)
475 {
476 Cell.f[i] = corner_value_nohash(Cell.i[i]);
477 }
478 }
479 }
480
481
482
487 {
488 Cell.i[0] = FVector3i(Idx.X + 0, Idx.Y + 0, Idx.Z + 0);
489 Cell.i[1] = FVector3i(Idx.X + 1, Idx.Y + 0, Idx.Z + 0);
490 Cell.i[2] = FVector3i(Idx.X + 1, Idx.Y + 0, Idx.Z + 1);
491 Cell.i[3] = FVector3i(Idx.X + 0, Idx.Y + 0, Idx.Z + 1);
492 Cell.i[4] = FVector3i(Idx.X + 0, Idx.Y + 1, Idx.Z + 0);
493 Cell.i[5] = FVector3i(Idx.X + 1, Idx.Y + 1, Idx.Z + 0);
494 Cell.i[6] = FVector3i(Idx.X + 1, Idx.Y + 1, Idx.Z + 1);
495 Cell.i[7] = FVector3i(Idx.X + 0, Idx.Y + 1, Idx.Z + 1);
496
498 {
500 }
501 else
502 {
504 }
505 }
506
507
508 // assume we just want to slide cell at XIdx-1 to cell at XIdx, while keeping
509 // yi and ZIdx constant. Then only x-coords change, and we have already
510 // computed half the values
512 {
513 Cell.f[0] = Cell.f[1];
514 Cell.f[3] = Cell.f[2];
515 Cell.f[4] = Cell.f[5];
516 Cell.f[7] = Cell.f[6];
517
518 Cell.i[0].X = XIdx; Cell.i[1].X = XIdx+1; Cell.i[2].X = XIdx+1; Cell.i[3].X = XIdx;
519 Cell.i[4].X = XIdx; Cell.i[5].X = XIdx+1; Cell.i[6].X = XIdx+1; Cell.i[7].X = XIdx;
520
522 {
524 }
525 else
526 {
528 }
529 }
530
531
539
540
542
543
548 {
550
551 // [TODO] maybe shouldn't alway use Z axis here?
553 {
554 FGridCell Cell;
555 int vertTArray[12];
556 for (int yi = 0; yi < CellDimensions.Y; ++yi)
557 {
558 if (CancelF())
559 {
560 return;
561 }
562 // compute full cell at x=0, then slide along x row, which saves half of value computes
563 FVector3i Idx(0, yi, ZIdx);
564 initialize_cell(Cell, Idx);
565 polygonize_cell(Cell, vertTArray);
566 for (int XIdx = 1; XIdx < CellDimensions.X; ++XIdx)
567 {
568 shift_cell_x(Cell, XIdx);
569 polygonize_cell(Cell, vertTArray);
570 }
571 }
572 });
573
574
575 parallel_mesh_access = false;
576 }
577
578
579
580
585 {
587 int vertTArray[12];
588
589 for (int ZIdx = 0; ZIdx < CellDimensions.Z; ++ZIdx)
590 {
591 for (int yi = 0; yi < CellDimensions.Y; ++yi)
592 {
593 if (CancelF())
594 {
595 return;
596 }
597 // compute full Cell at x=0, then slide along x row, which saves half of value computes
598 FVector3i Idx(0, yi, ZIdx);
599 initialize_cell(Cell, Idx);
600 polygonize_cell(Cell, vertTArray);
601 for (int XIdx = 1; XIdx < CellDimensions.X; ++XIdx)
602 {
603 shift_cell_x(Cell, XIdx);
604 polygonize_cell(Cell, vertTArray);
605 }
606
607 }
608 }
609 }
610
611
612
613
618 {
620 int vertTArray[12];
621
622 BlockedDoneCells.Reset(CellDimensions.X, CellDimensions.Y, CellDimensions.Z, 0);
623
624 TArray<FVector3i> stack;
625
626 for (FVector3d seed : Seeds)
627 {
628 FVector3i seed_idx = cell_index(seed);
629 if (!BlockedDoneCells.IsValidIndex(seed_idx) || BlockedDoneCells.GetValue(seed_idx.X, seed_idx.Y, seed_idx.Z) == 1)
630 {
631 continue;
632 }
633 stack.Add(seed_idx);
634 BlockedDoneCells.SetValue(seed_idx.X, seed_idx.Y, seed_idx.Z, 1);
635
636 while ( stack.Num() > 0 )
637 {
638 FVector3i Idx = stack[stack.Num()-1];
639 stack.RemoveAt(stack.Num()-1);
640 if (CancelF())
641 {
642 return;
643 }
644
645 initialize_cell(Cell, Idx);
646 if ( polygonize_cell(Cell, vertTArray) )
647 { // found crossing
649 {
650 FVector3i nbr_idx = Idx + o;
651 if (GridBounds.Contains(nbr_idx) && BlockedDoneCells.GetValue(nbr_idx.X, nbr_idx.Y, nbr_idx.Z) == 0)
652 {
653 stack.Add(nbr_idx);
654 BlockedDoneCells.SetValue(nbr_idx.X, nbr_idx.Y, nbr_idx.Z, 1);
655 }
656 }
657 }
658 }
659 }
660 }
661
662
663
664
669 {
670 // Parallel marching cubes based on continuation (ie surface-following / front propagation)
671 // can have quite poor multithreaded performance depending on the ordering of the region-growing.
672 // For example processing each seed point in parallel can result in one thread that
673 // takes significantly longer than others, if the seed point distribution is such
674 // that a large part of the surface is only reachable from one seed (or gets
675 // "cut off" by a thin area, etc). So we want to basically do front-marching in
676 // parallel passes. However this can result in a large number of very short passes
677 // if the front ends up with many small regions, etc. So, in the implementation below,
678 // each "seed cell" is allowed to process up to N neighbour cells before terminating, at
679 // which point any cells remaining on the active front are added as seed cells for the next pass.
680 // This seems to provide good utilization, however more profiling may be needed.
681 // (In particular, if the active cell list is large, some blocks of the ParallelFor
682 // may still end up doing much more work than others)
683
684
685 // set this flag so that append vertex/triangle operations will lock the mesh
686 parallel_mesh_access = true;
687
688 // maximum number of cells to process in each ParallelFor iteration
689 static constexpr int MaxNeighboursPerActiveCell = 100;
690
691 // list of active cells on the MC front to process in the next pass
693
694 // initially push list of seed-point cells onto the ActiveCells list
695 for (FVector3d Seed : Seeds)
696 {
697 FVector3i seed_idx = cell_index(Seed);
698 if ( BlockedDoneCells.IsValidIndex(seed_idx) && set_cell_if_not_done(seed_idx) )
699 {
701 }
702 }
703
704 // new active cells will be accumulated in each parallel-pass
707
708 while (ActiveCells.Num() > 0)
709 {
710 // process all active cells
711 ParallelFor(ActiveCells.Num(), [&](int32 Idx)
712 {
713 FVector3i InitialCellIndex = ActiveCells[Idx];
714 if (CancelF())
715 {
716 return;
717 }
718
720 int TempArray[12];
721
722 // we will process up to MaxNeighboursPerActiveCell new cells in each ParallelFor iteration
726
728 {
730
731 initialize_cell(TempCell, CellIndex);
732 if (polygonize_cell(TempCell, TempArray))
733 {
734 // found crossing
735 for (FVector3i GridOffset : IndexUtil::GridOffsets6)
736 {
737 FVector3i NbrCellIndex = CellIndex + GridOffset;
738 if (GridBounds.Contains(NbrCellIndex))
739 {
740 if (set_cell_if_not_done(NbrCellIndex) == true)
741 {
743 }
744 }
745 }
746 }
747 }
748
749 // if stack is not empty, ie hit MaxNeighboursPerActiveCell, add remaining cells to next-pass Active list
750 if (LocalStack.Num() > 0)
751 {
752 NewActiveCellsLock.Lock();
754 NewActiveCellsLock.Unlock();
755 }
756
757 });
758
760 if (NewActiveCells.Num() > 0)
761 {
763 }
764 }
765
766 parallel_mesh_access = false;
767 }
768
769
771
773 {
774 bool was_set = false;
775 {
776 BlockedDoneCells.ProcessValueThreadSafe(Idx.X, Idx.Y, Idx.Z, [&](int& CellValue)
777 {
778 if (CellValue == 0)
779 {
780 CellValue = 1;
781 was_set = true;
782 }
783 });
784 }
785 return was_set;
786 }
787
788
789
790
791
796 {
797 // construct bits of index into edge table, where bit for each
798 // corner is 1 if that value is < isovalue.
799 // This tell us which edges have sign-crossings, and the int value
800 // of the bitmap is an index into the edge and triangle tables
801 int cubeindex = 0, Shift = 1;
802 for (int i = 0; i < 8; ++i)
803 {
804 if (Cell.f[i] < IsoValue)
805 {
806 cubeindex |= Shift;
807 }
808 Shift <<= 1;
809 }
810
811 // no crossings!
812 if (EdgeTable[cubeindex] == 0)
813 {
814 return false;
815 }
816
817 // check each bit of value in edge table. If it is 1, we
818 // have a crossing on that edge. Look up the indices of this
819 // edge and find the intersection point along it
820 Shift = 1;
822 for (int i = 0; i <= 11; i++)
823 {
824 if ((EdgeTable[cubeindex] & Shift) != 0)
825 {
826 int a = EdgeIndices[i][0], b = EdgeIndices[i][1];
827 VertIndexArray[i] = edge_vertex_id(Cell.i[a], Cell.i[b], Cell.f[a], Cell.f[b]);
828 }
829 Shift <<= 1;
830 }
831
832 int64 CellHash = corner_hash(Cell.i[0]);
833
834 // now iterate through the set of triangles in TriTable for this cube,
835 // and emit triangles using the vertices we found.
836 int tri_count = 0;
837 for (uint64 tris = TriTable[cubeindex]; tris != 0; tris >>= 12)
838 {
839 int ta = int(tris & 0xf);
840 int tb = int((tris >> 4) & 0xf);
841 int tc = int((tris >> 8) & 0xf);
842 int a = VertIndexArray[ta], b = VertIndexArray[tb], c = VertIndexArray[tc];
843
844 // if a corner is within tolerance of isovalue, then some triangles
845 // will be degenerate, and we can skip them w/o resulting in cracks (right?)
846 // !! this should never happen anymore...artifact of old hashtable impl
847 if (!ensure(a != b && a != c && b != c))
848 {
849 continue;
850 }
851
852 append_triangle(a, b, c, CellHash);
853 tri_count++;
854 }
855
856 return (tri_count > 0);
857 }
858
859
865 std::atomic<int32> VertexCounter;
866
867 int64 NumVertexSections = 64;
871 {
872 return (int32)(hash % (NumVertexSections - 1));
873 }
874
879 {
880 int SectionIndex = GetVertexSectionIndex(CellHash);
881 int32 NewIndex = VertexCounter++;
882
883 if (parallel_mesh_access)
884 {
885 FScopeLock Lock(&VertexSectionLocks[SectionIndex]);
886 VertexSectionLists[SectionIndex].Add(FIndexedVertex{ NewIndex, V });
887 }
888 else
889 {
890 VertexSectionLists[SectionIndex].Add(FIndexedVertex{ NewIndex, V });
891 }
892
893 return NewIndex;
894 }
895
896
897 int64 NumTriangleSections = 64;
901 {
902 return (int32)(hash % (NumTriangleSections - 1));
903 }
904
908 void append_triangle(int A, int B, int C, int64 CellHash)
909 {
910 int SectionIndex = GetTriangleSectionIndex(CellHash);
911 if (parallel_mesh_access)
912 {
913 FScopeLock Lock(&TriangleSectionLocks[SectionIndex]);
914 TriangleSectionLists[SectionIndex].Add(FIndex3i(A, B, C));
915 }
916 else
917 {
918 TriangleSectionLists[SectionIndex].Add(FIndex3i(A, B, C));
919 }
920 }
921
922
927 {
928 VertexSectionLocks.SetNum((int32)NumVertexSections);
929 VertexSectionLists.Reset();
930 VertexSectionLists.SetNum((int32)NumVertexSections);
931 VertexCounter = 0;
932
933 TriangleSectionLocks.SetNum((int32)NumTriangleSections);
934 TriangleSectionLists.Reset();
935 TriangleSectionLists.SetNum((int32)NumTriangleSections);
936 }
937
943 {
945
946 int32 NumVertices = VertexCounter;
948 VertexBuffer.SetNum(NumVertices);
949 for (const TArray<FIndexedVertex>& VertexList : VertexSectionLists)
950 {
952 {
953 VertexBuffer[Vtx.Index] = Vtx.Position;
954 }
955 }
956 for (int32 k = 0; k < NumVertices; ++k)
957 {
958 int32 vid = AppendVertex(VertexBuffer[k]);
959 }
960
961 for (const TArray<FIndex3i>& TriangleList : TriangleSectionLists)
962 {
963 for (FIndex3i Tri : TriangleList)
964 {
965 AppendTriangle(Tri.A, Tri.B, Tri.C);
966 }
967 }
968 }
969
970
971
975 void find_iso(const TVector<double>& P1, const TVector<double>& P2, double ValP1, double ValP2, TVector<double>& PIso)
976 {
977 // Ok, this is a bit hacky but seems to work? If both isovalues
978 // are the same, we just return the midpoint. If one is nearly zero, we can
979 // but assume that's where the surface is. *However* if we return that point exactly,
980 // we can get nonmanifold vertices, because multiple fans may connect there.
981 // Since FDynamicMesh3 disallows that, it results in holes. So we pull
982 // slightly towards the other point along this edge. This means we will get
983 // repeated nearly-coincident vertices, but the mesh will be manifold.
984 const double dt = 0.999999;
985 if (FMath::Abs(ValP1 - ValP2) < 0.00001)
986 {
987 PIso = (P1 + P2) * 0.5;
988 return;
989 }
990 if (FMath::Abs(IsoValue - ValP1) < 0.00001)
991 {
992 PIso = dt * P1 + (1.0 - dt) * P2;
993 return;
994 }
995 if (FMath::Abs(IsoValue - ValP2) < 0.00001)
996 {
997 PIso = (dt) * P2 + (1.0 - dt) * P1;
998 return;
999 }
1000
1001 // Note: if we don't maintain min/max order here, then numerical error means
1002 // that hashing on point x/y/z doesn't work
1003 TVector<double> a = P1, b = P2;
1004 double fa = ValP1, fb = ValP2;
1005 if (ValP2 < ValP1)
1006 {
1007 a = P2; b = P1;
1008 fb = ValP1; fa = ValP2;
1009 }
1010
1011 // converge on root
1012 if (RootMode == ERootfindingModes::Bisection)
1013 {
1014 for (int k = 0; k < RootModeSteps; ++k)
1015 {
1016 PIso.X = (a.X + b.X) * 0.5; PIso.Y = (a.Y + b.Y) * 0.5; PIso.Z = (a.Z + b.Z) * 0.5;
1017 double mid_f = Implicit(PIso);
1018 if (mid_f < IsoValue)
1019 {
1020 a = PIso; fa = mid_f;
1021 }
1022 else
1023 {
1024 b = PIso; fb = mid_f;
1025 }
1026 }
1027 PIso = Lerp(a, b, 0.5);
1028
1029 }
1030 else
1031 {
1032 double mu = 0;
1033 if (RootMode == ERootfindingModes::LerpSteps)
1034 {
1035 for (int k = 0; k < RootModeSteps; ++k)
1036 {
1037 mu = FMathd::Clamp((IsoValue - fa) / (fb - fa), 0.0, 1.0);
1038 PIso.X = a.X + mu * (b.X - a.X);
1039 PIso.Y = a.Y + mu * (b.Y - a.Y);
1040 PIso.Z = a.Z + mu * (b.Z - a.Z);
1041 double mid_f = Implicit(PIso);
1042 if (mid_f < IsoValue)
1043 {
1044 a = PIso; fa = mid_f;
1045 }
1046 else
1047 {
1048 b = PIso; fb = mid_f;
1049 }
1050 }
1051 }
1052
1053 // final lerp
1054 mu = FMathd::Clamp((IsoValue - fa) / (fb - fa), 0.0, 1.0);
1055 PIso.X = a.X + mu * (b.X - a.X);
1056 PIso.Y = a.Y + mu * (b.Y - a.Y);
1057 PIso.Z = a.Z + mu * (b.Z - a.Z);
1058 }
1059 }
1060
1061
1062
1063
1064 /*
1065 * Below here are standard marching-cubes tables.
1066 */
1067
1068 static GEOMETRYCORE_API const uint8 EdgeIndices[12][2];
1069 static GEOMETRYCORE_API const uint16 EdgeTable[256];
1070 static GEOMETRYCORE_API const uint64 TriTable[256];
1071};
1072
1073
1074} // end namespace UE::Geometry
1075} // end namespace UE
#define ensure( InExpression)
Definition AssertionMacros.h:464
void ParallelFor(int32 Num, TFunctionRef< void(int32)> Body, bool bForceSingleThread, bool bPumpRenderingThread=false)
Definition ParallelFor.h:481
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
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
#define X(Name, Desc)
Definition FormatStringSan.h:47
const bool
Definition NetworkReplayStreaming.h:178
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint8_t uint8
Definition binka_ue_file_header.h:8
uint16_t uint16
Definition binka_ue_file_header.h:7
Definition ScopeLock.h:141
Definition ArrayView.h:139
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void RemoveAt(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2083
void Reset(SizeType NewSize=0)
Definition Array.h:2246
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_NODEBUG UE_FORCEINLINE_HINT bool Find(const ElementType &Item, SizeType &Index) const
Definition Array.h:1302
Definition AndroidPlatformMisc.h:14
static RealType Clamp(const RealType Value, const RealType ClampMin, const RealType ClampMax)
Definition MathUtil.h:222
Definition MarchingCubes.h:51
TArray< FCriticalSection > EdgeVertexSectionLocks
Definition MarchingCubes.h:324
int GetVertexSectionIndex(int64 hash)
Definition MarchingCubes.h:870
FVector3i cell_index(const TVector< double > &Pos)
Definition MarchingCubes.h:268
TArray< TArray< FIndexedVertex > > VertexSectionLists
Definition MarchingCubes.h:869
FBlockedDenseGrid3f BlockedCornerValuesGrid
Definition MarchingCubes.h:387
double corner_value_grid(const FVector3i &Idx)
Definition MarchingCubes.h:412
void ResetMesh()
Definition MarchingCubes.h:926
void corner_pos(const FVector3i &IJK, TVector< double > &Pos)
Definition MarchingCubes.h:256
void generate_basic()
Definition MarchingCubes.h:584
int64 edge_hash(const FVector3i &Idx1, const FVector3i &Idx2)
Definition MarchingCubes.h:297
int SafetyMaxDimension
Definition MarchingCubes.h:92
const int64 EDGE_Z
Definition MarchingCubes.h:295
TFunction< bool(void)> CancelF
Definition MarchingCubes.h:106
const int64 NumEdgeVertexSections
Definition MarchingCubes.h:322
void generate_parallel()
Definition MarchingCubes.h:547
void InitHashTables()
Definition MarchingCubes.h:532
double CubeSize
Definition MarchingCubes.h:74
virtual ~FMarchingCubes()
Definition MarchingCubes.h:122
FAxisAlignedBox3i GridBounds
Definition MarchingCubes.h:228
FMeshShapeGenerator & Generate() override
Definition MarchingCubes.h:134
double IsoValue
Definition MarchingCubes.h:62
int edge_vertex_id(const FVector3i &Idx1, const FVector3i &Idx2, double F1, double F2)
Definition MarchingCubes.h:349
ERootfindingModes RootMode
Definition MarchingCubes.h:97
void find_iso(const TVector< double > &P1, const TVector< double > &P2, double ValP1, double ValP2, TVector< double > &PIso)
Definition MarchingCubes.h:975
double corner_value_grid_parallel(const FVector3i &Idx)
Definition MarchingCubes.h:389
bool bParallelCompute
Definition MarchingCubes.h:80
void shift_cell_x(FGridCell &Cell, int XIdx)
Definition MarchingCubes.h:511
TAxisAlignedBox3< double > Bounds
Definition MarchingCubes.h:68
int GetTriangleSectionIndex(int64 hash)
Definition MarchingCubes.h:900
void initialize_cell(FGridCell &Cell, const FVector3i &Idx)
Definition MarchingCubes.h:486
FMarchingCubes()
Definition MarchingCubes.h:116
std::atomic< int32 > VertexCounter
Definition MarchingCubes.h:865
void SetDimensions()
Definition MarchingCubes.h:240
int FindVertexID(int64 hash)
Definition MarchingCubes.h:326
TFunction< double(TVector< double >)> Implicit
Definition MarchingCubes.h:56
FAxisAlignedBox3i LastGridBounds
Definition MarchingCubes.h:229
void BuildMesh()
Definition MarchingCubes.h:942
FVector3i CellDimensions
Definition MarchingCubes.h:113
TArray< TMap< int64, int > > EdgeVertexSections
Definition MarchingCubes.h:323
bool Validate()
Definition MarchingCubes.h:126
int64 corner_hash(const FVector3i &Idx)
Definition MarchingCubes.h:284
void append_triangle(int A, int B, int C, int64 CellHash)
Definition MarchingCubes.h:908
bool set_cell_if_not_done(const FVector3i &Idx)
Definition MarchingCubes.h:772
int64 corner_hash(int X, int Y, int Z)
Definition MarchingCubes.h:288
bool polygonize_cell(FGridCell &Cell, int VertIndexArray[])
Definition MarchingCubes.h:795
int AppendOrFindVertexID(int64 hash, TVector< double > Pos)
Definition MarchingCubes.h:334
void initialize_cell_values_grid(FGridCell &Cell, bool Shift)
Definition MarchingCubes.h:433
int append_vertex(TVector< double > V, int64 CellHash)
Definition MarchingCubes.h:878
FBlockedDenseGrid3i BlockedDoneCells
Definition MarchingCubes.h:770
bool parallel_mesh_access
Definition MarchingCubes.h:541
TVector< double > corner_pos(const FVector3i &IJK)
Definition MarchingCubes.h:262
const int64 EDGE_Y
Definition MarchingCubes.h:294
TArray< TArray< FIndex3i > > TriangleSectionLists
Definition MarchingCubes.h:899
bool bEnableValueCaching
Definition MarchingCubes.h:87
void initialize_cell_values_nohash(FGridCell &Cell, bool Shift)
Definition MarchingCubes.h:463
void generate_continuation_parallel(TArrayView< const FVector3d > Seeds)
Definition MarchingCubes.h:668
FMeshShapeGenerator & GenerateContinuation(TArrayView< const FVector3d > Seeds)
Definition MarchingCubes.h:169
int RootModeSteps
Definition MarchingCubes.h:102
const int64 EDGE_X
Definition MarchingCubes.h:293
void generate_continuation(TArrayView< const FVector3d > Seeds)
Definition MarchingCubes.h:617
TArray< FCriticalSection > VertexSectionLocks
Definition MarchingCubes.h:868
double corner_value_nohash(const FVector3i &Idx)
Definition MarchingCubes.h:458
TArray< FCriticalSection > TriangleSectionLocks
Definition MarchingCubes.h:898
Definition MeshShapeGenerator.h:19
void SetValue(int32 I, int32 J, int32 K, ElemType NewValue)
Definition BlockedDenseGrid3.h:336
ElemType GetValue(int32 I, int32 J, int32 K) const
Definition BlockedDenseGrid3.h:319
void Resize(int32 DimI, int32 DimJ, int32 DimK)
Definition BlockedDenseGrid3.h:795
void Reset()
Definition BlockedDenseGrid3.h:807
void SetValueThreadSafe(int32 I, int32 J, int32 K, ElemType NewValue)
Definition BlockedDenseGrid3.h:848
ElemType GetValueThreadSafe(int32 I, int32 J, int32 K)
Definition BlockedDenseGrid3.h:832
void ProcessValueThreadSafe(int32 I, int32 J, int32 K, ProcessFunc Func)
Definition BlockedDenseGrid3.h:869
constexpr int InvalidID
Definition IndexTypes.h:13
GEOMETRYCORE_API const FVector3i GridOffsets6[6]
Definition IndexUtil.cpp:24
ERootfindingModes
Definition MarchingCubes.h:44
Definition Sphere.cpp:10
Definition AdvancedWidgetsModule.cpp:13
static constexpr UE_FORCEINLINE_HINT T Max3(const T A, const T B, const T C)
Definition UnrealMathUtility.h:551
Definition IntBoxTypes.h:184
Definition IndexTypes.h:158
Definition MarchingCubes.h:234
double f[8]
Definition MarchingCubes.h:237
FVector3i i[8]
Definition MarchingCubes.h:236
Definition MarchingCubes.h:861
FVector3d Position
Definition MarchingCubes.h:863
int32 Index
Definition MarchingCubes.h:862
Definition IntVectorTypes.h:252
static FVector3i Zero()
Definition IntVectorTypes.h:326
int32 Z
Definition IntVectorTypes.h:257
int32 X
Definition IntVectorTypes.h:257
int32 Y
Definition IntVectorTypes.h:257
Definition BoxTypes.h:247
RealType Depth() const
Definition BoxTypes.h:578
RealType Width() const
Definition BoxTypes.h:568
bool IsEmpty() const
Definition BoxTypes.h:613
RealType MaxDim() const
Definition BoxTypes.h:598
RealType Height() const
Definition BoxTypes.h:573
TVector< RealType > Min
Definition BoxTypes.h:248
Definition Vector.h:51
T Z
Definition Vector.h:68
static TVector< T > Zero()
Definition Vector.h:112
T Y
Definition Vector.h:65
T X
Definition Vector.h:62