BuildLinux.sh hashes a sorted manifest of compiled inputs into VOXELFORGE_SOURCE_STAMP, printed first by every executable; Standalone/Tools/verify/verify.py checks it before and after each tier and records binary/input hashes and the resolved recipe, style map and layout. Fast (163 s) runs on the canonical owner world only and asserts FI Style 2 / SW Style 1; medium (8 min) and full (26 min) add the style matrix, GCC, determinism and cost. Controls: stale source, mislabelled style-0 owner, all-black capture, L30/L31, L35/L36 and the L38 step are all rejected. No geometry change; owner hashes identical to L37. Research R6: leak table, generator contract and a 10-step metre-native migration plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
14 KiB
Research R6 — director, generators, and mesher in metres
Read-only review of the current checkout and L20/L21/L22/L29, R2, and R5 notes. No builds or tests were run.
R2’s layout-chunk split is already resolved in the checked source. AttachValidatedWorldAssembly computes one physical LayoutChunkWidthSubvoxels from StrateLayoutChunkSizeMeters, passes it to both BuildSurfaceStratumPlan and BuildFloatingIslandStratumPlan, and writes the same manager/dispatch width into the resolved snapshot (L22). Do not apply the FI owner/Core ratio to layout bounds again. FI still uses that ratio to convert its generator parameters and passage dimensions from owner voxels; that separate authoring-unit leak remains.
1. Current leaks
The closest current match to the three layers is: director work in VoxelStrateManager.cpp::Initialize and VoxelForgeCoreAdapter.cpp::AttachValidatedWorldAssembly; field evaluators in Heightfield2D::point, FloatingIslandFieldV1::densitySubvoxels, and legacy UVoxelGenerator::GetDensityAt; shared composition in FStandaloneShapeWorld::ApplyResolvedStrateComposition; extraction in World::generateTile and UVoxelMarchingCubesMesher::GenerateMesh. VoxelWorld::BuildDesiredTiles is the scheduling side of the director. The table lists where these boundaries still cross.
| Leak | Current location and consequence |
|---|---|
| Director lookup inside field evaluation | Standalone/Core/src/ShapeWorldInternal.h::FStandaloneShapeWorld::DensityAt, NativeSurfaceDensityAt, NativeFloatingIslandDensityAt, and ClassifyBoxDetailed query UVoxelStrateManager and select native SurfaceWorld, native FloatingIslands, or legacy evaluation while workers sample. Source/VoxelForge/Private/VoxelGenerator.cpp::UVoxelGenerator::GetDensityAt also routes each sample through manager type/params. The resolved layout exists, but is not yet the sole dispatch input. |
| No common generator API/format | Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h::Heightfield2D and FloatingIslandFieldV1 have distinct recipe/evaluator APIs; the legacy path in VoxelGenerator.cpp, operator stacks, and morphology is a third. FStandaloneShapeWorld is their adapter and shared composition path at once. A new generator cannot currently implement one common contract. |
| Units cross boundaries repeatedly | VoxelStrateDefinition.h::StrateHeightInChunks, VoxelSettings.h::InterStrateGapChunks, and many VoxelStrateTypes.h shape/passage values are authored in chunks or voxels. VoxelStrateManager.cpp::Initialize converts metre chunk width to owner voxels; VoxelForgeCoreAdapter.cpp::WorldAssemblyFixed packs values as 1/256 Core voxel; BuildFloatingIslandStratumPlan and AttachValidatedWorldAssembly use owner/Core ratios for FI dimensions and passages. Snapshot bounds are micrometres, but recipe anchors are still labelled 1/256 Core voxel, and voxelSizeMicrometres is currently filled with the same Core size for every strate. VoxelStrateDefinition.h still documents 16 chunks as 64 m at 4 m/chunk although the current 8 m setting makes that 128 m. |
| Generator policy remains in shared composition | ShapeWorldInternal.h::ApplyResolvedStrateBoundaryBand branches on generator-kind strings for FI air-gap and surface/landing behavior; the density/classification functions above branch on generator types. These exceptions should become explicit resolved interface or generator capabilities, not string tests in the common evaluator. |
| Route and passage logic is only partly shared | VoxelForgeCoreAdapter.cpp::BuildFiniteSurfaceRoute plans the SurfaceWorld route. BuildFloatingIslandStratumPlan embeds passage/spiral dimensions into the FI anchor root. ResolvedLayoutInternal.h::PlanResolvedLayoutPassagesV1 still tests SurfaceWorld and FloatingIslands to place mouths and handle the FI root; ShapeWorldInternal.h::ApplyResolvedStratePassageCarves applies the shared carve but also contains route/support policy. Legacy passage behavior remains in UVoxelGenerator::GetDensityAt. A new generator should not need a route implementation. |
| Tile builder knows generator recipes | Standalone/Core/src/VoxelForgeCore.cpp::World::generateTile scans SurfaceWorld feature IDs 2/4, refines their resolution, limits a native SurfaceWorld density band, and selects/skips far-horizon repair based on SurfaceWorld/FloatingIslands plan presence. The generic mesher should receive bounds, surface hints, and a sampling request, not recipe IDs. |
The shared ceiling/content/bedrock records and Hard/Gradient/Interleaved evaluation are real foundations (ResolvedLayoutSnapshotV3, ApplyResolvedStrateBoundaryBand, L29). Remaining work is to make their resolved physical records authoritative and remove generator-name exceptions. The mesh-side boundary floor/ceiling patches in VoxelForgeCore.cpp/FarShellsInternal.h still duplicate part of that surface policy.
2. Generator contract
The director resolves an immutable world snapshot before jobs are queued. A generator is constructed once for a resolved strate and receives:
- Stable world seed, strate ID, generator/recipe/schema/evaluator versions, and that strate’s immutable parameters.
- Physical half-open bounds
[bottom, top)and lattice origin in integer micrometres; the resolved ceiling/content/bedrock and adjacent-interface records that touch the strate. - Its own
voxelSizeMicrometres(25 cm by default), independent of the 8 m layout chunk and of the tile key. The request gives the physical query bounds plus the mesher’s exact halo. LOD may guide work, but the same physical coordinate and recipe must return the same field value at every LOD. - The shared deterministic library: versioned noise, fixed-point conversion, common distance/shape helpers, and conservative bounds utilities. Resolved passage descriptors and style are consumed by shared composition; the generator need not plan or carve the inter-strate route.
Concrete evaluator shape: densityAt(xUm, yUm, zUm) -> signed field value, with one documented convention (recommended: negative is solid, zero is the surface, positive is air). Keep magnitude units stable and documented; metre-scaled signed distance is the target for shared band composition. Provide a conservative support/influence AABB or “unbounded/unknown”; an interval/classification for a requested AABB (solid, air, mixed, or unknown); and an optional detail envelope (smallest safe sample spacing). A SurfaceWorld-like generator may also return semantic ground/cap surfaces in metres. Caches and feature tile lists may accelerate these queries, but are not part of output identity.
A generator must never query the manager, UObject state, chunk index, season, or a neighbour while evaluating; choose strate bounds, gaps, transitions, bedrock/ceiling ownership, or route geometry; mutate input state; generate triangles; or vary a field by tile order, worker, or LOD. Unknown proof means evaluate, never skip. The director selects the evaluator once; shared composition applies the interface bands, passages and edits; the mesher consumes the composed field and generic hints. This is enough for TunnelNetwork or a cavern generator to be added without edits to the director’s sampling loop or mesher.
3. Ordered migration
“No” means existing-world density/mesh/output hashes stay identical. A new schema may change manifest identity only when deliberately versioned; do not hide such a change behind a claimed pure refactor. Size estimates are one engineer’s rough implementation effort: S 1–2 days, M 3–5 days, L 1–2 weeks.
| Step | Change and verification tier | Changes output? | Main risk | Size |
|---|---|---|---|---|
| 1 | Freeze the current source stamp, canonical owner FI Style 2/SW Style 1 resolved snapshots, and existing hashes as the comparison set. Use R5 fast to establish the baseline; medium/full remain promotion gates. | No | Incorrect/stale binaries or mislabeled style invalidate all comparisons. | XS |
| 2 | Add a common immutable field interface and wrap the current SurfaceWorld, FloatingIslands, and legacy evaluators without changing their arithmetic. Bind the evaluator once per resolved strate/tile, not via a manager lookup per density sample. Run fast hash parity; medium before merge. | No; hashes must match. | Virtual/function dispatch cost; accidentally changing float/fixed-point call order. | M |
| 3 | Make the resolved metre layout the sole runtime authority: resolve height, gap, transition width, band bounds, seed, and passage settings before scheduling. Keep chunk indices only as a compatibility projection. Convert the existing 8 m chunk and L29 bands exactly. R2’s FI/SW split is handled here by the already-present common layout width; retain owner/Core ratios only in the legacy FI parameter adapter. Run fast + medium (layout, transition, passage checks). | No for equivalent existing layouts; hashes must match. | Negative-Z floor/rounding, old season snapshots, adapter and manager disagreeing about interval edges. | M |
| 4 | Introduce metre-valued authored height/gap/transition/passage dimensions and a per-strate voxelSizeMeters default of 0.25. Add a versioned conversion from old chunk/voxel assets based on each legacy route’s established unit; do not assume the source unit is recorded on the asset or reinterpret old numeric values as metres. Keep 8 m layout chunk independent. Fast + medium for old assets; full when enabling non-default resolutions. |
No for converted old assets. Yes for changed metre values or voxel size; bump layout/recipe/evaluator identity. | Asset migration and resolution-dependent detail; changing 25 cm to 12.5 cm can mean about 8× samples per physical volume. | L |
| 5 | Route all new and existing passage requests through resolved per-strate configs and a shared route plan. First copy existing SW/FI route anchors exactly; replace FI-root and SurfaceWorld placement branches with explicit resolved anchor/surface capabilities in follow-up slices. Run fast + medium passage matrix and route parity. | No for exact route replay; hashes must match. New geometry/style semantics are Yes and need a generator/layout version bump. | Walkability/support, deterministic placement, route planning cost. | M |
| 6 | Move remaining boundary exceptions into explicit interface records/capabilities. Keep ceiling → content → breakable bedrock per strate and the upper strate’s Hard/Gradient/Interleaved rule. Preserve L29 samples and passage-mouth fade exactly. Run fast + medium transition, hole, and passage gates. | No; hashes must match. | Interface edge ownership and a false solid/air shortcut can open a hole. | M |
| 7 | Standardize culling outputs: conservative support bounds, interval certificates, unknown fallback, and detail envelope. Adapt existing field proofs first; replace type switches only when the new proof matches brute-force/classification controls. Run fast, then medium proof negatives and cost pairs. | No; hashes must match. | Unsound certificates skip density work; per-voxel bounds calls hurt performance. | M/L |
| 8 | Make World::generateTile and the mesher consume generic feature boxes, semantic surfaces, field bounds, and sample-spacing hints. Move current SurfaceWorld refinements behind capabilities while keeping their algorithms byte-for-byte. Run fast + medium ring/shape checks. |
No for dispatch-only refactor; hashes must match. | Cross-LOD seams, tile halos, mixed-resolution tile boundaries, Unreal mesher assumptions. | L |
| 9 | Add an opt-in metre-native TunnelNetwork/cavern recipe through the contract and generic mesher interface. Run medium on the new recipe and R5 full before enabling it in owner layouts. | Yes for worlds selecting it; bump that generator’s recipe/evaluator version. | New density cost and output identity. | M/L |
| 10 | Implement the L20 shared, LOD-stable surface/transition representation to remove measured ring ledges. Run medium ring/shape checks and R5 full on the final tree. | Yes; bump the mesher/layout version and establish new golden hashes. | Surface topology and cross-LOD transitions change; coarse sampling may erase thin bands. | L |
R5’s draft timings are about 3–4 min fast, 8–10 min additional medium, and 32–34 min full, excluding builds. Run fast for every step: output-neutral slices must match existing hashes; output-changing slices must match their new versioned golden hashes. Medium is required before promoting layout, route, culling, or mesher changes. Full is final-source evidence after output-changing versions and golden hashes are reviewed. Include the actual owner configs and assert their resolved styles, as R5 requires; do not treat style-override fixtures as owner evidence.
Cross-cutting risks
- Determinism: convert metres to integer micrometres once; use a specified tie rule and mathematical floor for negative coordinates; hash resolved parameters, voxel size, generator versions, and interface records in stable order. Keep floating-point contraction/platform checks for the compatibility evaluator.
- Per-voxel cost: eliminate manager/recipe selection from the hot call, pre-resolve field objects and tile candidates, and keep unknown proofs conservative. Benchmark actual FI2 and SW1 with R5 cost pairs at medium/full. A finer voxel size is cubic work growth, not a cheap resolution toggle.
- Unreal adapter:
VoxelForgeCoreAdapter.cppcurrently serializes UObject parameters into Core fixed units; new reflected metre fields need versioned asset migration and UHT/UBT validation. R2 also recorded that recent adapter changes had no Unreal build evidence. Static research cannot establish that compile boundary.
Open question for the owner
Should existing voxel-authored assets retain their current physical dimensions through a per-generator compatibility conversion? Recommended: yes, then author new recipes in metres. Reinterpreting old values at 25 cm would rescale worlds and require broad output-version bumps.