# World assembler M2 round 2 — standalone border closure Date: 2026-10-10 Status: **same-LOD gate passed; full LOD0/LOD1 gate failed; no production mesher selected.** ## Decision The hybrid now closes the same-LOD tile-border suite: 625/625 cases, with zero unmatched contour probes, open edges, or nonmanifold edges. MC retains its 625/625 pass, and DC also passes as a reference after the same assembly repair. The 2:1 LOD transition is still not ready. The sharp cliff crossing fixture passes for MC, hybrid, and DC, but the complete 625-case suite has failures on corners, overhangs, a doorway, and two DC cave cases. The contact sheet makes the sharp-feature improvement visible, but the failed full transition gate blocks a production choice and an Unreal implementation this round. Keep MC as the baseline and hybrid as the sharp-feature candidate. Keep DC-everywhere as a reference; it remains rougher on the sampled native patch. Continue standalone work on a topology-aware 2:1 transition before taking a candidate into Unreal. ## Harness and gates The standalone C++20 spike uses 25 cm cells and five fixtures: a 90-degree corner, doorway, one-metre riser, cliff ledge/overhang, and analytic cave chamber. Each fixture is translated through 125 lattice phases (0, 5, 10, 15, and 20 cm per axis). The retained outputs are [phase_sweep.csv](results/phase_sweep.csv), [cost.csv](results/cost.csv), [determinism.csv](results/determinism.csv), and [seams.csv](results/seams.csv). Same-LOD neighbors are sampled independently with a shared field halo and deterministic ownership of border quads. The assembled pair is checked after open-edge T-junction subdivision; the subdivision preserves the source triangle footprint. Hybrid/DC independent contours are also compared from temporary witness meshes. No shared surface mesh is clipped and reused for the neighbor comparison. LOD0/LOD1 uses independent 25 cm and 50 cm half-grids at a 2:1 ratio. The spike clips their surfaces at the border and tries a collar/chain zipper; a residual closed seam loop is capped only when its own sampled field residual is within budget. This is a tested transition prototype, not a complete Transvoxel table. The physical error budget is **0.50 m**, one LOD1 sample interval. This is a conservative, camera-independent limit that is easy to audit in the field. It does not establish a screen-space guarantee; round 3 must also check projected error at the target cameras. ## Same-LOD border results Each method has 625 independently generated neighbor cases. “Bad” includes unmatched probes, boundary edges, and nonmanifold edges after assembly. | Method | Bad cases | Unmatched probes | Open edges | Nonmanifold edges | Maximum contour mismatch | |---|---:|---:|---:|---:|---:| | MC | 0 / 625 | 0 | 0 | 0 | 0 m | | Hybrid | 0 / 625 | 0 | 0 | 0 | 0 m | | DC | 0 / 625 | 0 | 0 | 0 | 0 m | The merge-time T-junction conformance added 52 triangles across Hybrid’s suite and 40 across DC’s; MC needed none. Its measured added-triangle field residual is 0 m at the CSV precision. The full per-case data is in [seams.csv](results/seams.csv). ## 2:1 LOD transition results The full gate still fails. Raw fine/coarse contour comparisons stay below the 0.50 m budget, but the transition surface/topology does not close all cases. | Method | Bad cases | Seam-band open edges | Seam-band nonmanifold edges | Worst raw contour mismatch | Worst transition field residual | |---|---:|---:|---:|---:|---:| | MC | 5 / 625 | 15 | 5 | 0.318199 m | 0.250008 m | | Hybrid | 38 / 625 | 4,213 | 47 | 0.158352 m | 1.24975 m | | DC | 40 / 625 | 4,465 | 49 | 0.158352 m | 1.96263 m | Failures by fixture: MC has five doorway cases; Hybrid has 25 corner and 13 overhang cases; DC has those same 38 plus two organic-cave cases. Hybrid/DC transition residuals exceed the 0.50 m budget on the corner cases. Some remaining rows have open or nonmanifold seam edges even when the raw contour mismatch is small. The full CSV keeps pre-transition and internal topology counts separate. The unrestricted-cap diagnostic is preserved in [seams_cap_unbounded_diagnostic.csv](results/seams_cap_unbounded_diagnostic.csv). The dedicated sharp cliff crossing case passes all three methods: zero unmatched probes, open edges, and nonmanifold edges. Its maximum raw contour mismatch is 0.249977 m for MC and 0.419411 m for Hybrid/DC; the transition field residual maxima are 0.233333 m and 0.238888 m respectively. See [lod_cliff_crossing.csv](results/lod_cliff_crossing.csv). The remaining Unreal-only checks are actual `UBodySetup` collision cooking/contact, projected pixel error at the game cameras, and runtime task scheduling/upload against the plugin mesher. None of those can repair the standalone topology failures; the topology gate is still the reason to defer Unreal integration. ### Production skirt baseline The current plugin skirt depth is `max(1, SkirtCells) × Step × GeometryVoxelSize`; skirts are normal-extruded, double-sided visual strips. With Core’s 25 cm geometry voxel size and the default `SkirtCells = 4`, nominal depth is 1 m at LOD0 and 2 m at LOD1. Project settings can override these defaults. Skirts hide many visual gaps but do not make the neighboring surface topologically watertight. The new transition has not yet replaced that production behavior. ## Surface residual and crease splits Residual is the absolute field value sampled at triangle vertices, edge midpoints, and centroids; it is not a symmetric Hausdorff distance. Values below average the 625 phase rows per method; maxima are the worst sampled value. | Method | Mean residual | Worst residual | Tagged crease pairs | Normal split pairs | Split rate on tagged creases | Mean triangles / vertices | |---|---:|---:|---:|---:|---:|---:| | MC | 0.5664 cm | 16.6670 cm | 10,926 | 1,847 | 16.90% | 3,363.1 / 1,728.8 | | Hybrid | 0.2552 cm | 12.5027 cm | 140,876 | 140,806 | 99.95% | 3,365.3 / 3,469.2 | | DC | 0.2786 cm | 12.5027 cm | 140,876 | 140,806 | 99.95% | 3,365.8 / 1,985.6 | The hybrid keeps untagged/organic components on MC and uses DC for tagged sharp features. It preserves the sharp corner/riser/ledge better than MC in the analytic fixtures, while its feature splits roughly double the vertex count. ## Sampled native SurfaceWorld cliff The native comparison samples the 8 m patch at 25 cm from the M1 SurfaceWorld manifest (feature type 4, feature `0x9812c26310b90c9`, manifest hash `0x8933693d9560ba9f`). MC and Hybrid emit bit-identical vertex and triangle arrays, with identical mesh hash `0x9d58e0603f1bf9c7`: 2,345 vertices and 4,496 triangles each. This confirms that the hybrid leaves this untagged native patch unchanged. The sampled scalar-field residual is 0.212167 m mean and 16.0068 m max for both MC and Hybrid. This patch is cropped at the 8 m volume boundary and has 192 boundary edges; these values are not a seam result, and the scalar field is not a signed-distance guarantee. DC-everywhere differs (2,247 vertices, 3,961 triangles, 4.57272 m mean / 16.5898 m max, 504 boundary edges and 74 nonmanifold edges). The native CSV is [native_surface.csv](results/native_surface.csv). ## Cost and determinism The timer starts after the 32³ field grid is sampled, measures meshing only, and excludes Unreal scheduling, upload, collision cooking, and rendering. Values below are the median of five fixture medians; W1/W8 use three builds, while W16 uses one timed build because repeated W16 thread creation had caused a host access violation. Hash determinism is still checked at all three worker counts. | Workers | MC ms/tile | Hybrid ms/tile | DC ms/tile | |---:|---:|---:|---:| | 1 | 16.316 | 17.534 | 16.417 | | 8 | 53.914 | 54.840 | 52.606 | | 16 | 86.961 | 90.490 | 89.476 | All 45 W1/W8/W16 rows match the W1 hash for that method and fixture. The standalone collision proxy copies the render mesh: all hashes match and measured geometry delta is 0 m. This does not test Unreal collision cooking. These timings are **not comparable to the optimized production MC mesher**. The harness is a separate STL/map-based implementation with its own worker dispatch and already-sampled grid; the strong slowdown at W8/W16 is further evidence not to use these numbers as a production estimate. They are useful only for comparisons within this harness. ## Contact sheet The [updated offline contact sheet](results/captures/contact_sheet.jpg) is 864 × 1,968 pixels. It contains the five analytic fixtures, the sampled SurfaceWorld cliff, and both LOD seam views, with MC/Hybrid/DC columns. The 24 individual JPEGs are 288 × 246 pixels; all 25 files including the sheet are below 300 KB. No viewer or GPU path was used. ## Round-2 recommendation and proposed round-3 work (historical) At the end of round 2, the recommendation was not to select or enable the hybrid in the owner project: same-LOD passed, but the full LOD gate did not. Round 3 later narrowed the policy to LOD0 interior feature cells with MC at borders and coarser LODs; the implementation and its remaining transition limitation are recorded below. The following list preserves the original round-2 plan. When the standalone LOD gate passes, round 3 should: 1. Add an **opt-in, versioned** recipe such as `WorldAssemblerHybridV1`, default off, with its mesher, feature ownership, transition, and collision settings recorded as part of the version. Keep the existing Core recipe and default hashes unchanged. 2. Add an Unreal test map with the five analytic fixtures, the sampled SurfaceWorld cliff, and a native Core cave tile. Include independent same-LOD neighbors and a sharp cliff crossing from LOD0 to LOD1. Compare skirts on/off and use matched cameras plus wireframe views. 3. Cook collision through the real Unreal path. Wait for cooking completion, then run capsule sweeps/walk tests over the doorway, riser, cliff underside, cave wall, and both borders. Check collision/render agreement and recooking after a local edit. 4. Record mesh and collision cook latency, CPU/memory, triangle/vertex counts, LOD error in pixels at the fixture cameras, and player contact results. Keep MC as the direct baseline and preserve a one-switch rollback to the existing recipe. ## Run ledger and scope | Run/check | Result | |---|---| | Final MSVC C++20 build | Passed in 5.44 s, BelowNormal, affinity `0xFF00`. | | Full seam run with unrestricted residual-loop caps | Completed in 602.78 s; 83 LOD cases failed. Preserved as `results/seams_cap_unbounded_diagnostic.csv`. | | Final full seam run with 0.50 m cap-residual guard | Completed in 601.31 s; same-LOD passed 625/625 for all methods, LOD failures were MC 5, Hybrid 38, DC 40. No crash or guard trip. | | Cost/phase/native analysis | Completed in 120.95 s; 1,875 phase rows, 45 determinism rows, native patch comparison. | | Final offline renderer | Passed in 3.14 s; 25 JPEGs under the 300 KB cap. | | Earlier full seam variants | Completed in 559.32 s and 566.44 s. Both failed earlier same-LOD Hybrid/DC cases (36/625 each); superseded by the final T-junction conformance pass. | | Focused transition prototypes | Completed within seconds. Earlier MC doorway variants left 64, then 3–4 open edges; whole-mesh endpoint snapping introduced nonmanifold edges. Those variants were rejected. The bounded residual-loop cap closes this focused doorway case, but the full LOD matrix still fails. | | Earlier native halo diagnostic | An initial process exited `0xC0000005`; an AddressSanitizer launch first failed `0xC0000135` because its runtime DLL was unavailable. After using the Visual Studio runtime path, ASan found the halo index overflow; the indexing was fixed, and subsequent harness runs completed. | | W16 timing diagnostic | Repeated creation/timing of 16 OS workers caused a host access violation. W16 now uses one timed build; hash determinism remains checked. | | Aggregation tools | Two PowerShell summary attempts had query errors, including a missing `vertices` field in `phase_sweep.csv`; corrected queries ran without launching a mesher. No `0xC0000142` retry was needed. | Every final build, mesher, analysis, and renderer process ran BelowNormal with affinity `0xFF00`; the scripts enforce an 8 GB private-memory limit and 15-minute timeout. Peak observed mesher private memory was under 0.2 GB. No final process crashed, exceeded a limit, or needed a retry. For round 2, all edits and outputs were under `Plugins/VoxelForge/Standalone/Research/WorldAssemblerM2`, plus its dialogue log. No `Source/` file, Core source, owner project file, `.uasset`, or editor was touched in that round. No commit, push, or stash was made. ## Round-2 files changed and new source files Updated existing standalone files: [SharpMesherSpike.cpp](SharpMesherSpike.cpp), [BuildSharpMesherSpike.ps1](BuildSharpMesherSpike.ps1), [RunSharpMesherSpike.ps1](RunSharpMesherSpike.ps1), and [RenderSharpMesherCaptures.py](RenderSharpMesherCaptures.py). Updated this report and appended the M2 round-2 dialogue entry. **New source files this round: none.** CSVs, OBJ meshes, JPEG captures, logs, and local build outputs are generated research artifacts; none were committed. ## Round-3 Unreal integration (2026-10-10) The round-3 proposal is now in the plugin Core route as the versioned, opt-in `WorldAssemblerHybridV1` recipe. `bUseVoxelForgeCore` remains off by default, and the Core recipe defaults to `MarchingCubesV1`. The hybrid only consumes tagged type-2 world-plan feature boxes at LOD0. Cells touching a tile border and every LOD1+ cell use MC; existing border skirts remain. The selected recipe participates in Core content identity only when selected, so the disabled path and the default MC path retain their existing identities. ### BuildHost evidence The latest BuildHost UBT build and link-safety audit passed after the collision diagnostic was changed to compare the runtime component trace and cooked-body bounds. The full null-RHI actor commandlet ran for 11m00s and logged `pass complete=1`, `Success - 0 error(s)`. The PowerShell wrapper then returned 1 while formatting its final status because it cast a `SwitchParameter` to `Int32`; that print now uses the numeric fixture flag already computed by the script. A focused run after this correction returned 0. The corrected full actor wrapper was then rerun and returned 0: 11m06s, peak sampled private memory 4,007,432,192 bytes (3.73 GiB), `pass complete=1`, and zero engine errors. The final log is [`CoreWorldValidation.log`](../../../Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/CoreWorldValidation.log). W1/W8/W16 remained bit-stable at the round-6 Core hashes: density `0x8cc18e2981c50c4e`, mesh `0x78e9c8ea42ff2965`, payload `0x380e8e11e76b47ca`, topology `0x24e40452e99fe482`. The actor passed initial streaming, anchor movement, edit, height agreement, switch-off content gating, Blueprint carve/fill, surface travel, and native SurfaceWorld travel. On the surface route, all 2,553 desired visual tiles matched; 573 local tiles, 3,438 neighbor faces, and 432 LOD transitions were checked. The actor run used about 2.8 GiB private memory. The only UECC directory is the older 2026-09-17 folder; no round-3 process crashed or retried on `0xC0000142`. The hybrid selects triangles from the existing field and does not change field density, route constraints, or signed-density bounds. This actor rerun passed height agreement, plan import, anchor/edit/travel, switch-off gating, and Blueprint carve/fill. The last recorded full standalone field audits remain the M1 round-4 results: 298 bounds / 8,046 samples with zero misses or unknown ranges; 29,929 half-voxel shelf columns with zero sub-voxel runs; and a 530-center, 143,630-point route sweep with zero blocked, unsupported, or grade-failing samples. Their negative controls were detected. Those standalone audits were not rerun in round 3; the fresh route-related evidence is the BuildHost actor travel/height pass above. The focused production-mesher check passed all five fixture repeats. The separately staged native SurfaceWorld type-4 cliff tile at `(-22,13,-58)` also passed a BuildHost MC/hybrid bit-identity check: 10,231 vertices, 10,360 triangles, mesh hash `0xfc7bd7a3cf997992`. The full Core actor session ran the TunnelNetwork cave path; its organic cave chamber fixture stayed on MC and produced the same 2,408 triangles as MC. Same-LOD borders are generated independently and pass with 63 MC segments and 63 hybrid/fallback segments. The 125-phase LOD0/LOD1 doorway probe had zero cases and zero probes above 50 cm; its worst measured contour mismatch was 22.844 cm. This does not close the known MC topology failures: the standalone full transition matrix still has five failed doorway cases out of 625. This policy keeps the existing skirts as the repair and leaves a true transition template for later work. At LOD0, the measured 17.68 cm corner bevel is 0.35 of the 50 cm LOD1 sample. It may round off and pop at the LOD threshold. Null RHI provided no projected-error or live visual measurement; the owner must judge it at the target camera and movement speed. ### Cooked collision contacts All five fixture contacts hit the actor's Pawn channel and the target RMC component. The actor's Core render hash matched the standalone tile, render and cooked Chaos body bounds matched, and the direct component trace differed from the world trace by 0.000 cm. Density-field contact error was within one 25 cm Core voxel: | Contact | World position (cm) | Field contact error | |---|---:|---:| | Corner top | (1025, 4150, −50127.64) | 20.332 cm | | Doorway lintel | (1350, 4150, −50068.55) | 5.951 cm | | Riser face | (1350, 4600, −50257.71) | 0.000 cm | | Ledge top | (1450, 4600, −50140.14) | 8.956 cm | | Ledge underside | (1450, 4600, −50190.14) | 13.262 cm | ### Production mesher cost These BuildHost timings compare the plugin's production MC and hybrid routes after field sampling. They measure meshing, not render upload or collision cooking. | Fixture, 32³ cells | MC | Hybrid | Hybrid / MC | Triangles (MC / hybrid) | |---|---:|---:|---:|---:| | 90° corner | 2.246 ms | 15.048 ms | 6.70× | 3,196 / 3,200 | | Doorway | 1.819 ms | 11.538 ms | 6.34× | 1,724 / 1,712 | | 1 m riser | 1.979 ms | 11.974 ms | 6.05× | 2,108 / 2,112 | | Cliff ledge | 2.270 ms | 12.839 ms | 5.66× | 3,132 / 3,128 | | Organic cave | 2.106 ms | 2.089 ms | 0.992× | 2,408 / 2,408 | Sharp-tagged fixtures cost roughly 5.66–6.70× MC in this plugin harness. The untagged cave is effectively unchanged. This is a substantial local CPU cost where the tagged feature path runs. ### Owner hand-off and source inventory In the owner project, enable `Use VoxelForge Core` on the `AVoxelWorld` and set `Core Mesher Recipe` to `World Assembler Hybrid V1`. Both switches remain opt-in; the default recipe is MC. To preview the surface-strate placements, duplicate the settings asset, import [`surface_hybrid_fixture_plan.json`](round3/surface_hybrid_fixture_plan.json) through `SourceWorldAssemblyManifestJson` / `ImportWorldAssemblyManifestJson`, then visit the corner, doorway, riser, and ledge around the coordinates in the contact table. The plan carries nine stable type-2 feature boxes and retains the sampled type-4 cliff. The owner should review the corner and doorway as distance increases and cross the LOD threshold with skirts visible. No owner asset was edited; all checks here used BuildHost with null RHI, so no visual capture was produced. New source files this round are the mirrored private mesher headers [`VoxelHybridSharpMesher.h`](../../../Source/VoxelForge/Private/VoxelHybridSharpMesher.h) and [`VoxelHybridSharpMesher.h`](../../Source/VoxelForge/Private/VoxelHybridSharpMesher.h). No new `.cpp` or generated header was added. Likely compile touchpoints are `VoxelWorld.h/.cpp`, `VoxelTypes.h`, `VoxelMarchingCubesMesher.h/.cpp`, both hybrid mesher headers, `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`, `Standalone/Core/src/VoxelForgeCore.cpp`, `VoxelForgeExploreCommandlet.cpp`, and `VoxelForgeEditor.Build.cs`. ### Round-3 failed-run ledger Times below are BuildHost log stamps. Repeated attempts are listed individually; each was null-RHI BuildHost work. The final passing commandlet and focused checks are described above. | Run(s) | Result and correction | |---|---| | 02:32:40, 02:45:03 | The exact-output check failed for the tagged corner (`stable=1`, `expected_mc_identity=0`). Corrected the fixture assertion; the final five-fixture repeat check passes. | | 03:00:55, 03:04:08 | The doorway probe compared the full coarse span with the fine span and reported 125/125 over-budget cases, worst 800.047 cm. Restricted comparison to their shared overlap; the final 125-phase probe is below budget. | | 03:06:12, 03:24:05, 04:23:42 | Focused five-fixture checks passed. | | 03:19:40 | Actor collision contact fixture failed before it recorded a corner contact. | | 03:35:38, 03:52:53, 04:08:52, 04:21:48, 04:35:15, 04:51:17, 05:09:02, 05:22:12, 05:35:24, 05:52:51 | Actor checks exposed misses in the old pawn/visibility/closest-triangle diagnostic. Replaced that test with a direct RMC component trace plus cooked-body/render-bounds comparison. | | 06:13:08 | Actor process hit the 900-second timeout at about 3.95 GiB and was stopped. No crash folder appeared. | | 06:19:47 | Geometry audit included an injected synthetic fixture tile and rejected its 51.570 cm edge. Excluded injected fixture tiles from the natural-terrain scale audit. | | 06:32:52 | Pawn/capsule hits worked, but the riser expected point was 100 cm from the actual contact (corner error 20.332 cm). Corrected the fixture anchor. | | 06:48:33 | Pawn contacts worked, but the standalone closest-ray helper reported no visual hits; riser anchor was still offset. Replaced the unreliable helper and corrected the anchor again. | | 07:03:11 | Double-precision triangle-ray helper still reported no visual hits; the riser target at x=1,250 cm missed the actual x=1,350 cm contact. Replaced this check with component tracing and render/cooked bounds. | | 07:05–07:17 | A duplicate wrapper launch was refused because BuildHost PID 5616 was already active; its command line was verified as the requested null-RHI test and monitored. That actor run failed on the stale visual-ray helper and riser anchor. | | Before final UBT pass | One compile failed because commandlet checks referenced `ReferenceWorld`, `TravelOptions`, and `CoreError` after their scope. Moved the check into scope; the next BuildHost UBT and link audit passed. | | 07:22–07:33 | Full actor commandlet completed all assertions with `pass complete=1` and zero engine errors, but the wrapper returned 1 on its final `[int]$EnableHybridFixturePlacement` formatting cast. Fixed the status print to use the existing numeric flag; focused fixture and cliff wrappers then exited 0. | | 07:40–07:51 | Reran the full actor path after the wrapper fix. Commandlet and wrapper both passed in 11m06s; peak sampled private memory was 4,007,432,192 bytes (3.73 GiB), and the engine reported zero errors. | No new UECC folder, process crash, or `0xC0000142` retry occurred in round 3. No owner project/editor was opened or built; no `.uasset` was edited; no commit, push, or stash was made.