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:
Fr0zkaandClaude Opus 5.5 committed 2026-10-10 10:41:43 +02:00
1 parent 22a6b30494
commit 4661a445be
48 files changed
+106493 -71

No files matched your search

@@ -43,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;
@@ -229,6 +234,109 @@ 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 interpolates the sampled field. A finite hard box is only included by the field at
// samples inside it, so an edge can interpolate away from the box plane when the box face
// lies between lattice points. For V2, keep the MC connectivity and move only roots whose
// lattice edge crosses a tagged analytic face. This is deterministic and leaves tile edges
// to the unchanged MC/skirt path.
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;
@@ -1073,8 +1073,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
// Le DC reste limite aux surfaces LOD0 balisees; les patches connectes au bord restent MC.
VoxelHybridSharpMesher::FResult HybridResult;
MeshData.HybridFeatureCount = HybridSharpFeatures.Num();
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)
{
MeshData.bHybridMesherInvoked = true;
HybridResult = VoxelHybridSharpMesher::Generate(
@@ -1083,6 +1084,12 @@ 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>{};
if (bHybridSharpSnap) MeshData.bHybridMesherInvoked = true;
MeshData.HybridSharpCellCount = HybridResult.SharpCells;
MeshData.HybridDualCellCount = HybridResult.DualCells;
MeshData.HybridActiveEdgeRootCount = HybridResult.ActiveEdgeRoots;
@@ -1182,6 +1189,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))
@@ -1194,6 +1202,28 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
: FMath::Clamp((IsoLevel - D1) / (D2 - D1), 0.0f, 1.0f);
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);
// Every cell incident to this lattice edge must be interior so all
// copies of the shared root take the same exact or MC path.
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)
@@ -1216,9 +1246,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]);
// F17 — vote majoritaire (≥ 2 vertex sky-cap ⇒ triangle sky-cap).
const int32 CapVotes = (int32)VertexClasses[Idx0]
+18 -5
View File
@@ -11449,9 +11449,18 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
const VoxelForgeCore::TileKey CoreKey{{Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z}, Tile.Level};
VoxelForgeCore::TileGenerationOptions Options;
Options.mesherRecipe = CoreMesherRecipe == EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV1
? VoxelForgeCore::MesherRecipe::worldAssemblerHybridV1
: VoxelForgeCore::MesherRecipe::marchingCubesV1;
switch (CoreMesherRecipe)
{
case EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV1:
Options.mesherRecipe = VoxelForgeCore::MesherRecipe::worldAssemblerHybridV1;
break;
case EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV2:
Options.mesherRecipe = VoxelForgeCore::MesherRecipe::worldAssemblerHybridV2;
break;
default:
Options.mesherRecipe = VoxelForgeCore::MesherRecipe::marchingCubesV1;
break;
}
Options.hashDensity = bCaptureHashes;
std::string CoreError;
std::optional<VoxelForgeCore::TileMesh> CoreMesh;
@@ -13290,7 +13299,7 @@ FString AVoxelWorld::GetCurrentSeasonContentHash() const
{
return FString();
}
const bool bHybridMesher = CoreMesherRecipe == EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV1;
const bool bHybridMesher = CoreMesherRecipe != EVoxelForgeCoreMesherRecipe::MarchingCubesV1;
if (AssemblyHash.IsEmpty() && !bHybridMesher)
{
return SeasonHash;
@@ -13316,10 +13325,14 @@ FString AVoxelWorld::GetCurrentSeasonContentHash() const
Payload += TEXT("|world_assembly=");
Payload += AssemblyHash;
}
if (bHybridMesher)
if (CoreMesherRecipe == EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV1)
{
Payload += TEXT("|core_mesher=WorldAssemblerHybridV1");
}
else if (CoreMesherRecipe == EVoxelForgeCoreMesherRecipe::WorldAssemblerHybridV2)
{
Payload += TEXT("|core_mesher=WorldAssemblerHybridV2");
}
FTCHARToUTF8 Utf8Payload(*Payload);
uint8 Digest[FSHA1::DigestSize];
FSHA1::HashBuffer(Utf8Payload.Get(), Utf8Payload.Length(), Digest);
@@ -120,6 +120,15 @@ public:
bUseHybridSharpMesher = true;
}
// Version 2 keeps MC topology and projects eligible LOD0 edge roots onto the exact
// analytic box planes. It avoids the tile-wide dual-contouring reconstruction in V1.
// La version 2 conserve la topologie MC et projette les racines LOD0 sur les plans exacts.
void SetHybridSharpFeatureSnap(TArray<FVoxelHybridSharpFeatureBox>&& InFeatures)
{
HybridSharpFeatures = MoveTemp(InFeatures);
bUseHybridSharpFeatureSnap = true;
}
private:
FORCEINLINE bool ShouldAbortWork() const
{
@@ -130,6 +139,7 @@ private:
const FVoxelSharedDensityGrid* SharedDensityGrid = nullptr;
TArray<FVoxelHybridSharpFeatureBox> HybridSharpFeatures;
bool bUseHybridSharpMesher = false;
bool bUseHybridSharpFeatureSnap = false;
float GeometryVoxelSizeCm = VOXEL_SIZE;
public:
+2 -1
View File
@@ -78,7 +78,8 @@ UENUM(BlueprintType)
enum class EVoxelForgeCoreMesherRecipe : uint8
{
MarchingCubesV1 UMETA(DisplayName = "Marching Cubes V1"),
WorldAssemblerHybridV1 UMETA(DisplayName = "World Assembler Hybrid V1")
WorldAssemblerHybridV1 UMETA(DisplayName = "World Assembler Hybrid V1"),
WorldAssemblerHybridV2 UMETA(DisplayName = "World Assembler Hybrid V2")
};
/** A non-player streaming center: keep a small box of level-0 tiles loaded around Actor so it has