Files
VoxelForge/Standalone/Research/WorldAssemblerM2/REPORT.md
T
Fr0zkaandClaude Opus 5.5 22a6b30494 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
2026-10-10 09:53:04 +02:00

23 KiB
Raw Blame History

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, cost.csv, determinism.csv, and 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.

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.

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.

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.

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 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, BuildSharpMesherSpike.ps1, RunSharpMesherSpike.ps1, and 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.

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 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 and 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.