wip(assembler): M2 round 3 -- opt-in hybrid sharp mesher (WorldAssemblerHybridV1) in the Unreal Core route

Default MarchingCubesV1 unchanged (W1/W8/W16 = round-6 hashes). Hybrid DC only in LOD0 interior cells
with tagged features; tile-border cells and LOD1+ stay MC. BuildHost: fixtures repeat exactly, cooked
collision bounds = render bounds, same-LOD borders pass, sampled SurfaceWorld cliff bit-identical to MC.
Open: tagged tiles cost 5.7-6.7x MC; corner-top field contact error 20 cm; 5 MC doorway LOD cases.

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 09:53:04 +02:00
1 parent a3b7d2015e
commit 22a6b30494
21 files changed
+3009 -23

No files matched your search

@@ -2,6 +2,7 @@
// Implémentation du marching cubes density-only.
#include "VoxelMarchingCubesMesher.h"
#include "VoxelHybridSharpMesher.h"
#include "MarchingCubesTables.h"
#include "StandaloneShapeWorld.h"
#include "VoxelDensityProfile.h"
@@ -164,6 +165,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
// plus de (ré)allocation de hash-map par tuile (chaque worker a son propre exemplaire).
static thread_local TMap<FIntVector, int32> VertexMap;
VertexMap.Reset();
static thread_local TMap<FIntVector, TMap<uint64, int32>> SplitNormalVertexMap;
SplitNormalVertexMap.Reset();
const int32 VertexCapacityHint=FMath::Clamp(InCellsPerAxis,2,CHUNK_SIZE);
VertexMap.Reserve(VertexCapacityHint*VertexCapacityHint*2);
@@ -230,7 +233,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
};
// Normale fournie par l'appelant (gradient lu dans la grille de densité, T1.b).
auto GetOrCreateVertex = [&](const FVector& WorldPos, const FVector& RawNormal) -> int32
auto GetOrCreateVertex = [&](const FVector& WorldPos, const FVector& RawNormal,
uint64 NormalSplitKey = 0) -> int32
{
const FIntVector Key(
FMath::RoundToInt(WorldPos.X * 100.0f),
@@ -238,13 +242,27 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
FMath::RoundToInt(WorldPos.Z * 100.0f)
);
if (int32* Existing = VertexMap.Find(Key))
if (NormalSplitKey != 0)
{
if (TMap<uint64, int32>* SplitMap = SplitNormalVertexMap.Find(Key))
{
if (int32* Existing = SplitMap->Find(NormalSplitKey)) return *Existing;
}
}
else if (int32* Existing = VertexMap.Find(Key))
{
return *Existing;
}
const int32 NewIndex = MeshData.Vertices.Num();
VertexMap.Add(Key, NewIndex);
if (NormalSplitKey != 0)
{
SplitNormalVertexMap.FindOrAdd(Key).Add(NormalSplitKey, NewIndex);
}
else
{
VertexMap.Add(Key, NewIndex);
}
MeshData.Vertices.Add(WorldPos);
@@ -1349,6 +1367,20 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
SampleGradientG(gx, gy, gz + 1) - SampleGradientG(gx, gy, gz - 1));
};
// DC is limited to tagged LOD0 patches. Coarse tiles and a connected patch that reaches a
// 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())
{
HybridResult = VoxelHybridSharpMesher::Generate(
CellsPerAxis, Step, IsoLevel, GeometryVoxelSize, OriginVoxels, ChunkWorldPos,
HybridSharpFeatures,
[&](int32 X, int32 Y, int32 Z) { return SampleG(X, Y, Z); },
[&](int32 X, int32 Y, int32 Z) { return GradAt(X, Y, Z); });
}
//=========================================================================
// ITÉRATION SUR LES CELLULES
//=========================================================================
@@ -1564,6 +1596,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
const int32 E0 = MCTriTable[CaseIndex][i];
const int32 E1 = MCTriTable[CaseIndex][i + 1];
const int32 E2 = MCTriTable[CaseIndex][i + 2];
if (HybridResult.UsesDCEdge(cx, cy, cz, E0, CellsPerAxis))
{
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]);
@@ -1591,6 +1627,26 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
}
}
// Add DC geometry to the same material runs as MC. Analytic feature IDs split corner normals.
// Ajouter la geometrie DC dans les memes groupes que MC; les IDs separent les normales nettes.
for (const VoxelHybridSharpMesher::FTriangle& Triangle : HybridResult.Triangles)
{
const int32 Idx0 = GetOrCreateVertex(
Triangle.Positions[0], Triangle.Normals[0], Triangle.SplitKeys[0]);
const int32 Idx1 = GetOrCreateVertex(
Triangle.Positions[1], Triangle.Normals[1], Triangle.SplitKeys[1]);
const int32 Idx2 = GetOrCreateVertex(
Triangle.Positions[2], Triangle.Normals[2], Triangle.SplitKeys[2]);
const int32 CapVotes = bStandaloneSkipMaterialGroups ? 0
: static_cast<int32>(VertexClasses[Idx0])
+ static_cast<int32>(VertexClasses[Idx1])
+ static_cast<int32>(VertexClasses[Idx2]);
TArray<int32>& Dst = (CapVotes >= 2) ? CapTris : GroundTris;
Dst.Add(Idx0);
Dst.Add(Idx2);
Dst.Add(Idx1);
}
if (bProfileMesher)
{
VoxelDensityProfile::AddMeasurement(