wip(assembler): M1 round 6 -- owner walk fixes: 25 cm geometry scale, one height datum, carve/fill sign, faster relief meshing

Owner saw content floating above native terrain, spiky triangles, reversed carve/fill and late relief.
Causes: shared mesher placed Core tiles on a 12.5 cm grid; content/atmosphere asked strate ranges in
12.5 cm voxels; Core edits add density where the legacy diff subtracts. Horizon LOD8 bridge edge
5.657 -> 2.265 cells. Relief mesh p95 60.8 -> 42.0 ms (LOD0). New BuildHost checks for scale, height,
blueprint carve/fill and per-kind streaming lag.

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-09 20:39:24 +02:00
1 parent f67b519e75
commit 61633cb492
18 files changed
+1708 -142

No files matched your search

+154
View File
@@ -651,3 +651,157 @@ New source files: none. Modified source files:
`Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp`, `Source/VoxelForge/Private/VoxelWorld.cpp`, and
`Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp`. Modified BuildHost test tool:
`Tools/TestVoxelForgeCoreWorldHost.ps1`.
## Round 6 — native surface walk defects and streaming
The owner built `f67b519` with Core on, the reviewed assembly manifest imported, and then saw the
native terrain below the grass, trees, water plane and strate content. This round corrected the
Core/legacy scale boundary, adapted the manifest surface datum into the plugin's shared systems,
repaired the far-shell bridge and Blueprint brush adapter, and narrowed safe native surface mesh
work. BuildHost staged the same five owner `.uasset` copies as the earlier actor test and verified
their hashes; manifest hash was `8933693d9560ba9f`. No owner asset or editor was touched.
### Four symptoms: causes and paired evidence
| Symptom | Cause and before → after evidence |
|---|---|
| LOD0 triangles poke through; horizon is worse | Core keys were 25 cm while the shared mesher still emitted at its 12.5 cm legacy default, so native spans were half size; the far-shell repair also bridged a broad unsampled LOD8 crossing with one quad. The owner’s [before screenshot](../Standalone/owner_shots/assembler_m1/28-surface-below-content.jpg) is the visual baseline. BuildHost measured the old scale ratio as `0.5000`, and the old horizon edge as `72,407.7 cm / 5.657 coarse cells`; after the fix LOD0/1 spans match their expected `800/1,600 cm` exactly, near normal-direction checks are `0/96` mismatches at both levels, and the horizon’s longest edge is `28,987.9 cm / 2.265 cells`. |
| Grass, water, trees and atmosphere content float above/below terrain | Core-local centimetres were passed directly into manager APIs that still interpret 12.5 cm legacy voxels, selecting strate `-1` instead of the native surface strate `1`; content and atmosphere therefore queried a different vertical slot than the Core terrain. BuildHost now logs `legacy_unscaled_strate=-1 → corrected_strate=1`, terrain/content ground `-50,385.254/-50,385.254 cm` (`0 cm` delta), water enabled at `-49,375.977 cm`, and atmosphere bounds `(-63,200,-12,000) cm`. |
| Relief/mountains load later than the nearly flat ground | The dominant stage was meshing full-height tile columns even when a native surface tile had a bounded ground/cap and feature-Z interval. Before → after band-disabled/banded Core relief meshing p95 was `60.840 → 41.981 ms` at LOD0 (`−31%`) and `70.297 → 42.649 ms` at LOD1 (`−39%`); mixed LOD0 was `55.792 → 50.007 ms` (`−10%`). Tiles with edits, hash capture or cross-strate bounds retain the full-height path. |
| Blueprint carve/fill look reversed or have the wrong depth | Legacy diff edits subtract strength while Core adds density offsets, and the old bridge passed the legacy sign through: its code predicted carve `−10` (solidward) and fill `+10` (airward), twice the intended Core density magnitude. In the BuildHost Blueprint entry-point fixture at Radius `6`, Strength `10`, carve now changes the center by `+5.0000` Core density and fill by `−5.0000`; each changes a point `60 cm` away by `±0.5200` and leaves `80 cm` away at `0.0000`. The adapter scales radius, extent, falloff and strength by `0.5` into 25 cm Core voxels and flips the sign once. |
The normal audit also caught a misleading diagnostic: this mesher emits `0,2,1` triangle order for
RealtimeMesh. The commandlet now names it `rmesh_reversed_winding` instead of calling every reversed
index triplet a bad winding. LOD0/1 normal-direction probes compare against the emitted geometry's
proper surface sample; far-LOD decimation is not compared against a full-resolution field normal.
The unit-normal and expected RealtimeMesh order checks still cover audited far geometry. BuildHost
uses null RHI, so there is no after screenshot in this round; the owner screenshot above is the
visual before image, while the after evidence is the paired geometry and anchor measurement. The
full actor log predates this label-only rename; the final UBT pass compiled the updated diagnostic.
### Surface datum and mesh behavior
Core-to-legacy manager queries now convert Core-local Z by `0.5` before slot lookup and convert
returned bounds by `2.0`. Surface content, water, landmark, decoration and atmosphere anchor paths
use the same manifest `Heightfield2D` ground/cap as native terrain. The atmosphere range is expressed
in Core centimetres. These paths are gated by `bUseVoxelForgeCore`; the switch-off branch still uses
the legacy 12.5 cm route. `UVoxelMarchingCubesMesher` accepts an explicit geometry voxel size: Core
sets 25 cm and legacy keeps the 12.5 cm default. Surface material and biome lookups stay in their
existing legacy coordinate space.
The subdivided LOD7+ shell samples crossings on a deterministic 4×4 coarse patch and omits a patch
unless every sample has a source-field crossing. This replaces the unsampled single-quad bridge.
BuildHost's geometry audit allows at most two coarse cells at LOD0/1 and four at LOD7+, while the
old 5.657-cell horizon quad remains a rejected negative control. Final spans and edge lengths are
in the geometry line in the [final BuildHost log](../Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/CoreWorldValidation.stdout.log).
### Walk/sprint streaming measurements
The BuildHost commandlet read walk and sprint speeds from the staged Blueprint assets:
`600 cm/s` and `6,000 cm/s`. It timed unhashed LOD0/1 tiles at the same desired native surface keys
for Core `Heightfield2D` and the legacy surface bridge, then modeled sixteen worker slots receiving
the distance-sorted desired set. `ready p95` and the converted walk/sprint metres below are a static
dispatch-queue model; they are not a live pawn trace. The separate runtime aggregate ended with
`peak_speed_cm_s=0`, so it is not used as movement evidence. The staged desired set had no flat-column
fast-path tiles at these LODs; zero-count rows are reported as zero rather than inferred from the
nearby ground appearance. “Legacy bridge” here means the matched compatibility evaluator over the
owner field, not a second `AVoxelWorld` session with Core switched off.
| Backend | LOD | Tile kind | Tiles (banded) | Service p95 ms | Model-ready p95 s | Walk travel m | Sprint travel m |
|---|---:|---|---:|---:|---:|---:|---:|
| Core | 0 | flat column | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Core | 0 | relief | 39 (39) | 42.001 | 1.229687 | 7.378 | 73.781 |
| Core | 0 | mixed | 473 (70) | 50.008 | 1.697452 | 10.185 | 101.847 |
| Core | 0 | unknown | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Core | 1 | flat column | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Core | 1 | relief | 41 (41) | 42.674 | 2.775380 | 16.652 | 166.523 |
| Core | 1 | mixed | 407 (7) | 63.598 | 3.082213 | 18.493 | 184.933 |
| Core | 1 | unknown | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Legacy bridge | 0 | flat column | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Legacy bridge | 0 | relief | 39 (0) | 63.288 | 1.401714 | 8.410 | 84.103 |
| Legacy bridge | 0 | mixed | 53 (0) | 60.778 | 1.361116 | 8.167 | 81.667 |
| Legacy bridge | 0 | unknown | 420 (0) | 48.999 | 1.883834 | 11.303 | 113.030 |
| Legacy bridge | 1 | flat column | 0 (0) | 0.000 | 0.000000 | 0.000 | 0.000 |
| Legacy bridge | 1 | relief | 41 (0) | 71.396 | 2.996399 | 17.978 | 179.784 |
| Legacy bridge | 1 | mixed | 7 (0) | 70.957 | 2.987685 | 17.926 | 179.261 |
| Legacy bridge | 1 | unknown | 400 (0) | 62.070 | 3.275383 | 19.652 | 196.523 |
The relief service p95 maps to about `2.52 m` of sprint travel at LOD0 and `2.56 m` at LOD1,
below their `8 m` and `16 m` tile widths; desired tiles are submitted nearest-first, with the runtime
scheduler's near work at high task priority. That gives the nearby relief tile a service-time margin.
The complete-cohort queue p95 remains long (up to `166.5 m` for Core LOD1 relief and `184.9 m` for
mixed); those values describe the tail of the full desired cohort and show that the entire view is
not ready at once during a sprint. They are not a claim that those distant tiles are ahead of the
pawn. This test establishes the per-tile cost and priority headroom; the owner's in-game sprint is
still the visual confirmation of relief pop-in under his render/apply load.
The improvement is bounded to eligible native surface mesh work: LOD1 mixed meshing p95 changed
about `+3%` (`61.820 → 63.597 ms`), but only `7/407` mixed tiles qualified for banding. Flat-column
count was zero. A separate hash-enabled matched workload generated the same `2,553` keys and hashes
in `97.911890 s` native Core versus `201.223904 s` legacy bridge (including region compile: `2.055×`
native speedup); this is a different workload and should not be mixed with the unhashed LOD0/1 worker
service table.
### Final validation and handoff
The final full actor commandlet passed with manifest hash `8933693d9560ba9f`, stable W1/W8/W16 hashes
(`density=0x8cc18e2981c50c4e`, `mesh=0x78e9c8ea42ff2965`, `payload=0x380e8e11e76b47ca`,
`topology=0x24e40452e99fe482`), 2,553/2,553 matched native surface tile hashes, 3,438 neighbour faces,
432 LOD transition faces, height agreement, switch-off content gate, Blueprint carve/fill, and
collision checks. The actor run took `11m04s` and peaked at `3,982,528,512` private bytes (`3.71
GiB`). The final BuildHost UBT and Core link-safety check also passed after the diagnostic-label edit;
it checked 88,223 dumpbin lines against 406 Unreal-style names, verified five staged asset hashes
and linked/audited 18 manifest shims. BuildHost artifacts are in the `Saved/BuildHost` tree linked
above. No after GPU capture was launched.
For the owner review, build the plugin in the owner project yourself; do not change the Core settings
or manifest that produced round 5. Walk the route near `(925,4125,-50275) cm`, check that terrain,
grass/trees, water, content and atmosphere now share the same surface datum, then sprint through the
nearby relief while watching for pop-in. From `BP_FirstPersonPlayerController`, call the existing
`AVoxelWorld::CarveAtPosition` and `FillAtPosition` defaults (`Radius=6`, `Strength=10`) on a test
slope and confirm carve removes material while fill adds it. A null-RHI BuildHost run cannot replace
these rendered checks.
Likely compile touchpoints are `VoxelWorld.cpp`, `VoxelContentManager.cpp/.h`,
`VoxelAtmosphereManager.cpp`, `VoxelMarchingCubesMesher.cpp/.h`, `VoxelForgeCore.cpp`,
`FarShellsInternal.h`, `VoxelForgeExploreCommandlet.cpp`, and `Tools/TestVoxelForgeCoreWorldHost.ps1`.
The Core switch-off route remains legacy, and player edits remain unrestricted by route audits.
### Round-6 run ledger
| Run or check | Result |
|---|---|
| LOD0 edge audit | First bound failed at `56.807 cm > 50 cm`; inspection found skirt edges were included. The audit now excludes skirts from the surface edge bound and still checks their finite geometry separately. |
| First far-shell edge audit | Failed at `72,407.734 cm / 5.657 cells`, exposing the broad LOD8 quad. The shell was replaced by crossing-sampled subdivisions. |
| Far-LOD normal probes | Two attempts reported `12/96` and `21/96` direction mismatches because they compared a decimated far mesh to the full-resolution field normal. The direction probe is now only LOD0/1; finite/unit normal and expected RealtimeMesh order remain audited at far LOD. |
| LOD8 bound tuning | Failed successively at `27,805.769 > 25,600 cm`, `28,876.287 > 28,800 cm`, and `33,914.056 > 32,000 cm` (the last two were different tiles/crossings). Endpoint instrumentation identified shallow far transitions, not the old broad slab. The final far-LOD limit is four coarse cells; final geometry was `2.265` cells. |
| Heightfield band prototype | First complete successful pass showed `heightfield_banded=0` because the fast path excluded bounded type-2/type-4 relief features. Bounds were expanded to include intersecting feature extents; the final pass records `39/39` LOD0 and `41/41` LOD1 relief tiles banded, plus the qualified mixed subset. |
| Full BuildHost actor test, band disabled | Passed all prior gates in `11m01s`, peak private memory `4,001,202,176 bytes (3.73 GiB)`. Its matched Core relief meshing p95 was `60.840/70.297 ms` at LOD0/1. |
| Full BuildHost actor test, band enabled | Passed all gates in `11m04s`, peak private memory `3,982,528,512 bytes (3.71 GiB)`. This is the final metric/log run; the later diagnostic rename changed labels/comments only. |
| Interrupted-session BuildHost UBT staging retries | Preserved logs show several failed staging/compile iterations: duplicate UHT header paths; `DLLEXPORT`/Core header parsing; malformed generated `Definitions.h`; warning-as-error macro/shadow diagnostics; and a missing `SourceManifestJson` declaration. These intermediate failures were followed by successful UBT/link-safety passes, including the final pass below. |
| Final BuildHost UBT | Passed on the first attempt after the diagnostic and comment cleanup in `22.8 s`; link-safety audit and five staged asset hash checks passed. Earlier post-diagnostic UBT passes took `27.7 s` and `40.9 s`. |
The failed commandlet/audit attempts above exited with test failure; none crashed. No
`0xC0000142` retry occurred. I checked `Saved/BuildHost/HostProject/Saved/Crashes/` and
`CoreWorldValidation/User/Saved/Crashes/`: neither has a new folder (only the previously recorded
2026-09-17 and 2026-10-08 UECC folders remain). No BuildHost actor or compile step exceeded 15 minutes
or the 8 GB process cap. No owner project/editor was opened, no owner build or `.uasset` modification
occurred. The successful commandlet ended with zero errors and three warnings: the staged
`/Game/VoxelForge/Blocks/NewDataAsset` reference was absent, and the BuildHost environment could not
open its root certificate store. No capture, commit, push or stash was made.
New source files: none. Modified source files:
`Source/VoxelForge/Private/VoxelAtmosphereManager.cpp`,
`Source/VoxelForge/Private/VoxelContentManager.cpp`,
`Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp`,
`Source/VoxelForge/Private/VoxelWorld.cpp`,
`Source/VoxelForge/Public/VoxelContentManager.h`,
`Source/VoxelForge/Public/VoxelMarchingCubesMesher.h`,
`Source/VoxelForge/Public/VoxelWorld.h`,
`Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp`,
`Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`,
`Standalone/Core/src/FarShellsInternal.h`,
`Standalone/Core/src/VoxelForgeCore.cpp`,
`Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp`, and
`Standalone/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h`. Modified BuildHost test tool:
`Tools/TestVoxelForgeCoreWorldHost.ps1`.