wip(assembler): M2 round 4 -- WorldAssemblerHybridV2: exact contacts, tagged tiles 1.18-1.27x MC
The round-3 'field contact error' measured hit-to-anchor distance; V2 contacts are 0 cm and on the zero isosurface. Tagged-tile cost 5.7-6.7x -> 1.175-1.270x MC; organic 0.998x. Bound, shelf-thickness and route audits rerun with negative controls; W1/W8/W16 stable; V1 and MarchingCubesV1 unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
22a6b30494
commit
4661a445be
48 files changed
+106493
-71
No files matched your search
@@ -8,6 +8,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -42,6 +43,11 @@ namespace VoxelHybridSharpMesher
|
||||
else V.Z = Value;
|
||||
}
|
||||
|
||||
inline int32 IntAxisValue(const FIntVector& V, int32 Axis)
|
||||
{
|
||||
return Axis == 0 ? V.X : (Axis == 1 ? V.Y : V.Z);
|
||||
}
|
||||
|
||||
struct FPlane
|
||||
{
|
||||
uint64 Id = 0;
|
||||
@@ -223,6 +229,106 @@ namespace VoxelHybridSharpMesher
|
||||
return SDF;
|
||||
}
|
||||
|
||||
struct FPreparedFeatureBox
|
||||
{
|
||||
uint64 StableId = 0;
|
||||
double Minimum[3]{};
|
||||
double Maximum[3]{};
|
||||
};
|
||||
|
||||
inline std::vector<FPreparedFeatureBox> PrepareFeatureBoxes(
|
||||
const TArray<FVoxelHybridSharpFeatureBox>& Features)
|
||||
{
|
||||
std::vector<FPreparedFeatureBox> Prepared;
|
||||
Prepared.reserve(static_cast<std::size_t>(Features.Num()));
|
||||
for (const FVoxelHybridSharpFeatureBox& Feature : Features)
|
||||
{
|
||||
FPreparedFeatureBox Box;
|
||||
Box.StableId = Feature.StableId;
|
||||
for (int32 Axis = 0; Axis < 3; ++Axis)
|
||||
{
|
||||
Box.Minimum[Axis] = static_cast<double>(Feature.MinimumSubvoxels[Axis])
|
||||
/ SubvoxelsPerVoxel;
|
||||
Box.Maximum[Axis] = static_cast<double>(Feature.MaximumSubvoxels[Axis])
|
||||
/ SubvoxelsPerVoxel;
|
||||
}
|
||||
Prepared.push_back(Box);
|
||||
}
|
||||
return Prepared;
|
||||
}
|
||||
|
||||
// MC interpolation can move a finite hard box's zero crossing away from its analytic face.
|
||||
// V2 retains MC connectivity and snaps only interior tile roots that cross a tagged face.
|
||||
inline bool TrySnapFeatureEdge(const std::vector<FPreparedFeatureBox>& Features,
|
||||
const FIntVector& OriginVoxels, const FIntVector& LocalStart, int32 Axis,
|
||||
const FVector& ChunkWorldPos, float GeometryVoxelSize,
|
||||
FVector& InOutWorldPosition, FVector& OutNormal, uint64& OutSplitKey)
|
||||
{
|
||||
const double EdgeStart = static_cast<double>(IntAxisValue(OriginVoxels, Axis)
|
||||
+ IntAxisValue(LocalStart, Axis));
|
||||
const double EdgeEnd = EdgeStart + 1.0;
|
||||
const double EdgeLow = FMath::Min(EdgeStart, EdgeEnd);
|
||||
const double EdgeHigh = FMath::Max(EdgeStart, EdgeEnd);
|
||||
const double RootVoxel = static_cast<double>(IntAxisValue(OriginVoxels, Axis))
|
||||
+ (AxisValue(InOutWorldPosition, Axis) - AxisValue(ChunkWorldPos, Axis))
|
||||
/ GeometryVoxelSize;
|
||||
constexpr double Tolerance = 1.0e-9;
|
||||
double BestDistance = std::numeric_limits<double>::infinity();
|
||||
uint64 BestStableId = std::numeric_limits<uint64>::max();
|
||||
uint64 BestFaceId = 0;
|
||||
double BestPlane = 0.0;
|
||||
int32 BestSide = 0;
|
||||
|
||||
for (const FPreparedFeatureBox& Feature : Features)
|
||||
{
|
||||
bool bWithinFaceSpan = true;
|
||||
for (int32 OtherAxis = 0; OtherAxis < 3; ++OtherAxis)
|
||||
{
|
||||
if (OtherAxis == Axis) continue;
|
||||
const double Coordinate = static_cast<double>(IntAxisValue(OriginVoxels, OtherAxis)
|
||||
+ IntAxisValue(LocalStart, OtherAxis));
|
||||
if (Coordinate < Feature.Minimum[OtherAxis] - Tolerance
|
||||
|| Coordinate > Feature.Maximum[OtherAxis] + Tolerance)
|
||||
{
|
||||
bWithinFaceSpan = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bWithinFaceSpan) continue;
|
||||
|
||||
for (int32 Side = 0; Side < 2; ++Side)
|
||||
{
|
||||
const double Plane = Side == 0
|
||||
? Feature.Minimum[Axis] : Feature.Maximum[Axis];
|
||||
if (Plane < EdgeLow - Tolerance || Plane > EdgeHigh + Tolerance) continue;
|
||||
const double Distance = FMath::Abs(Plane - RootVoxel);
|
||||
const uint64 FaceId = Feature.StableId ^ (0x9e3779b97f4a7c15ull
|
||||
+ static_cast<uint64>(Axis * 2 + Side + 1));
|
||||
if (Distance > BestDistance + Tolerance
|
||||
|| (FMath::Abs(Distance - BestDistance) <= Tolerance
|
||||
&& std::tie(Feature.StableId, FaceId)
|
||||
>= std::tie(BestStableId, BestFaceId)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BestDistance = Distance;
|
||||
BestStableId = Feature.StableId;
|
||||
BestFaceId = FaceId != 0 ? FaceId : static_cast<uint64>(Axis + 1);
|
||||
BestPlane = Plane;
|
||||
BestSide = Side;
|
||||
}
|
||||
}
|
||||
|
||||
if (BestFaceId == 0) return false;
|
||||
SetAxisValue(InOutWorldPosition, Axis, AxisValue(ChunkWorldPos, Axis)
|
||||
+ (BestPlane - static_cast<double>(IntAxisValue(OriginVoxels, Axis)))
|
||||
* GeometryVoxelSize);
|
||||
OutNormal = FVector::ZeroVector;
|
||||
SetAxisValue(OutNormal, Axis, BestSide == 0 ? -1.0 : 1.0);
|
||||
OutSplitKey = BestFaceId;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint64 SelectFeature(const FRoot& Root, int32 Axis)
|
||||
{
|
||||
if (Root.Planes.empty()) return 0;
|
||||
|
||||
@@ -1371,8 +1371,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// tile edge use MC there, preserving the existing MC-to-MC clipmap transition and skirts.
|
||||
// Le DC reste limite aux surfaces LOD0 balisees; les patches connectes au bord restent MC.
|
||||
VoxelHybridSharpMesher::FResult HybridResult;
|
||||
if (bUseHybridSharpMesher && Step == 1 && CellsPerAxis == CHUNK_SIZE
|
||||
&& CzLo == 0 && CzHi == CellsPerAxis - 1 && !HybridSharpFeatures.IsEmpty())
|
||||
const bool bHybridLod0Interior = Step == 1 && CellsPerAxis == CHUNK_SIZE
|
||||
&& CzLo == 0 && CzHi == CellsPerAxis - 1 && !HybridSharpFeatures.IsEmpty();
|
||||
if (bUseHybridSharpMesher && bHybridLod0Interior)
|
||||
{
|
||||
HybridResult = VoxelHybridSharpMesher::Generate(
|
||||
CellsPerAxis, Step, IsoLevel, GeometryVoxelSize, OriginVoxels, ChunkWorldPos,
|
||||
@@ -1380,6 +1381,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
[&](int32 X, int32 Y, int32 Z) { return SampleG(X, Y, Z); },
|
||||
[&](int32 X, int32 Y, int32 Z) { return GradAt(X, Y, Z); });
|
||||
}
|
||||
const bool bHybridSharpSnap = bUseHybridSharpFeatureSnap && bHybridLod0Interior;
|
||||
const std::vector<VoxelHybridSharpMesher::FPreparedFeatureBox> PreparedSharpFeatures =
|
||||
bHybridSharpSnap
|
||||
? VoxelHybridSharpMesher::PrepareFeatureBoxes(HybridSharpFeatures)
|
||||
: std::vector<VoxelHybridSharpMesher::FPreparedFeatureBox>{};
|
||||
|
||||
//=========================================================================
|
||||
// ITÉRATION SUR LES CELLULES
|
||||
@@ -1543,6 +1549,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
? FPlatformTime::Cycles64() : 0;
|
||||
FVector EdgeVertices[12];
|
||||
FVector EdgeNormals[12];
|
||||
uint64 EdgeSplitKeys[12]{};
|
||||
for (int32 i = 0; i < 12; i++)
|
||||
{
|
||||
if (MCEdgeTable[CaseIndex] & (1 << i))
|
||||
@@ -1578,6 +1585,28 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
EdgeVertices[i] = Positions[A] + T * (Positions[B] - Positions[A]);
|
||||
EdgeNormals[i] = Gradients[A] + T * (Gradients[B] - Gradients[A]);
|
||||
if (bHybridSharpSnap)
|
||||
{
|
||||
const int32 Axis = CornerOffsets[A].X != CornerOffsets[B].X ? 0
|
||||
: (CornerOffsets[A].Y != CornerOffsets[B].Y ? 1 : 2);
|
||||
const FIntVector LocalStart(cx + CornerOffsets[A].X,
|
||||
cy + CornerOffsets[A].Y, cz + CornerOffsets[A].Z);
|
||||
// All cells incident to this lattice edge must stay interior so
|
||||
// the shared root cannot disagree across a tile-border cell.
|
||||
const bool bCanSnapEdge = bHybridSharpSnap
|
||||
&& LocalStart.X >= 2 && LocalStart.Y >= 2 && LocalStart.Z >= 2
|
||||
&& LocalStart.X <= CellsPerAxis - 2
|
||||
&& LocalStart.Y <= CellsPerAxis - 2
|
||||
&& LocalStart.Z <= CellsPerAxis - 2;
|
||||
FVector AnalyticNormal;
|
||||
if (bCanSnapEdge && VoxelHybridSharpMesher::TrySnapFeatureEdge(
|
||||
PreparedSharpFeatures, OriginVoxels, LocalStart, Axis,
|
||||
ChunkWorldPos, GeometryVoxelSize, EdgeVertices[i],
|
||||
AnalyticNormal, EdgeSplitKeys[i]))
|
||||
{
|
||||
EdgeNormals[i] = AnalyticNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bSampleCell)
|
||||
@@ -1600,9 +1629,12 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int32 Idx0 = GetOrCreateVertex(EdgeVertices[E0], EdgeNormals[E0]);
|
||||
const int32 Idx1 = GetOrCreateVertex(EdgeVertices[E1], EdgeNormals[E1]);
|
||||
const int32 Idx2 = GetOrCreateVertex(EdgeVertices[E2], EdgeNormals[E2]);
|
||||
const int32 Idx0 = GetOrCreateVertex(
|
||||
EdgeVertices[E0], EdgeNormals[E0], EdgeSplitKeys[E0]);
|
||||
const int32 Idx1 = GetOrCreateVertex(
|
||||
EdgeVertices[E1], EdgeNormals[E1], EdgeSplitKeys[E1]);
|
||||
const int32 Idx2 = GetOrCreateVertex(
|
||||
EdgeVertices[E2], EdgeNormals[E2], EdgeSplitKeys[E2]);
|
||||
|
||||
TArray<int32>* Dst = &GroundTris;
|
||||
if (!bStandaloneSkipMaterialGroups)
|
||||
|
||||
@@ -128,6 +128,13 @@ public:
|
||||
bUseHybridSharpMesher = true;
|
||||
}
|
||||
|
||||
// Version 2 keeps MC topology and projects eligible LOD0 edge roots onto exact box planes.
|
||||
void SetHybridSharpFeatureSnap(TArray<FVoxelHybridSharpFeatureBox>&& InFeatures)
|
||||
{
|
||||
HybridSharpFeatures = MoveTemp(InFeatures);
|
||||
bUseHybridSharpFeatureSnap = true;
|
||||
}
|
||||
|
||||
private:
|
||||
FORCEINLINE bool ShouldAbortWork() const
|
||||
{
|
||||
@@ -139,6 +146,7 @@ private:
|
||||
const FStandaloneShapeWorld* ShapeWorldContext = nullptr;
|
||||
TArray<FVoxelHybridSharpFeatureBox> HybridSharpFeatures;
|
||||
bool bUseHybridSharpMesher = false;
|
||||
bool bUseHybridSharpFeatureSnap = false;
|
||||
float GeometryVoxelSizeCm = VOXEL_SIZE;
|
||||
|
||||
public:
|
||||
|
||||
Reference in new issue
Block a user