Files
VoxelForge/Standalone/SHARED_CPP_LIBRARY_PLAN.md
T
Fr0zkaandClaude Opus 5.5 557f6aac30 standalone: VoxelForgeCore library -- the fast generator behind a clean C++20 API, output unchanged
- Standalone/Core: public header VoxelForgeCore.h (no Unreal types), static library target,
  ResolvedWorldSnapshot (from the schema-v1 JSON), World (compile, generateTile, density/sign,
  point/segment solid, classifyBox), host-supplied JobScheduler (the core starts no threads),
  sequenced, idempotent EditTransaction returning affected tiles per LOD.
- The viewer, benchmarks, edits and audits run on Core; StreamingWorld stays host code.
- Same as round 11: 11 hash rows at W1 and W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six fixed captures byte-identical; cold W8 1.025 s vs 1.041 s reference.
- SHARED_CPP_LIBRARY_PLAN.md rewritten for a UBT module compiling the same sources via shims.
Open issue before Unreal: Core's implementation still compiles the Compat stand-ins named like
Unreal types (TArray, FVector...); inside a UE binary that is an ODR clash. Must be namespaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 11:39:02 +02:00

9.3 KiB
Raw Blame History

VoxelForgeCore shared-library plan

This plan describes the extracted shape-world pipeline and the next Unreal integration. The standalone implementation is under Standalone/Core; no file in the plugin's Source tree was changed.

Current layout and build

  • Core/include/VoxelForgeCore/VoxelForgeCore.h is the public C++20 API. It includes only standard C++ headers and value types.
  • Core/src/VoxelForgeCore.cpp adapts the copied density evaluator and marching-cubes mesher to the public API. ShapeWorldInternal.h owns shape compilation, density/detail evaluation, edits and audits. FarShellsInternal.h owns far-parent repairs and strata/connector shells.
  • Core/core_sources.txt is the translation-unit manifest. Build.ps1 reads it for both -Target Core and -Target All; the latter links the viewer against Build/VoxelForgeCore/VoxelForgeCore.lib. Use -OutputName to keep another executable open. Builds use /std:c++20 /O2 /arch:AVX2 /fp:precise /MP8, BelowNormal priority and affinity 0xFF00.
  • Source/StandaloneShapeWorld.h and Source/StandaloneResearchTunnels.h are forwarding headers for old standalone include sites. StreamingWorld.h remains host code: it owns worker threads, queued jobs, caches, cancellation, D3D upload state and per-frame upload budgets. When a core world is supplied, it gets desired tiles and their priority from the library and submits each tile to World::generateTile. Shape-world --stream-benchmark, --stream-pipeline, edit, audit, and --benchmark-level runs use the Core world. Explicit evaluator and owner-comparison modes retain the compatibility generator only as the comparison side of those diagnostics.
  • The build manifest compiles the copied CPU generator and mesher with the private compatibility headers under Compat. Those headers and their Unreal-shaped stand-ins are implementation details. They are absent from the public include tree and public API.

API and ownership

ResolvedWorldSnapshot::FromJson takes the normalized UTF-8 schema-v1 JSON produced by the standalone owner export. It resolves the input into owned read-only data. World owns the compiled shape field and edit state for that snapshot. See Core/API.md for signatures, units, and examples.

The host supplies a JobScheduler. The core calls its synchronous parallel_for during shape compilation; the host chooses the pool, fibers, task graph or serial execution. Core code starts no threads. After compilation, hosts may run independent tile generation and query calls on their own workers. Serialize compilation and edit application against reads/generation of the same World; there is no internal lock or cancellation policy.

Tile meshes own positions, normals, UVs, colors and indices. Positions are centimetres; query and tile coordinates are voxels. Group counts expose the ground index run, ceiling index run, and skirt triangle subset. Density/sign, point-solid, segment-solid, and tile-classification queries support host gameplay and collision checks without returning engine objects.

Edit transactions contain an authority-supplied increasing sequence and an ordered brush list. The core hashes those exact values, accepts an identical replay idempotently, rejects sequence reuse with different data and out-of-order transactions, and returns affected desired tiles grouped by LOD. The host schedules generateTile for those keys and drops results made stale by its own cancellation/version policy. Every client must apply the same sequence and brush order.

Unreal integration: UBT module

Add a VoxelForgeCore runtime module to the plugin. Its Build.cs sets CppStandardVersion.Cpp20, exports only Standalone/Core/include, and keeps these private include roots available to the core implementation:

  • Standalone/Core/src
  • Standalone/Compat
  • Standalone/Source
  • Standalone/Source/VoxelForge/Public
  • Standalone/Source/VoxelForge/Private

UBT discovers translation units under a module's Source tree. Add one thin .cpp include shim per row of Core/core_sources.txt, with each shim including exactly one canonical source file by a relative path into Standalone. Keeping one included source per shim preserves the translation unit boundaries used by Build.ps1. A manifest check in the UBT module should fail when a manifest row lacks a shim or a shim names a different file. Do not copy implementation files into the module. The standalone and UBT builds then compile the same source bytes.

The existing VoxelForge module owns the adapter and calls the new module. Its adapter runs on the game thread, resolves soft references and asset defaults, preserves array order, and serializes settings, biome and terrain-op records, strata layouts, passages, seed, voxel size, clip limits and viewer start into the exact schema-v1 document accepted by FromJson. It must not serialize UObject pointers or depend on unordered map iteration. Snapshot creation returns a descriptive error for unresolved or unsupported assets. Once created, workers only see the immutable core snapshot and standard C++ results.

The plugin supplies a JobScheduler backed by its chosen task system. The adapter submits shape build work and tile jobs to that scheduler, observes generation IDs for cancellation, and discards stale results. It copies completed TileMesh data into FVoxelMeshData or the plugin's current mesh payload on the host side, preserving centimetre positions, normals, UVs, colors, indices and ground/ceiling/skirt group counts. UObject creation, collision cooking, render-resource changes, game-thread application, residency and upload budgets remain in the plugin.

What the plugin pipeline replaces

The current plugin's AVoxelWorld::GenerateTileResult worker path performs proofs and UVoxelGenerator::ClassifyTile, calls UVoxelMarchingCubesMesher::GenerateMesh, builds RealtimeMesh streams with BuildTileStreamSet, then publishes an epoch-tagged FChunkResult for game-thread upload/collision handling. AVoxelWorld::CarveAtPosition, CarveBox, and CarveCapsule route soft FVoxelModification brushes through UVoxelDiffLayer::ApplyModification and remesh the affected chunks. The adapter replaces those CPU-field and edit operations as follows:

Current call or state Core API replacement Keep in the plugin
Shape interval/detail-band build and any active shape-world dispatch World::compile with explicit bounds/detail options Asset resolution, build admission and cancellation
GetDensityAt, lattice sign tests, and point/segment checks in meshing/gameplay densityAt, densityAtLattice, isSolidAt, isSolidAlongSegment Gameplay policy and physics objects
ClassifyTile / tile proof classifyBox Clipmap residency and tile lifetime
GenerateMesh plus shape-world far-parent repair and shell stitching generateTile Convert owned result to FVoxelMeshData, call BuildTileStreamSet, and upload it
VoxelClipmapDesiredTiles::Build tile-set calculation desiredTiles and prioritizeTiles Decide when to request, cancel, retain or evict jobs
UVoxelDiffLayer::ApplyModification and affected-chunk scan applyEditTransaction with densityOffset brushes carrying signed strength and falloff; regenerate returned LOD keys Brush budgets, authority sequence, replication, and result acceptance

Delete plugin-side active-world dispatch when routing these calls; the World context is an explicit object passed to each operation. The plugin's epoch/cancellation checks still reject stale results, but cannot interrupt an executing core call. Because edit application replaces field edit state, the host must drain in-flight core calls before applying a transaction and then submit the affected tiles under a new epoch. Do not move worker admission, collision cooking, actor or material creation, or rendering into the library.

Acceptance record

The retained Round 11 executable is the reference at checkout 631dd57. The final Core build matched its 11 hash rows (full detail, whole view and LOD0–8) at both W1 and W8. It retained the 5,642/5,642 route audit, passed the shape/look and far-survival audits, and the damaged-shell control still reports 512 removed triangles, 16 missing boundary surfaces, 16 holes, and exit 14. Six fixed 1280×800 captures compare byte-for-byte with the Round 11 references. The final single-tile --shape-world --benchmark-level=0 path reports generator=VoxelForgeCore.

The final no-hash, cold W8 direct full-payload benchmark was interleaved reference/core/core/reference with --stream-benchmark --shape-world --force-generate --workers=8 --clip-radius=3. Process wall means were 1.041 s for the reference (1.035, 1.047) and 1.0245 s for Core (1.037, 1.012), 16.5 ms faster. Each run generated 2,226 tiles with the full 110.90 MiB payload and zero cache hits.

The separate neighborhood-first --stream-pipeline benchmark was repeated in eight interleaved processes per build. Whole-view-ready means were 0.887 s for the reference (0.878–0.899 s) and 0.894 s for Core (0.880–0.932 s). The 7 ms mean difference is smaller than the 54 ms combined run-to-run range; Core reached the 27-tile neighborhood and 960-tile full-detail milestones at comparable times. Direct benchmark, pipeline, audit, and capture logs are retained in Core/verification.