research(R17): authoring and adapter wiring to bring Core stamps and cavern graphs into Unreal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
6110f118e3
commit
b9ccff4ad6
1 file changed
+41
@@ -0,0 +1,41 @@
|
|||||||
|
# VoxelForge Research 17 — Unreal landmarks and cavern graphs
|
||||||
|
|
||||||
|
## Recommended authoring model
|
||||||
|
|
||||||
|
Add an optional `CoreGeometry` block to the existing per-strate [`UVoxelStrateDefinition`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateDefinition.h:346>) asset. It is already the strate’s content bag and a `UPrimaryDataAsset`; a separate asset adds references and loading work, while a recipe flag is too coarse to describe authored atoms. [Epic’s Data Assets documentation](https://dev.epicgames.com/documentation/unreal-engine/data-assets-in-unreal-engine)
|
||||||
|
|
||||||
|
The block can hold stamp requests and cavern room/edge inputs. Give every atom an explicit stable string ID, and author distances in metres. Convert once to integer micrometres at the adapter boundary, following [R8’s unit and versioning plan](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research8/REPORT.md>). UE defaults to centimetres, and unit metadata is editor-facing, so the adapter must use the field’s actual stored unit. [UE units](https://dev.epicgames.com/documentation/unreal-engine/units-of-measurement-in-unreal-engine?lang=en-US), [property metadata](https://dev.epicgames.com/documentation/unreal-engine/unreal-engine-uproperties?lang=en-US)
|
||||||
|
|
||||||
|
Keep `FStrateLandmark` as the presentation and streaming entry. Add an optional reference from landmarks—and, when needed, decorations—to a Core stable ID. The existing landmark instances are keyed by lattice cell and entry index, so those keys cannot identify a fixed Core feature across systems. See [`FStrateLandmark`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:2275>) and [`LandmarkInstances`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelContentManager.h:434>).
|
||||||
|
|
||||||
|
## Adapter flow and runtime lookup
|
||||||
|
|
||||||
|
[`AttachValidatedWorldAssembly`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:839>) builds the layout at line 1045 and plans passages at lines 1259–1265. Gather the authored requests from each resolved slot, then call `ResolveResolvedLayoutStampsV2` and `ResolveResolvedCavernGraphV1` **after passage planning and before** `Layout.finalize` at line 1268. The demo uses that resolver order. The adapter already writes the resulting layout JSON at lines 1273–1274; [`ResolvedLayoutSnapshotV3`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h:945>) already contains the stamp and graph records.
|
||||||
|
|
||||||
|
Namespace IDs with the strate and feature name: the stamp resolver requires unique non-empty IDs and derives placement randomness from seed, strate, and ID. It performs bounded deterministic placement from a preferred anchor. The graph resolver, however, currently derives seeds and bounds and validates authored room centers and edge control points; it does **not** plan graph routes or choose their positions.
|
||||||
|
|
||||||
|
The runtime snapshot parser already reads `resolved_layout_json` into `ResolvedWorldSnapshot`, whose `resolvedLayout()` accessor is available before the snapshot is moved into the Core world. [`AVoxelWorld::InitializeVoxelForgeCoreWorld`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWorld.cpp:7570>) is the handoff point. Cache or expose that layout there, then extend [`UVoxelContentManager::Initialize`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelContentManager.cpp:315>) to retain it. Today the manager parses the world assembly manifest only.
|
||||||
|
|
||||||
|
Build a non-reflected runtime `TMap<FString, FCoreResolvedAnchor>` from the layout’s stable IDs to local positions and bounds. Actor, light, and decoration placement can then resolve the same ID. For example, a light reads the chamber center, while a set-piece references the dais stamp and can surface-find at that XY. Transform the resulting local position through the world actor transform.
|
||||||
|
|
||||||
|
Useful UE types are `USTRUCT(BlueprintType)` and `UPROPERTY` for authored records; `FString`, `TArray`, an enum, and explicit metre-valued fields for authoring; and a plain C++ `TMap`, `FTransform`, and shared immutable runtime snapshot for lookup. Keep Core’s `std::string`, `std::vector`, and resolver types out of reflected properties. [UE struct reflection](https://dev.epicgames.com/documentation/en-us/unreal-engine/structs-in-unreal-engine), [reflection and UHT](https://dev.epicgames.com/documentation/unreal-engine/reflection-system-in-unreal-engine)
|
||||||
|
|
||||||
|
## Determinism, multiplayer, and risks
|
||||||
|
|
||||||
|
Stamp and graph seeds are deterministic from the same world seed, strate, and stable ID. Peers with matching authored assets, resolver versions, and seed do not need the generated records replicated. Keep terrain edits in the existing diff layer. Include the resolved layout’s `stableIdentity` in the multiplayer/content identity: [`GetCurrentSeasonContentHash`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWorld.cpp:15118>) currently appends the assembly content hash, while the resolved layout is a separate payload. Gameplay actors still need normal server authority and replication; deterministic client-local spawning suits cosmetic props. [UE property replication](https://dev.epicgames.com/documentation/unreal-engine/replicate-actor-properties-in-unreal-engine)
|
||||||
|
|
||||||
|
UHT must parse the new reflected structs and fields; use UE-supported property types and avoid exposing Core C++ types through `UPROPERTY`. The strate asset already registers a custom version in [`VoxelStrateDefinition.cpp`](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelStrateDefinition.cpp:12>). Extend that version if a migration is needed; keep existing chunk/voxel fields’ meaning unchanged. Additive optional fields can default empty. Epic documents the GUID-based custom-version pattern and object-level `Serialize` migration path. [Asset versioning](https://dev.epicgames.com/documentation/unreal-engine/versioning-of-assets-and-packages-in-unreal-engine?lang=en-US)
|
||||||
|
|
||||||
|
## Ordered implementation plan
|
||||||
|
|
||||||
|
1. **Core-only portability gate:** compile the existing Core target and resolver API independently. **Compilable on Linux: yes**; this does not validate the Unreal adapter.
|
||||||
|
2. Add reflected authored atom structs and the optional per-strate property, with explicit stable IDs and metre semantics. **Compilable on Linux: no**; requires UHT.
|
||||||
|
3. Map authored atoms to Core requests in `AttachValidatedWorldAssembly`; invoke the stamp and graph resolvers before layout finalization. **Compilable on Linux: no**; Unreal adapter.
|
||||||
|
4. Cache/expose the parsed resolved layout and add stable-ID lookup for presentation placement. **Compilable on Linux: no**; Unreal runtime.
|
||||||
|
5. Add content-hash coverage for `stableIdentity`, then run UHT/UBT and review asset migration/resave behavior in the owner’s Windows editor. **Compilable on Linux: no**; the adapter and UHT require the Unreal build environment.
|
||||||
|
|
||||||
|
## Question for the owner
|
||||||
|
|
||||||
|
Should cavern room centers and edge control points be authored in metres for the current graph resolver, or must Core place the graph from stable IDs? The current resolver supports authored points and deterministic irregularity, but does not generate the graph route.
|
||||||
|
|
||||||
|
Read-only review only; no source edits, builds, or tests were made.
|
||||||
Reference in new issue
Block a user