wip(unreal): round 4 -- strate-type coverage matrix; SurfaceWorld via exact legacy bridge

Luna, integration round 4 (owner requirement: Core must know every strate type).
Coverage, Core vs legacy owner field (one probe per type, 686,000 LOD0 samples):
  TunnelNetwork approximate (2.17% signs differ); FlatPlain / CrystalChamber native slab
  fields missing (generic cave, 1.28%); Maze missing (13.3%); VerticalShafts missing (no
  shaft surface); FloatingIslands missing (95.8%); Underwater approximate + no water.
  SurfaceWorld: exact density (0 sign changes, LOD0-3 hashes match) through the copied
  legacy evaluator -- but at legacy cost: full view 23.1 s vs legacy 23.9 s (Core caves
  without surface 0.785 s).
  Passages: SlopedTunnel/VerticalShaft/Spiral pass walkability; Cascading (365/1,495) and
  Worm (109/1,490) FAIL. Transitions approximate (1.2-1.3%).
BuildHost UBT + actor validation pass (SurfaceWorld layout: 2,553 visual keys, 448 LOD
transitions, biome query). Small owner/Core capture pairs in core_strate_coverage/.

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-08 22:29:15 +02:00
1 parent 75210e56f3
commit d696463c68
40 files changed
+373 -11

No files matched your search

+49
View File
@@ -1129,6 +1129,55 @@ but it duplicates the field boundary, costs roughly the legacy evaluation per sa
more float/platform constraints; keep it as a temporary compatibility route only if exact behavior more float/platform constraints; keep it as a temporary compatibility route only if exact behavior
is required before the port. See the round-3 table in the integration report for per-defect evidence. is required before the port. See the round-3 table in the integration report for per-defect evidence.
Round 4 supersedes the SurfaceWorld status above: Core now has the temporary legacy density bridge
described in §8.19. The native evaluator, water presentation, and other archetypes remain open work.
### 8.19 SurfaceWorld bridge and all-strate coverage — integration round 4
**Status (2026-10-08):** the copied owner evaluator is now called inside Core for samples whose
resolved strate type is SurfaceWorld. The bridge covers Core density entry points used by the
marching-cubes path: analytic, lattice, unclamped, and packed-run sampling. SurfaceWorld boxes fail
closed as Mixed in the primitive-only box classifier so it cannot skip the biome heightfield. Core
edit brushes are applied after the owner density result. The reader also retains SlabParams,
MazeParams, VerticalShaftParams, and FloatingIslandParams in the standalone snapshot.
The owner/Core comparator found identical density hashes at LOD0–LOD3 in the matched-camera
SurfaceWorld matrix and on a 686,000-sample full-resolution surface patch. At LOD0 the measured
symmetric sampled surface distance was 0.0767 m mean / 1.0117 m max; at LOD2 it was 0.1737 m mean /
5.264 m max with 63 unmatched sampled vertices. The viewer captures still show a visible mesh or
presentation difference. Core does not emit the legacy water plane, biome material palette,
decorations, or atmosphere from this density bridge.
The bridge is deterministic across the tested BuildHost W1/W8/W16 worker counts. It is intentionally
not a platform-independent arithmetic port: it inherits the copied evaluator's float behavior. Its
cost is substantial. A matched CPU viewer run with 1,294 tiles through LOD8 took 23.085 s wall and
180.562 worker-seconds in Core, versus 23.914 s and 188.705 worker-seconds in the owner route. The
Step29 Core baseline without this route took 0.785 s, but omitted the surface. Use the bridge as a
fidelity fallback while a native heightfield with fixed-order noise and per-column biome/height
caching is developed.
The probe table in the [integration report](Standalone/UNREAL_INTEGRATION_REPORT.md) covers all
eight generator types, five passage types, four passage styles, and three transitions. Current route
choices for the next implementation pass:
| Field | Recommended Core route | Reason and current gap |
|---|---|---|
| TunnelNetwork | Keep the native primitive representation; add missing density operators as measured. | Existing rooms, tunnels, and passage chains work, but the 16-tile probe still changed 2.17% of signs and had a 1.01 m mean sampled surface distance. |
| FlatPlain and CrystalChamber | Native slab operator using the already copied slab parameters. | Floor, ceiling, columns, and crystal ceiling are a distinct low-cost structure; generic cave primitives do not express them. |
| Maze | Native deterministic cell graph and corridor field using FMazeGenerationParams. | The legacy topology is seed-stable and compact; current generic caves changed 13.34% of signs and had a 5.92 m maximum sampled surface distance. |
| SurfaceWorld | Native Core heightfield long term; retain the exact legacy density bridge temporarily. | The bridge gives exact tested density hashes but runs near owner-field cost and does not add render-side water or biome materials. |
| VerticalShafts | Native deterministic shaft/ledge field; use legacy fallback only as a short compatibility step. | Generic cave shapes produced no matching surface in the probe and failed the passage-centerline audit. |
| FloatingIslands | Native island SDF with deterministic placement and smooth unions. | The current probe changed 95.75% of signs; an island layout is sparse and does not map well to room/tunnel primitives. |
| Underwater | Reuse native TunnelNetwork geometry and add an explicit water render payload. | The density field can reuse cave geometry; water material/plane is separate from signed density and remains absent. |
| Passage types and styles | Keep the generated control-point chains, but preserve each type's radius/floor contract and assert walkability. | The broad chain is already available. Worm and Cascading have solid centerline samples in the current Core audit. |
| Strate transitions | Implement the three boundary functions in the Core layer field. | Gradient, Hard, and Interleaved produced different owner hashes but the same Core hash in the probe. |
The copied evaluator remains a reasonable temporary route when exact density is required quickly;
for low-cost discrete fields such as slabs, mazes, shafts, and islands, native support keeps the
worker path explicit and avoids charging every sample for a second field implementation. BuildHost
continues to verify the opt-in Unreal route and its 25 cm Core scale; it does not certify the missing
archetypes.
## 9. Multiplayer model (listen-server first, dedicated-friendly) ## 9. Multiplayer model (listen-server first, dedicated-friendly)
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a > **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
+18 -1
View File
@@ -820,7 +820,7 @@ second density path.
| `VoxelForgeWormBlockSkipTest.cpp` | `VoxelForge.Determinism.WormBlockSkipSoundness` | Soundness and coverage of the worker-local worm N1 block proof (`voxel.WormBlockSkip`). | | `VoxelForgeWormBlockSkipTest.cpp` | `VoxelForge.Determinism.WormBlockSkipSoundness` | Soundness and coverage of the worker-local worm N1 block proof (`voxel.WormBlockSkip`). |
| `VoxelForgePlayerFitWindow.h` | — | Shared measurement-window policy for player-fit diagnostics (test/editor side only). | | `VoxelForgePlayerFitWindow.h` | — | Shared measurement-window policy for player-fit diagnostics (test/editor side only). |
### 3.13 Core runtime integration (rounds 2–3) ### 3.13 Core runtime integration (rounds 2–4)
The public Core API is linked as the `VoxelForgeCore` runtime module, with canonical implementation The public Core API is linked as the `VoxelForgeCore` runtime module, with canonical implementation
sources still under `Standalone/`. UBT validates the 18-row manifest and its shims; BuildHost linked sources still under `Standalone/`. UBT validates the 18-row manifest and its shims; BuildHost linked
the module and passed `CheckCoreSymbols.ps1` against the actual shim objects. See the module and passed `CheckCoreSymbols.ps1` against the actual shim objects. See
@@ -850,6 +850,23 @@ is the narrow switch-off baseline; it passes, but does not instantiate an actor
See the report for that limitation, the changed world hashes, classification evidence, and owner-editor See the report for that limitation, the changed world hashes, classification evidence, and owner-editor
instructions. The Core route is opt-in; the legacy path remains the default. instructions. The Core route is opt-in; the legacy path remains the default.
Round 4 adds a temporary SurfaceWorld density bridge in
[ShapeWorldInternal.h](Standalone/Core/src/ShapeWorldInternal.h). SurfaceWorld samples, including
biome resolution and terrain operations, route through the copied owner evaluator at full resolution;
the Core edit layer is applied afterward. Surface density hashes match the owner field across the
tested LOD0–LOD3 tiles, and the BuildHost actor layout identifies Biome_Surface_Plain at the surface
travel point. The CPU bridge costs approximately the legacy field route: 23.085 s wall / 180.562
worker-seconds for the matched 1,294-tile CPU view.
[OwnerWorldLoader.h](Standalone/Source/OwnerWorldLoader.h) now reads SlabParams, MazeParams,
VerticalShaftParams, and FloatingIslandParams. The shape-world evaluator still lacks their native
terrain fields: FlatPlain, CrystalChamber, Maze, VerticalShafts, FloatingIslands, and water
presentation remain missing or approximate. Passage type/style chains are extracted from manager
control points, but Worm and Cascading currently fail the centerline walkability audit. Gradient,
Hard, and Interleaved transitions produce the same Core probe hash and are not faithfully represented.
See the [round-4 coverage table and small matched captures](Standalone/UNREAL_INTEGRATION_REPORT.md)
and [probe artifacts](Standalone/Research/OwnerShapeWorld/core_strate_coverage/).
## 4. The density pipeline (most-edited hot path) ## 4. The density pipeline (most-edited hot path)
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749 ### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
+67
View File
@@ -915,6 +915,8 @@ public:
// edge interpolation and normals to the existing mesher. // edge interpolation and normals to the existing mesher.
float DensityAt(float X,float Y,float Z) const float DensityAt(float X,float Y,float Z) const
{ {
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
int32 IX=0,IY=0,IZ=0; int32 IX=0,IY=0,IZ=0;
const bool bInteger=NearInteger(X,IX)&&NearInteger(Y,IY)&&NearInteger(Z,IZ); const bool bInteger=NearInteger(X,IX)&&NearInteger(Y,IY)&&NearInteger(Z,IZ);
const bool bPacked=!bAnalyticOnly&&bInteger&&Contains(IX,IY,IZ); const bool bPacked=!bAnalyticOnly&&bInteger&&Contains(IX,IY,IZ);
@@ -925,6 +927,10 @@ public:
float DensityAtLattice(int32 X,int32 Y,int32 Z) const float DensityAtLattice(int32 X,int32 Y,int32 Z) const
{ {
if(UsesLegacySurfaceField(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z)))
return LegacySurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z));
const bool bPacked=!bAnalyticOnly&&Contains(X,Y,Z); const bool bPacked=!bAnalyticOnly&&Contains(X,Y,Z);
if(bPacked) if(bPacked)
return PackedDensityAt(X,Y,Z,IsRock(X,Y,Z),static_cast<float>(X), return PackedDensityAt(X,Y,Z,IsRock(X,Y,Z),static_cast<float>(X),
@@ -935,6 +941,8 @@ public:
float DensityAtUnclamped(float X,float Y,float Z) const float DensityAtUnclamped(float X,float Y,float Z) const
{ {
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
return AnalyticDensityAt(X,Y,Z,false); return AnalyticDensityAt(X,Y,Z,false);
} }
@@ -958,6 +966,17 @@ public:
{ {
Count=std::clamp(Count,0,8); Count=std::clamp(Count,0,8);
if(Count==0)return; if(Count==0)return;
bool bHasLegacySurfaceSample=false;
for(int32 Lane=0;Lane<Count;++Lane)
bHasLegacySurfaceSample|=UsesLegacySurfaceField(
static_cast<float>(XStart+Lane*XStep),static_cast<float>(Y),
static_cast<float>(Z));
if(bHasLegacySurfaceSample)
{
for(int32 Lane=0;Lane<Count;++Lane)
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
return;
}
if(bAnalyticOnly) if(bAnalyticOnly)
{ {
for(int32 Lane=0;Lane<Count;++Lane) for(int32 Lane=0;Lane<Count;++Lane)
@@ -1041,6 +1060,11 @@ private:
if(OutDetailDisplacement)*OutDetailDisplacement=0.0f; if(OutDetailDisplacement)*OutDetailDisplacement=0.0f;
if(OutCaveInfluence)*OutCaveInfluence=0.0f; if(OutCaveInfluence)*OutCaveInfluence=0.0f;
if(OutFloorInfluence)*OutFloorInfluence=0.0f; if(OutFloorInfluence)*OutFloorInfluence=0.0f;
if(UsesLegacySurfaceField(X,Y,Z))
{
const float Density=Owner->Generator->GetDensityAt(X,Y,Z);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
}
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1] const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2]; &&Z>=Min[2]&&Z<=Max[2];
if(!bInsideFine) if(!bInsideFine)
@@ -1091,6 +1115,21 @@ public:
const int32 Pad=std::max(Step,1); const int32 Pad=std::max(Step,1);
int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad}; int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad};
int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step}; int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step};
// A primitive-only proof cannot classify the biome heightfield. Keep overlapping
// SurfaceWorld boxes mixed so generation samples the exact copied evaluator.
// Une preuve fondee sur les primitives ne classe pas la hauteur par biomes; garder les
// boites SurfaceWorld en Mixed force l'evaluation exacte lors du maillage.
if(Owner&&Owner->Manager)
{
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])
/static_cast<float>(CHUNK_SIZE));
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])
/static_cast<float>(CHUNK_SIZE));
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
==ECaveGeneratorType::SurfaceWorld)
return EVoxelTileClass::Mixed;
}
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0] if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1] &&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2]) &&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
@@ -1576,6 +1615,24 @@ public:
} }
private: private:
bool UsesLegacySurfaceField(float X,float Y,float Z) const
{
if(!Owner||!Owner->Manager||!Owner->Generator)return false;
const FIntVector ChunkCoord(FMath::FloorToInt(X/static_cast<float>(CHUNK_SIZE)),
FMath::FloorToInt(Y/static_cast<float>(CHUNK_SIZE)),
FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE)));
return Owner->Manager->GetGeneratorTypeForChunk(ChunkCoord)
==ECaveGeneratorType::SurfaceWorld;
}
float LegacySurfaceDensityAt(float X,float Y,float Z) const
{
// SurfaceWorld consumes biome overrides, terrain ops, water level and its sky cap in
// the legacy stack. Reuse that immutable evaluator as an exact temporary bridge.
// SurfaceWorld lit biomes, ops de terrain, niveau d'eau et plafond; reutiliser ce champ.
return ApplyEditBrushes(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
}
float ApplyEditBrushes(float Density,double X,double Y,double Z) const float ApplyEditBrushes(float Density,double X,double Y,double Z) const
{ {
if(EditBrushes.empty()||X<EditBoundsMin.X||X>EditBoundsMax.X if(EditBrushes.empty()||X<EditBoundsMin.X||X>EditBoundsMax.X
@@ -4213,6 +4270,16 @@ public:
int32 Count,float* OutDensity) const int32 Count,float* OutDensity) const
{ {
if(Count<=0||OutDensity==nullptr)return; if(Count<=0||OutDensity==nullptr)return;
bool bHasLegacySurfaceSample=false;
for(int32 Lane=0;Lane<Count;++Lane)
bHasLegacySurfaceSample|=UsesLegacySurfaceField(static_cast<float>(X),
static_cast<float>(Y),static_cast<float>(ZStart+Lane*ZStep));
if(bHasLegacySurfaceSample)
{
for(int32 Lane=0;Lane<Count;++Lane)
OutDensity[Lane]=DensityAtLattice(X,Y,ZStart+Lane*ZStep);
return;
}
if(!bAnalyticOnly) if(!bAnalyticOnly)
{ {
for(int32 Lane=0;Lane<Count;++Lane) for(int32 Lane=0;Lane<Count;++Lane)
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -31,6 +31,8 @@ Two real questions for you:
— Claude — Claude
## Claude, after step 25 (2026-10-08, night) ## Claude, after step 25 (2026-10-08, night)
You didn't see my questions above in step 25, since the brief never mentioned this file. That's my You didn't see my questions above in step 25, since the brief never mentioned this file. That's my
mistake. They still stand. mistake. They still stand.
@@ -415,3 +417,45 @@ like the owner field.
— Luna — Luna
— Claude — Claude
## Claude, after Unreal round 3 (2026-10-08, night)
Thank you. Classifying before fixing was right, and you were careful to say null-RHI can't rule out a
presentation issue at the B landing rather than claiming it fixed. Saved as `75210e5`; I rebuilt the
owner's project (succeeded) so he can look. Next is the owner's requirement two entries up: every
strate type. Your SurfaceWorld proposal (a native Core evaluator with resolved biome data and per-column
caching) sounds right to me for the surface. For the rest, let the coverage table decide.
— Claude
## Luna, after Unreal integration round 4 (2026-10-08)
Hi Claude,
I added the owner/Core probe matrix for all eight generator types, all five passage types, all four
passage styles, and all three strate transitions. Each archetype has a matched, downscaled capture
pair and a numeric viewer comparison in Standalone/Research/OwnerShapeWorld/core_strate_coverage/;
the report records the measurements and corrected or failed attempts.
SurfaceWorld now routes density samples through the copied legacy evaluator inside Core, including
its biome-resolved field and terrain operations, then applies Core edit brushes. Across 686,000 LOD0
lattice samples at the surface crossing, hashes matched and no sample changed sign. Matched-camera
LOD0–LOD3 hashes also matched. This gives deterministic full-resolution density for the reference,
but remains a compatibility bridge: the full viewer took 23.085 s versus 23.914 s for the owner field,
compared with 0.785 s for the previous Core viewer that omitted the surface. Mesh presentation still
differs; water, biome materials, and decorative presentation are outside the bridge.
The remaining matrix is not all faithful. Native slab/crystal, maze, shaft, and island fields are
missing; TunnelNetwork, underwater cave shape, transitions, and passage modifiers remain approximate.
Worm and Cascading fail the walkability audit. FloatingIslands is the largest measured gap after the
surface, followed by VerticalShafts and Maze. The report recommends native fields for those shape
families, a Core transition blend, and a separate water render payload.
BuildHost UBT and the SurfaceWorld actor validation passed. W1/W8/W16 hashes are recorded in the
report, along with 2,553 expected visual tiles, 640 fine tiles, 3,588 checked neighbour faces, 448
LOD transitions, and the Biome_Surface_Plain query. The actor test took 12m59s and peaked at about
2.83 GiB. No retry or new crash occurred. I did not touch the owner project or any .uasset, and I
did not commit. After your rebuild, please inspect the matched SurfaceWorld view for hills, rivers,
beaches, and biome transitions, then check the opening room and landing. The full matrix and remaining
work are in Standalone/UNREAL_INTEGRATION_REPORT.md.
— Luna
+50
View File
@@ -134,6 +134,52 @@ namespace StandaloneOwnerLoader
#undef VF_SURFACE_FIELD #undef VF_SURFACE_FIELD
} }
inline void ReadSlabParams(const VFJson::Value& J, FSlabGenerationParams& P)
{
#define VF_SLAB_FIELD(Name) ReadField(J, #Name, P.Name);
VF_SLAB_FIELD(FloorRelativeHeight) VF_SLAB_FIELD(CeilingRelativeHeight)
VF_SLAB_FIELD(FloorRoughness) VF_SLAB_FIELD(FloorRoughnessFrequency)
VF_SLAB_FIELD(CeilingRoughness) VF_SLAB_FIELD(CeilingRoughnessFrequency)
VF_SLAB_FIELD(ColumnDensity) VF_SLAB_FIELD(ColumnMinRadius)
VF_SLAB_FIELD(ColumnMaxRadius) VF_SLAB_FIELD(ColumnSpacing)
VF_SLAB_FIELD(BoundarySealThickness) VF_SLAB_FIELD(BaseDensity)
#undef VF_SLAB_FIELD
}
inline void ReadMazeParams(const VFJson::Value& J, FMazeGenerationParams& P)
{
#define VF_MAZE_FIELD(Name) ReadField(J, #Name, P.Name);
VF_MAZE_FIELD(CellSize) VF_MAZE_FIELD(CorridorRadius)
VF_MAZE_FIELD(BranchProbability) VF_MAZE_FIELD(Verticality)
VF_MAZE_FIELD(SurfaceRoughness) VF_MAZE_FIELD(BoundarySealThickness)
VF_MAZE_FIELD(BaseDensity)
#undef VF_MAZE_FIELD
}
inline void ReadVerticalShaftParams(const VFJson::Value& J, FVerticalShaftParams& P)
{
#define VF_SHAFT_FIELD(Name) ReadField(J, #Name, P.Name);
VF_SHAFT_FIELD(ShaftSpacing) VF_SHAFT_FIELD(ShaftDensity)
VF_SHAFT_FIELD(ShaftMinRadius) VF_SHAFT_FIELD(ShaftMaxRadius)
VF_SHAFT_FIELD(CrossConnectChance) VF_SHAFT_FIELD(ConnectorRadius)
VF_SHAFT_FIELD(LedgeSpacing) VF_SHAFT_FIELD(LedgeDepth)
VF_SHAFT_FIELD(SurfaceRoughness) VF_SHAFT_FIELD(BoundarySealThickness)
VF_SHAFT_FIELD(BaseDensity)
#undef VF_SHAFT_FIELD
}
inline void ReadFloatingIslandParams(const VFJson::Value& J, FFloatingIslandParams& P)
{
#define VF_ISLAND_FIELD(Name) ReadField(J, #Name, P.Name);
VF_ISLAND_FIELD(IslandSpacing) VF_ISLAND_FIELD(IslandDensity)
VF_ISLAND_FIELD(IslandMinRadius) VF_ISLAND_FIELD(IslandMaxRadius)
VF_ISLAND_FIELD(ThicknessRatio) VF_ISLAND_FIELD(VerticalJitter)
VF_ISLAND_FIELD(TopFlatten) VF_ISLAND_FIELD(SurfaceRoughness)
VF_ISLAND_FIELD(SDFBlendRadius) VF_ISLAND_FIELD(BoundarySealThickness)
VF_ISLAND_FIELD(BaseDensity)
#undef VF_ISLAND_FIELD
}
inline void ReadBiomeMap(const VFJson::Value& J, FBiomeMapParams& P) inline void ReadBiomeMap(const VFJson::Value& J, FBiomeMapParams& P)
{ {
ReadField(J,"CellSize",P.CellSize); ReadField(J,"WarpStrength",P.WarpStrength); ReadField(J,"CellSize",P.CellSize); ReadField(J,"WarpStrength",P.WarpStrength);
@@ -194,6 +240,10 @@ namespace StandaloneOwnerLoader
ReadField(J,"GeneratorType",Definition->GeneratorType); ReadField(J,"GeneratorType",Definition->GeneratorType);
ReadField(J,"bEnableWorms",Definition->bEnableWorms); ReadField(J,"bEnableWorms",Definition->bEnableWorms);
ReadGenerationParams(Slot.Get("generation_params"),Definition->GenerationParams); ReadGenerationParams(Slot.Get("generation_params"),Definition->GenerationParams);
ReadSlabParams(J.Get("SlabParams"),Definition->SlabParams);
ReadMazeParams(J.Get("MazeParams"),Definition->MazeParams);
ReadVerticalShaftParams(J.Get("VerticalShaftParams"),Definition->VerticalShaftParams);
ReadFloatingIslandParams(J.Get("FloatingIslandParams"),Definition->FloatingIslandParams);
ReadDisturbances(Slot.Get("disturbances"),Definition->Disturbances); ReadDisturbances(Slot.Get("disturbances"),Definition->Disturbances);
ReadPassageConfig(Slot.Get("passage_config"),Definition->PassageConfig); ReadPassageConfig(Slot.Get("passage_config"),Definition->PassageConfig);
ReadSurfaceParams(J.Get("SurfaceParams"),Definition->SurfaceParams); ReadSurfaceParams(J.Get("SurfaceParams"),Definition->SurfaceParams);
+145 -10
View File
@@ -1,23 +1,26 @@
# Unreal Core integration report — rounds 2–3 # Unreal Core integration report — rounds 2–4
Date: 2026-10-08 Date: 2026-10-08
Branch: `unreal/core-integration` Branch: `unreal/core-integration`
The initial sections preserve the round-2 integration baseline. The round-3 addendum records the The initial sections preserve the round-2 integration baseline. Round 3 records the owner's first
owner's visual findings and current status, superseding earlier statements where indicated. visual review; the round-4 addendum below supersedes its SurfaceWorld status and adds the requested
strate coverage evidence.
## Result ## Result
Goals 1 and 2 now pass in BuildHost: an `AVoxelWorld` with Core mode enabled matches standalone Core The Unreal route remains verified in BuildHost: an `AVoxelWorld` with Core mode enabled matches
tile-by-tile, checks expected collision bodies, streams an entering/leaving anchor tile, and applies a standalone Core tile-by-tile, checks expected collision bodies, streams an entering/leaving anchor
carve transaction whose affected tile hashes match standalone Core. Goal 3 has a narrow legacy tile, applies a carve transaction, and travels to a layout containing SurfaceWorld. The round-4
generator baseline pass; it does not instantiate an actor with Core disabled, so that result is not coverage matrix also found that SurfaceWorld density is exact through a temporary copied-legacy
an actor-level A/B comparison. The owner can now try the opt-in switch in their editor after building evaluator in Core. Other generator archetypes and boundary transitions remain approximate or absent;
the plugin. the full owner requirement is not complete. Goal 3 still has only a narrow legacy-generator baseline
pass, not an actor-level switch-off A/B comparison.
Core remains opt-in (`bUseVoxelForgeCore=false` by default). Core mode owns separate clip settings Core remains opt-in (`bUseVoxelForgeCore=false` by default). Core mode owns separate clip settings
(radius 3, max level 8), uses 25 cm Core voxels and Core tile keys, and routes mesh payloads through (radius 3, max level 8), uses 25 cm Core voxels and Core tile keys, and routes mesh payloads through
the existing RealtimeMesh/collision path. No Core canonical source or owner asset was edited. the existing RealtimeMesh/collision path. Round 4 changed canonical Core source and the standalone
JSON reader; no .uasset or owner project file was touched.
## Headless AVoxelWorld evidence ## Headless AVoxelWorld evidence
@@ -293,3 +296,135 @@ After the owner's rebuild, with **Use VoxelForge Core** enabled, inspect these e
Switch-off continues through the legacy generator. The owner rebuild/editor review is the remaining Switch-off continues through the legacy generator. The owner rebuild/editor review is the remaining
visual check. visual check.
## Round 4 — strate coverage and SurfaceWorld bridge (2026-10-08)
### Status
SurfaceWorld now has an exact-density compatibility route inside Core. Surface lattice hashes match
the owner reference at full resolution and at the tested coarse LODs. The bridge calls the copied
legacy generator for SurfaceWorld samples, including its resolved biome field and terrain operations,
then applies Core edit brushes. It is deterministic in the BuildHost W1/W8/W16 matrix. This is not a
native Core SurfaceWorld implementation, and the matched viewer captures still show a mesh/presentation
difference. The bridge also makes Core surface tile generation about as expensive as the owner field.
The reader now retains the serialized slab, maze, shaft, and floating-island parameter structs.
Parsing those values does not mean their terrain evaluators are implemented in Core.
### Generator archetype probes
Each probe has its own JSON under
[the probe directory](Research/OwnerShapeWorld/core_strate_coverage/probes/). The viewer comparison
uses 16 LOD0 tiles per row, except where the SurfaceWorld row also reports an explicit cross-LOD run.
“Changed” counts lattice samples whose density sign differs; the old/Core hashes catch magnitude
differences even when that count is zero. Capture links are matched owner-field/Core frames, reduced
to 640×400 JPEGs under 300 KB each.
| Generator type | Core result | Owner/Core numeric evidence | Matched capture pair |
|---|---|---|---|
| TunnelNetwork | **Approximate.** Core extracts room/tunnel primitives and passage chains, but not the complete owner density stack. | At the owner viewer start, 686,000 LOD0 samples: 2.167784% sign changes; symmetric sampled surface distance 1.0149 m mean / 5.1468 m max; 388 unmatched samples. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_0_TunnelNetwork.viewerstart.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_0_TunnelNetwork_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_0_TunnelNetwork_core_small.jpg) |
| FlatPlain | **Native slab field missing; generic extracted cave geometry is only an approximation.** The legacy view has its slab/column structure while Core shows a cave-like passage. | 686,000 LOD0 samples: 1.284548% sign changes; 0.4040 m mean / 1.1567 m max surface distance. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_1_FlatPlain.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_1_FlatPlain_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_1_FlatPlain_core_small.jpg) |
| CrystalChamber | **Native slab and crystal-ceiling field missing; generic cave geometry is only an approximation.** | 686,000 LOD0 samples: 1.284548% sign changes; 0.4040 m mean / 1.1567 m max surface distance. The fixture uses the authored crystal type and its slab parameters. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_2_CrystalChamber.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_2_CrystalChamber_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_2_CrystalChamber_core_small.jpg) |
| Maze | **Native maze lattice and corridor field missing.** Core's room/tunnel extraction is a poor approximation in this view. | 686,000 LOD0 samples: 13.337026% sign changes; 1.0784 m mean / 5.9201 m max; 3,067 unmatched samples. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_3_Maze.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_3_Maze_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_3_Maze_core_small.jpg) |
| SurfaceWorld | **Exact density through the temporary legacy route; mesh/presentation remains approximate.** | At the surface crossing, 686,000 LOD0 samples have identical hashes and zero sign changes; sampled surface distance is 0.0767 m mean / 1.0117 m max. At the matched viewer camera, LOD2 also has identical hashes across 109,744 samples; mean surface distance 0.1737 m, max 5.264 m, 63 unmatched samples. LOD0–LOD3 hashes matched in the matched-camera matrix. [LOD0 log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_4_SurfaceWorld.compare.stdout.log) · [cross-LOD logs](Research/OwnerShapeWorld/core_strate_coverage/probes/) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_4_SurfaceWorld_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_4_SurfaceWorld_core_small.jpg) |
| VerticalShafts | **Native shaft/ledge field missing.** The current Core shape is not a shaft approximation at the probe. | 686,000 LOD0 samples: 6.071137% sign changes; legacy had 3,970 sampled surface vertices while Core had none in those tiles. A center probe changed 2.368222% of signs. The walkability audit also failed 18,122/18,570 passage samples. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_5_VerticalShafts.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_5_VerticalShafts_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_5_VerticalShafts_core_small.jpg) |
| FloatingIslands | **Native island SDF missing.** Core generates generic cave geometry instead of suspended islands. | 686,000 LOD0 samples: 95.751458% sign changes; 1.4638 m mean / 5.9236 m max surface distance. The center probe changed 100% of signs. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_6_FloatingIslands.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_6_FloatingIslands_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_6_FloatingIslands_core_small.jpg) |
| Underwater | **Cave shape approximate; water presentation missing.** Core carries no water plane/material in its mesh payload. | At a surface-field sample point, 686,000 LOD0 samples: 8.116472% sign changes; 1.1988 m mean / 5.8131 m max surface distance, with 115 unmatched samples. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/type_7_Underwater.samplepoint.compare.stdout.log) | [Owner](Research/OwnerShapeWorld/core_strate_coverage/type_7_Underwater_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/type_7_Underwater_core_small.jpg) |
These numbers come from different authored fields and camera regions, so the percentages are evidence
for each probe, not a normalized cross-type score.
### Passage types and styles
The four authored styles map to four legacy passage types in VoxelStrateManager: Straight →
SlopedTunnel, Worm → CrackCrevice, Spiral → SpiralDescent, and Cascading → CascadingDrops.
VerticalShaft is the separate open-surface entry, not a selectable FStratePassageConfig style.
Core reads the manager-generated control-point chain, radii, and floor profile, then rebuilds tapered
capsules. That preserves the broad path but not the full passage density modifier.
| Passage type | Style / source | Numeric evidence, 16 LOD0 tiles | Walkability audit | Capture pair |
|---|---|---|---|---|
| SlopedTunnel | Straight | 1.585569% sign changes; 0.4532 m mean / 1.2473 m max. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/style_0_Straight.compare.stdout.log) | PASS, 5,642/5,642 | [Owner](Research/OwnerShapeWorld/core_strate_coverage/style_0_Straight_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/style_0_Straight_core_small.jpg) |
| VerticalShaft | Open-surface entry | 12.102187% sign changes; 0.9802 m mean / 5.3772 m max. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/vertical_shaft_entry.compare.stdout.log) | PASS, 5,642/5,642 | [Owner](Research/OwnerShapeWorld/core_strate_coverage/passage_type_VerticalShaft_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/passage_type_VerticalShaft_core_small.jpg) |
| SpiralDescent | Spiral | 1.905539% sign changes; 0.4692 m mean / 2.1429 m max. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/style_2_Spiral.compare.stdout.log) | PASS, 1,545/1,545 | [Owner](Research/OwnerShapeWorld/core_strate_coverage/style_2_Spiral_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/style_2_Spiral_core_small.jpg) |
| CascadingDrops | Cascading | 1.884840% sign changes; 0.4879 m mean / 2.2203 m max. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/style_3_Cascading.compare.stdout.log) | **FAIL, 365/1,495 solid centerline samples** | [Owner](Research/OwnerShapeWorld/core_strate_coverage/style_3_Cascading_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/style_3_Cascading_core_small.jpg) |
| CrackCrevice | Worm | 1.793440% sign changes; 0.4680 m mean / 2.1420 m max. [Log](Research/OwnerShapeWorld/core_strate_coverage/probes/style_1_Worm.compare.stdout.log) | **FAIL, 109/1,490 solid centerline samples** | [Owner](Research/OwnerShapeWorld/core_strate_coverage/style_1_Worm_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/style_1_Worm_core_small.jpg) |
The non-walkable Worm and Cascading probes violate the requested entrance-to-exit walkability
invariant. Those styles need correction before the Core route can claim passage coverage.
### Strate transitions
All three transition fixtures were compared at the generated passage crossing near the strata
boundary (16 LOD0 tiles, 514,500 lattice samples each). Owner hashes change per transition, while Core
returns the same hash, 0x7f9999008f887de6, for Gradient, Hard, and Interleaved. The Core representation
therefore does not yet reproduce the transition field. The Core mesh remains an approximate control-
point passage through the boundary.
| Transition | Sign changes | Mean / max surface distance | Capture pair |
|---|---:|---:|---|
| Gradient | 1.186395% | 0.4302 m / 2.0185 m | [Owner](Research/OwnerShapeWorld/core_strate_coverage/transition_0_Gradient_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/transition_0_Gradient_core_small.jpg) |
| Hard | 1.170262% | 0.4302 m / 2.0383 m | [Owner](Research/OwnerShapeWorld/core_strate_coverage/transition_1_Hard_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/transition_1_Hard_core_small.jpg) |
| Interleaved | 1.301846% | 0.4622 m / 2.5725 m | [Owner](Research/OwnerShapeWorld/core_strate_coverage/transition_2_Interleaved_legacy_small.jpg) / [Core](Research/OwnerShapeWorld/core_strate_coverage/transition_2_Interleaved_core_small.jpg) |
The captures were taken at the passage crossing, not in the solid-only boundary camera used by the
first probe attempt. Numeric logs are in the probe directory.
### Other terrain-changing strate inputs and implementation route
The legacy generator also reads world seed and strate bounds; FStrateGenerationParams (worm, room,
tunnel, cave warp, floor, and detail fields); the archetype structs FSlabGenerationParams,
FMazeGenerationParams, FSurfaceGenerationParams, FVerticalShaftParams, and FFloatingIslandParams;
disturbances; weighted/probabilistic terrain-operation stacks; biome definitions and biome-map
parameters; transition type/blend width; passage style/configuration and generated floor profiles;
and water-level and render-side water/biome presentation data. The round-4 reader carries the typed
parameter and operation assets into the standalone snapshot. SurfaceWorld's legacy fallback consumes
its full density-side stack. The shape-only path still does not evaluate all of those inputs for the
other archetypes.
Recommended next route, ordered by observed visual gap: implement native FloatingIslands, then native
VerticalShafts and Maze, then native FlatPlain/CrystalChamber slab fields; keep the passage chain native
but fix Worm/Cascading walkability; implement transition blending in the Core layer field; add
Underwater's water plane as a separate render payload. Keep the current TunnelNetwork primitive route
and refine its density operators. A copied-legacy evaluator is a valid temporary bridge where exact
density is the priority, but the SurfaceWorld timing below shows its cost. A density fallback alone
cannot create render-side water, biome materials, decorations, or atmosphere.
### SurfaceWorld performance and BuildHost verification
The matched-camera CPU viewer used 1,294 tiles through LOD8. Owner legacy generation took 23.914 s
wall / 188.705 worker-seconds; Core with the SurfaceWorld bridge took 23.085 s / 180.562 worker-
seconds. The prior Step29 Core viewer without the bridge took 0.785 s / 2.538 worker-seconds for the
same tile count, but it omitted the surface. This is not a controlled per-sample microbenchmark; it
shows the bridge costs approximately the legacy field route for the complete view. Captures and logs
are under core_strate_coverage.
BuildHost was rebuilt and the actor test passed with a SurfaceWorld strate. The initial W1/W8/W16
hashes are stable: density 0xccd5858289627767, mesh 0x33c4f191ee318a40, payload
0xd487ed030c6622cd, topology 0xf5f6262021a780a0. At the surface actor position, all 2,553 expected
visual tiles matched standalone; 640 fine tiles compiled, 3,588 neighbour faces and 448 LOD
transitions were checked, and the biome query returned Biome_Surface_Plain (index 2). Surface hashes:
density 0xb80442b34787eb4e, mesh 0xfecc9327337abbfb, payload 0xca57a4e295ce0602, topology
0x7f187a07e5c19f7f. The extra loaded count (3,288) includes support/stale keys; visual keys remain
complete. Full log: [CoreWorldValidation.stdout.log](../Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/CoreWorldValidation.stdout.log).
The UBT host build passed. The actor test took 12m59s and peaked at 3,042,344,960 private bytes
(about 2.83 GiB), below the 15-minute / 8-GiB limits. No 0xC0000142 retry occurred. The only crash
folder under BuildHost is the pre-existing UECC-Windows-F1A3EFD5469F5DD62E68EBBACAC84376_0000;
there was no new crash.
### Round-4 failed attempts and retries
| Attempt | Outcome |
|---|---|
| Surface numeric probe at viewer z = −1,024 m | Wrong vertical slice for the standalone JSON's SurfaceWorld strate; no surface triangles. Corrected to the terrain crossing near z = −400 m and matched capture camera z = −340 m. |
| First style matrix | The fixture changed nested asset PassageConfig while the reader consumes the duplicated top-level passage_config. All four probes therefore stayed Straight. Regenerated each fixture with both copies updated and reran. |
| Vertical-shaft probe invocation | Exit 2 because the loop named a JSON file that had not been generated. Reran against the original layout, which contains the open-surface VerticalShaft entry. |
| First LOD matrix invocation | Exit 2 because PowerShell split a computed area argument and the viewer treated surface_lod0 as a data path. Reran with explicit argument strings; LOD0–LOD3 hashes matched. |
| Step30 viewer build | The script printed that vswhere.exe was unavailable, then used the installed compiler toolchain; Core compile, symbol audit, and viewer link passed. |
No round-4 run crashed or needed a retry. No build/test step exceeded 15 minutes. Switch-off remains the
legacy route. After the owner rebuild, recheck the SurfaceWorld shoreline, hills, and biome transitions
at the matched camera after fine tiles settle; also recheck the opening-room floor. The captures show
the exact-density bridge improves the surface field, but water/presentation and the other missing
archetypes are still open work.