Pinch cause: SmoothStep taper drove the underside radius to zero at one point. Version 2: depth 0.75x diameter (thin-pad control at 0.30 fails), 3-4 seeded roots, bounded contour warp + roughness, one off-route side island, 3 m rock spans. Bounds/route/W1-W8-W16 pass. Open: legacy probe metadata says 25 cm but production legacy is 12.5 cm -- round 3 must fix the import scale contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
244 lines
15 KiB
Markdown
244 lines
15 KiB
Markdown
# VoxelForgeCore API reference
|
||
|
||
Public header: `Core/include/VoxelForgeCore/VoxelForgeCore.h` (C++20, standard library only).
|
||
Build the static library and viewer with `Build.ps1 -Target All`, or only the library with
|
||
`Build.ps1 -Target Core`.
|
||
|
||
The public header uses only standard C++ types and can be included after Unreal's `CoreMinimal.h`.
|
||
`Core/verification/UnrealHeaderCoexistence.cpp` is the compile smoke check against the installed
|
||
Unreal 5.7 headers. Copied generator and compatibility declarations stay in the private
|
||
`vfcore::detail` implementation and do not appear in this API.
|
||
|
||
## Units and ownership
|
||
|
||
- `Vec3d` query positions and `Vec3i` voxel coordinates are in **voxels**.
|
||
- `TileKey.coordinate` is a tile coordinate; one coordinate step spans `32 << lod` voxels.
|
||
- Mesh vertex positions are **centimetres**. UVs, RGBA8 colors, normals and 32-bit indices are
|
||
returned per vertex / triangle in owned `std::vector`s.
|
||
- `MeshGroups::groundTriangleCount` and `ceilingTriangleCount` describe the contiguous index runs
|
||
emitted by the mesher. Ceiling is the final run. `skirtTriangleCount` is a subset of the ground
|
||
and ceiling runs. `surfaceVertexCount` ends before skirt vertices.
|
||
- A `World` owns its compiled field and edit log. The snapshot can be shared as immutable input.
|
||
Serialize compile/edit changes against query and tile-generation calls on that same `World`.
|
||
|
||
## Load and compile
|
||
|
||
`ResolvedWorldSnapshot::FromJson` accepts the normalized UTF-8 schema-v1 owner export used by the
|
||
standalone loader. It returns `std::nullopt` and fills `error` when parsing or asset resolution
|
||
fails. Read `configuration()` for the resolved scalar settings.
|
||
|
||
Implement `JobScheduler::parallel_for` in the host. It must run each index from zero to `count - 1`
|
||
exactly once and return only after all submitted work is complete. The library creates no threads.
|
||
`ShapeBuildRequest` supplies the detail and clip bounds in voxels, worker limit, detail variant,
|
||
far-world inclusion, and optional tunnel/detail profiles.
|
||
|
||
```cpp
|
||
std::string error;
|
||
auto snapshot = VoxelForgeCore::ResolvedWorldSnapshot::FromJson(json, error);
|
||
if (!snapshot) { /* report error */ }
|
||
|
||
VoxelForgeCore::World world(std::move(*snapshot));
|
||
VoxelForgeCore::ShapeBuildRequest build;
|
||
build.minVoxel = {-256, -768, -384};
|
||
build.maxVoxel = { 256, -256, 128};
|
||
build.clipMinVoxel = {-57344, -57344, -8192};
|
||
build.clipMaxVoxel = { 49152, 49152, 8192};
|
||
build.workerLimit = 8;
|
||
build.includeFarWorld = true;
|
||
if (!world.compile(build, hostScheduler, error)) { /* report error */ }
|
||
```
|
||
|
||
For a streamed shape world, compile the initial camera region once, then add deterministic detail
|
||
pages before generating any newly desired fine tile outside that region:
|
||
|
||
```cpp
|
||
const auto entering = world.desiredTiles(newCameraVoxels);
|
||
VoxelForgeCore::TileGenerationOptions options;
|
||
std::size_t pageCoveredTiles = 0;
|
||
if (!world.compileTileRegion(entering, 8, hostScheduler, error, &pageCoveredTiles)) {
|
||
/* report error */
|
||
}
|
||
for (const auto& key : entering) {
|
||
if (auto mesh = world.generateTile(key, options, error)) { /* upload and retain */ }
|
||
}
|
||
world.retainTileRegions(entering);
|
||
```
|
||
|
||
By default, the Core groups missing keys by LOD and builds one analytic primitive/bin region for
|
||
each entering LOD slab. The region bounds come from the requested absolute tile coordinates, with
|
||
an 8-voxel sampling apron; no dense lattice is allocated for unused parts of the slab. A tile's
|
||
samples therefore depend on its world coordinates and the world snapshot, not the camera path or
|
||
the order in which tiles arrive. `compileTileRegion` skips LODs at or above
|
||
`fullResolutionLodCount`, keys covered by the initial `ShapeBuildRequest`, and keys already
|
||
resident. Its optional output counts requested keys that received a new region. After the regions
|
||
needed by the next desired set compile, call `retainTileRegions` with that set to release regions
|
||
that are no longer needed. The Core owns region lifetime and mesh data; the host keeps still-visible
|
||
meshes resident until replacement tiles are generated and uploaded.
|
||
|
||
Pass `analyticOnly = false` to retain the reference packed-page compiler. Packed pages use absolute
|
||
parent-cell coordinates and an 8-voxel apron; each covers a 128-voxel cube or one larger coarse
|
||
tile. This path remains available for parity checks and workloads that need the packed lattice.
|
||
|
||
## World assembler manifest v1 (evaluator v1–v4)
|
||
|
||
`ResolvedWorldSnapshot::FromJson` accepts an optional `world_assembly` object alongside the owner
|
||
snapshot. The object is the shipped-plan payload shape: a fixed-scale recipe and finite-strate plan
|
||
with hashes, no generated mesh or voxel samples. The current review payload is written to
|
||
[`round4/world_assembly.json`](../Research/WorldAssemblerM1/round4/world_assembly.json).
|
||
Evaluator 1–3 payloads remain readable with their original hashes and field semantics. Evaluator 4
|
||
adds a minimum two-voxel shelf thickness and a versioned route support contract; it requires at
|
||
least two route waypoints.
|
||
|
||
Every coordinate and scalar uses signed integer units of 1/256 voxel (`units_per_voxel: 256`),
|
||
independent of the plugin's 25 cm Core voxel and of tile size. The schema version is 1; evaluator
|
||
versions 1 and 2 are accepted, with evaluator 2 used for new recipes. Evaluator 1 retains its
|
||
original evaluation and hash semantics. JSON integer fields are canonicalized in declared field
|
||
order and hashed as 4- or 8-byte little-endian words with FNV-1a 64. Hashes are emitted as 16
|
||
lowercase hexadecimal characters; JSON key order and whitespace do not affect them. The root,
|
||
recipe, plan and each stratum carry a content hash. The recipe stores its seed, warped biome-map
|
||
settings and one to eight biome profiles; the plan stores finite-strate extents, cap settings,
|
||
stable-ID features, fixed-season water, route points and (for evaluator 4) player-capsule, grade,
|
||
support and sweep-spacing constraints. Route points are fixed-scale capsule-center locations with
|
||
stable IDs and a protected automatic-change corridor radius. Bounds, feature count, route count, route
|
||
IDs, corridor widths and finite-strate capsule extents are validated before a manifest is accepted.
|
||
|
||
Evaluator 2 adds climate-based biome assignment, output-space blending of both ground and cap
|
||
profiles, per-biome relief and height warp, beach width, cliff limits, and optional per-biome cap
|
||
profiles. Feature types 1, 2 and 3 represent a cliff plane, a bounded 3D overhang box and an authored
|
||
cap feature. An overhang feature has a stable ID and a finite AABB clipped to its stratum; intervals
|
||
outside the box can use its certified influence bound, while intersecting intervals remain on the
|
||
evaluation path. Water surface and fill are separate sample outputs. The season water plane is fixed;
|
||
this block does not generate a water mesh.
|
||
|
||
```cpp
|
||
std::string error;
|
||
auto manifest = VoxelForgeCore::WorldAssemblyManifest::FromJson(payload, error);
|
||
if (!manifest || !manifest->validate(error)) { /* reject the payload */ }
|
||
const auto* loaded = snapshot.worldAssembly(); // null when the owner snapshot has no plan
|
||
```
|
||
|
||
`Heightfield2D` is the first field block. `point(xQ, yQ, zQ)` evaluates the biome map and blended
|
||
height profiles at fixed-scale coordinates, returning the ground and cap heights, primary and
|
||
neighbor biome IDs, Q16 blend weight and signed density. Evaluator 2 also returns the fixed water
|
||
surface and water-fill sample values. Negative density is solid. Its contract reads `biome` and
|
||
writes `height`, `rock`, `water` and `waterSurface`; route and `surfaceTags` remain declared channels
|
||
for later blocks. The detail envelope reports maximum absolute displacement and minimum feature
|
||
size. Evaluator 4 defines the shelf's minimum solid thickness as two Core voxels (0.5 m) and applies
|
||
its feather to the horizontal footprint so it cannot thin the roof-to-floor section.
|
||
`AuditHeightfieldShelfThickness` samples shelf sections against the pre-feature field and includes
|
||
the previous sub-voxel taper as a negative control. Influence is global in XY and limited to the
|
||
finite stratum's Z extent, except for the explicitly bounded feature boxes.
|
||
|
||
`interval(minXQ, minYQ, minZQ, maxXQ, maxYQ, maxZQ)` returns a conservative density range or
|
||
`known == false`; callers must keep evaluating when it is unknown. The current heightfield range
|
||
uses conservative profile, beach, cliff, cap and bounded-feature envelopes. The public
|
||
`AuditHeightfieldBounds` samples points in randomized boxes and targeted biome-border, cliff, cap,
|
||
negative-coordinate and overhang boxes, and includes a deliberately false one-sided range as a
|
||
negative control. A pass means no sampled point escaped and the negative control was rejected; it
|
||
is an audit of this block implementation, not a proof for future blocks.
|
||
|
||
`AuditHeightfieldRoute` sweeps the recorded route through the final composed field using the
|
||
versioned player capsule (0.34 m radius, 1.76 m height), checks a 15-degree maximum grade, samples
|
||
capsule clearance at no more than half-voxel intervals, and probes support below five foot locations
|
||
at each swept center. Automatic changes must be composed first and pass this audit before the plan is
|
||
accepted. Player carve/fill edits remain unrestricted. This validates analytic field collision and
|
||
support; it does not cook an Unreal physics mesh.
|
||
|
||
## Floating-island feature family v1 (standalone Core)
|
||
|
||
`FloatingIslandRecipeV1` and `FloatingIslandManifestV1` define a separate, reusable finite 3D feature
|
||
family. Coordinates, dimensions, and density values use signed 1/256-voxel units; negative density
|
||
is solid. The recipe carries its seed, fixed vertical layout, site density, island count and spacing,
|
||
radius range, top flattening, taper, bounded contour warp, roughness, blend, connector dimensions and
|
||
route constraints. `PlanFloatingIslandCluster` emits stable island and connector IDs, a connected
|
||
cluster graph, conservative influence AABBs, and a centerline route. At least the primary chain is
|
||
always planned; `islandDensityQ16` controls up to two optional side sites. Connectors carry a sharp-tag
|
||
ready bit for a later hybrid-mesher adapter. The schema accepts geometry version 1 and 2. Manifests
|
||
without a geometry version retain version-1 evaluation and canonical hashes.
|
||
|
||
`FloatingIslandFieldV1::densitySubvoxels` evaluates the canonical island and connector field.
|
||
`interval` returns unknown for boxes overlapping any influence AABB and a certified all-air range
|
||
outside them. Geometry version 2 builds a flat walkable crust plus deterministic lobed rock roots,
|
||
keeps bounded oblique surface detail inside its certified envelope, and arches the connector
|
||
undersides. AABBs include root depth, contour displacement, detail, arch sag and smoothing reach.
|
||
`AuditFloatingIslandBounds` checks sampled boxes and void beside connectors and includes a deliberately
|
||
shrunken island-bound negative control. `AuditFloatingIslandThickness` reports each island's
|
||
`(topZ - bottomZ) / (2 * radius)` and checks a caller-supplied minimum; `AuditFloatingIslandRoute`
|
||
samples the player capsule and support along the serialized route. M3 round 2 passed at a minimum
|
||
depth-to-diameter ratio of 0.75 against a 0.70 threshold, while a 0.30 thin-pad control failed.
|
||
|
||
The family is still separate from `WorldAssemblyManifest` and the Unreal adapter. M3 rounds 1–2 prove
|
||
the standalone Core representation, bounded geometry, route, determinism and matched legacy timing.
|
||
The frozen owner evaluator is a visual and timing oracle, not a hash target. Unreal import and cooked
|
||
collision are planned for M3 round 3.
|
||
|
||
## Queries and tiles
|
||
|
||
`densityAt(position)` returns signed density; negative is solid and nonnegative is air. Server and
|
||
authority code can compile/query a `World` and run `AuditHeightfieldRoute` without calling
|
||
`generateTile`; generated render meshes are not part of this query path. Unreal physics collision
|
||
cooking remains the adapter's separate readiness step.
|
||
`isSolidAt` wraps that sign test. `isSolidAlongSegment(start, end, spacing)` samples evenly along
|
||
the segment with no interval wider than `spacing` voxels; it returns true if any sample is solid.
|
||
`classifyBox(origin, step, cells)` returns `allSolid`, `allAir` or `mixed` for mesher tile
|
||
classification.
|
||
|
||
`desiredTiles(cameraVoxels)` returns the snapshot's clipmap set. Pass it to
|
||
`prioritizeTiles(tiles, centerChunk)` when submitting work. Generate independent keys concurrently
|
||
on host workers:
|
||
|
||
```cpp
|
||
VoxelForgeCore::TileGenerationOptions options;
|
||
auto mesh = world.generateTile(key, options, error);
|
||
if (!mesh) { /* report error */ }
|
||
// mesh->vertices, mesh->indices and mesh->groups are owned by this result.
|
||
```
|
||
|
||
The optional `hashDensity` flag fills deterministic density, viewer-mesh, payload and topology
|
||
hashes. `captureDensitySamples` is a diagnostic option and retains the sampled lattice values.
|
||
|
||
## Deterministic edits
|
||
|
||
Build an `EditTransaction` with a monotonically increasing authority sequence and an ordered brush
|
||
array (`sphere`, `capsule`, `roundedBox`; `carve`, `addRock`, `densityOffset`). `carve` and
|
||
`addRock` apply hard signed-distance CSG. `densityOffset` applies `strength` inside a sphere or
|
||
inside/around a capsule or box with its `falloff` band; negative strength carves and positive
|
||
strength adds rock. This operation maps the plugin's soft `FVoxelModification` brushes into the
|
||
shared field. Apply the transaction against the currently desired keys:
|
||
|
||
```cpp
|
||
VoxelForgeCore::EditTransaction edit;
|
||
edit.sequence = 12;
|
||
VoxelForgeCore::EditBrush brush;
|
||
brush.shape = VoxelForgeCore::EditBrush::Shape::capsule;
|
||
brush.operation = VoxelForgeCore::EditBrush::Operation::densityOffset;
|
||
brush.a = {0.0, 0.0, 0.0};
|
||
brush.b = {12.0, 0.0, 0.0};
|
||
brush.radius = 3.0;
|
||
brush.strength = -10.0f;
|
||
brush.falloff = 2.0f;
|
||
edit.brushes.push_back(brush);
|
||
const auto affected = world.applyEditTransaction(edit, currentTiles, error);
|
||
if (affected) {
|
||
for (const auto& lodTiles : affected->affectedByLod)
|
||
for (const auto& key : lodTiles)
|
||
submitHostJob([&, key] {
|
||
std::string tileError;
|
||
return world.generateTile(key, options, tileError);
|
||
});
|
||
}
|
||
```
|
||
|
||
The transaction digest covers the sequence and each brush field in order. Replaying the same
|
||
sequence and values returns the original result; reusing a sequence with different values or
|
||
applying an older sequence is rejected. Each client must deserialize identical brush values and
|
||
apply transactions in the same sequence. `affectedByLod` is limited to the desired keys passed to
|
||
the call.
|
||
|
||
## Other outputs
|
||
|
||
`farConnectorSegments()` returns owned connector geometry, while `farConnectorCandidates(min,max)`
|
||
returns candidate indices overlapping a voxel-space box. The audit methods expose shape detail,
|
||
surface-family and camera-look checks for host diagnostics. All returned values are standard C++;
|
||
the host owns conversion to engine mesh, collision or renderer objects.
|