refactor: resolved metre layout is the runtime authority (L41, migration step 3); stamp covers plugin sources; R8
UVoxelStrateManager::Initialize resolves the layout width to integer micrometres before passages or scheduling; each slot stores physical [bottom, top) bounds, transition mode/width and passage settings; chunk indices remain a floor-based compatibility projection. The Core adapter derives interiors and L29 bands from those resolved values. verify: full-tier cost baseline is HEAD (or --baseline), tier timeout 7200 s with 1800 s per process; the source stamp now includes the plugin Source tree compiled on Linux (it previously missed VoxelStrateManager.cpp and friends). Owner hashes identical to L37; fast, medium and full PASS on a clean rebuild. Unreal manager/adapter edits are uncompiled on Windows. Research R8: inventory of unit-bearing UPROPERTYs and a custom-version asset migration plan. 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
efa9e89468
commit
1abee91721
11 files changed
+662
-215
No files matched your search
@@ -0,0 +1,69 @@
|
||||
# R8 — metre authoring in Unreal assets
|
||||
|
||||
Read-only source and UE documentation review. No source, build, or test changes.
|
||||
|
||||
## 1. Reflected spatial-property inventory
|
||||
|
||||
Conversion notation: `n` is the old serialized number. Use double precision and integer micrometres at the adapter boundary. `owner voxel` = 0.125 m (`VOXEL_SIZE`); `Core voxel` = that world's saved `CoreVoxelSizeCm / 100` (default 0.25 m); legacy runtime chunk = 32 owner voxels = 4 m; vertical *layout* chunk = the captured old `StrateLayoutChunkSizeMeters` (8 m default in current manager code). Frequencies are cycles per old voxel: `f_new_per_m = f_old / oldVoxelMeters`. Inverse-frequency fallbacks must be converted with the same route.
|
||||
|
||||
| Reflected fields (source line) | Current unit and use | Convert to metres / losslessness |
|
||||
|---|---|---|
|
||||
| `UVoxelStrateDefinition::StrateHeightInChunks` ( `VoxelStrateDefinition.h:75` ), `TransitionBlendChunks` (`:103`), `UVoxelSettings::InterStrateGapChunks` (`VoxelSettings.h:420`) | Integer vertical layout chunks; manager assigns slot bounds and transition bands. | `heightM = count × captured old layoutChunkM`; same for gap and transition width. Exact and yields the same chunk indices only when old effective width is known and new metres remain an integer multiple of it. Current manager uses `StrateLayoutChunkSizeMeters` (`VoxelSettings.h:427`, default 8 m); the strate comment says 4 m (`VoxelStrateDefinition.h:71–73`). This must be resolved from each owner's pre-update world, not guessed. |
|
||||
| `UVoxelSettings::StrateLayoutChunkSizeMeters` (`VoxelSettings.h:427`) | Already metres; physical vertical-layout chunk width, independent of Core tile/voxel width. | Keep as metres. It is the basis for converting height, gap and transition values, not the 4 m legacy streaming chunk. |
|
||||
| `ViewDistanceXY/Up/Down` (`VoxelSettings.h:44,47,50`), `PawnCollisionRadiusChunks` (`:96`), `StrateViewMarginChunks` (`:111`), `RenderDistanceChunks` (`:183`), `DecorationRadiusChunks` / `DecorationNearRadiusChunks` (`:241,248`); `FStrateLandmark::SpacingChunks`, `StreamRadiusChunks`, `ExclusionRadiusChunks`, `SuppressRadiusChunks` (`VoxelStrateTypes.h:2298,2335,2344,2360`) | Legacy chunk counts controlling streaming extents, collision work, content grids and landmark placement. These routes define one chunk as 4 m (32 × 12.5 cm); landmark/content code multiplies by the runtime chunk width. | `m = n × 4`. Exact for the old grid. Keep `StrateLayoutChunkSizeMeters` separate; it is not this chunk. If these knobs remain integer counts internally, recover `round(m/4)` only for the old 4 m grid. |
|
||||
| `WorldRadiusVoxels`, `EdgeSealThickness`, `OriginSpineRadius`, `MaxBrushRadius` (`VoxelSettings.h:222,226,440,465`); decoration spacing/far spacing/march step/column depth (`:263,271,277,289`); `FDecoSubCompanion::RadiusMinVox/RadiusMaxVox` (`VoxelStrateTypes.h:2127,2130`), `FDecoCompanion::RadiusMinVox/RadiusMaxVox` (`:2168,2171`); landmark orb radius/falloff/max-shadow distance (`:2383,2388,2393`) | Owner Runtime voxels at 12.5 cm. Used as radii, bounds, placement spacing/search steps, or light ranges. | `m = n × 0.125`. Exact physical conversion for the 12.5 cm grid. The spacing/step fields need metre-aware sampling, not just renamed UI labels. |
|
||||
| `FStrateGenerationParams` (`VoxelStrateTypes.h`: `WormNetworkRange` 458; `RoomSpacing` 479; `MinRoomRadius` 496; `MaxRoomRadius` 504; `FloorReliefStrength` 552; `RoomMouthRiseBlendVoxels` 571; `OriginRoomRadius` 593; `TunnelMinRadius` 615; `TunnelMaxRadius` 622; `MaxTunnelLength` 637; `TunnelWarpStrength` 649; `TunnelFloorMaxLedgeHeight` 708; `SDFBlendRadius` 739; `CaveWarpStrength` 762; `SurfaceRoughness` 786; `DomainWarpStrength` 815; `FloorBias` 840; `BoundarySealThickness` 1076) | Owner-voxel distances/displacements used by cave morphology, warps, roughness, tunnel/room envelopes and seals. | `m = n × 0.125`. Exact for physical lengths. `TunnelFloorTerraceStepHeight` (`:702`) is serialized but explicitly ignored; keep it as deprecated compatibility data instead of converting a dead control. `TunnelEndpointZOffset` (`:678`) and `TunnelFloorGentleSlopeThreshold` (`:716`) are ratios/degrees, not lengths. |
|
||||
| `FStrateGenerationParams` frequencies (`VoxelStrateTypes.h:418,560,771,793,822`) | `WormFrequency`, `FloorReliefFrequency`, `CaveWarpFrequency`, `RoughnessFrequency`, `DomainWarpFrequency`; inverse owner-voxel wavelength. | `cycles/m = f / 0.125` (multiply by 8). Exact change of units; resulting sampled output is byte-identical only if the evaluator uses the same operation/rounding order. |
|
||||
| `FSlabGenerationParams` (`VoxelStrateTypes.h`: `FloorRoughness` 1259; `ColumnMinRadius` 1311; `ColumnMaxRadius` 1317; `ColumnSpacing` 1324; `CeilingRoughness` 1280; `BoundarySealThickness` 1334) and `FMazeGenerationParams` (`:CellSize` 1371; `CorridorRadius` 1377; `SurfaceRoughness` 1391; `BoundarySealThickness` 1395) | Owner-voxel geometry: surfaces, formations, columns, cells, corridors and seals. | `m = n × 0.125`, exact physically. Slab frequencies at `:1266,1288` use `cycles/m = f/0.125`. Relative floor/ceiling heights, density and probabilities remain dimensionless. |
|
||||
| `FSurfaceGenerationParams` geometry (`VoxelStrateTypes.h`: `ElevationRange` 1430; `SurfaceRoughness` 1452; `HeightWarpStrength` 1460; `TerraceHeight` 1490; `LayerLineDepth` 1509; `LayerLineSpacing` 1513; `CliffSampleDist` 1537; `OverhangReach` 1554; `OverhangHeight` 1560; `BeachWidth` 1588; `CeilingRoughness` 1599; `CeilingUndulation` 1613; `CeilingRidgeStrength` 1625; `CeilingWarpStrength` 1635; `BoundarySealThickness` 1643) | Voxel lengths/displacements. **Current Core SurfaceWorld adapter consumes these raw in Core-voxel units** via `WorldAssemblyFixed`; the legacy owner path is 12.5 cm, while the current Core default is 25 cm. | To preserve the current Core recipe, `m = n × captured CoreVoxelSizeCm/100` (default `n×0.25`), not `n×0.125`. If preserving a legacy Runtime route instead, use that route's 0.125 m scale. This route distinction is essential. |
|
||||
| `FSurfaceGenerationParams` frequencies (`VoxelStrateTypes.h:1434,1443,1447,1464,1470,1565,1604,1617,1629,1639`) | Continent, mountain, detail, height-warp, relief, overhang, ceiling roughness/undulation/ridge/warp frequencies. Current SurfaceWorld adapter treats them as cycles per Core voxel. | `cycles/m = f / captured CoreVoxelMeters` (×4 at 25 cm, ×8 at 12.5 cm). Use `fallbackM = fallbackOldVoxels × CoreVoxelMeters` when old frequency is zero. Exact unit conversion; see adapter caveat below. |
|
||||
| `FVerticalShaftParams` (`VoxelStrateTypes.h`: `ShaftSpacing` 1672; radii 1680,1683; `ConnectorRadius` 1691; `LedgeSpacing` 1696; `LedgeDepth` 1700; `SurfaceRoughness` 1704; `BoundarySealThickness` 1708), `FFloatingIslandParams` (`:IslandSpacing` 1737; radii 1746,1749; roughness 1766; `SDFBlendRadius` 1771; seal 1775), `FStrateDisturbanceParams` (`:ChasmSpacing/Radius` 1808,1810; bridge spacing/radius 1817,1819; ridge spacing/height/thickness 1826,1828,1830; seal 1838) | Owner voxels; FI comments explicitly define owner scale and current adapter converts FI dimensions by owner/Core ratio. | `m = n×0.125`; exact. Disturbance and shaft spacing become metres too. Density, thickness ratio and jitter remain unitless. |
|
||||
| `FStratePassageConfig` (`VoxelStrateTypes.h`: `MouthRadius` 1886; `MidRadius` 1889; `ReachMin/Max` 1895,1898; `DistanceMin/Max` 1904,1907; `Wander` 1913; `VerticalWobble` 1922; `SpiralRadius` 1927; `CascadeLedge` 1938) | Owner voxels; current Core adapter explicitly maps these through `OwnerVoxelsToCoreQ`. | `m=n×0.125`; exact. `Connections`, `Segments`, `CascadeSteps` are counts; `SpiralTurns` is turns, so keep those. The adapter's fixed 12-owner-voxel bore/wall reserves also need to become explicit 1.5 m / 0.5 m constants. |
|
||||
| `FBiomeMapParams` (`VoxelBiomeTypes.h`: `CellSize` 37; `WarpStrength` 42; `BorderBlend` 51; frequencies 46,63,71) | Widths and warp displacement are voxel units; current biome recipe adapter packs them as Core voxels. Climate/warp frequencies are inverse Core-voxel units on that route. | Width/displacement: `m=n×CoreVoxelMeters`; frequency: `cycles/m=f/CoreVoxelMeters`. The legacy owner evaluator instead requires 0.125 m. `ReliefContrast` is unitless. |
|
||||
| `UVoxelTerrainOpDefinition` (`VoxelTerrainOpDefinition.h`: `TerraceStepHeight` 91; `LayerLineSpacing` 112; `RibbingSpacing` 127; `OverhangDepth` 174; arch radii 195,201; column radii 218,224; pit radii/depth 241,247,253; chimney radii/height 270,276,282; dome radii 297,303; `PinchStrength/PinchLength` 328,334) | Legacy owner-voxel distances and SDF radii/lengths; `PinchStrength` is a physical narrowing width in morphology. | `m=n×0.125`. `ScallopFrequency` (`:159`) and `OverhangFrequency` (`:179`) become `f/0.125` cycles/m. `LayerLineDepth` (`:118`) and `RibbingDepth` (`:133`) are density amplitudes, not distances; leave them outside metre conversion. |
|
||||
| `FPlacementProfile::SurfaceOffset`, `LocationOffset`, `CullDistance` (`VoxelStrateTypes.h:2068,2072,2097`); `UVoxelStrateDefinition::CeilingLayerZOffset/FloorLayerZOffset` (`VoxelStrateDefinition.h:316,329`); `AVoxelWorld::CoreVoxelSizeCm` (`VoxelWorld.h:304`) | Unreal centimetres. Placement vector is post-snap world offset; layer offsets are vertical offsets; Core voxel field is a world resolution (12.5–25 cm). | Scalar/vector centimetres: `/100` per component. Core resolution: `CoreVoxelSizeMeters=cm/100`. Exact at micrometre precision for current values. Do not bake this world-level value into a shared strate asset if different worlds use different Core sizes. |
|
||||
| `EditorBrushCenter`, `EditorBrushRadius`, `BiomePreviewWorldSize`, `BiomePreviewCenter` (`VoxelWorld.h:862,866,902,906`) | Brush center is Unreal cm; brush radius and preview window size/center are legacy owner-voxel coordinates. | Center cm `/100`; other values/components `×0.125`. `BiomePreviewCenter` is a coordinate, not a size, but its units still change. |
|
||||
| `UVoxelSeasonAsset` mirrors (`VoxelSeasonAsset.h:56,59`) and `VoxelSettings::WorldAssemblyManifestJson` (`VoxelSettings.h:392`) | Reflected mirror values are voxel-valued; the JSON is serialized manifest text, not a scalar property. Manifest schema has `HeightInChunks`, `InterStrateGapChunks`, `OriginSpineRadius`, `WorldRadiusVoxels` and per-strate `HeightInChunks` (`VoxelSeasonManifest.h:88–91,164`). | Migrate the source JSON with its own schema-version step, using the same route-aware formulas; refresh mirrors and content hash. Do not multiply opaque JSON text or convert mirrors independently. CSV/DataTable import sources also need a separate migration. |
|
||||
|
||||
`ClipRadius` (`VoxelSettings.h:153`) and `AVoxelWorld::CoreClipRadius` (`VoxelWorld.h:308`) are tile counts, `SkirtCells` (`VoxelSettings.h:193`) scales with LOD step, and `CoarseTileCells`/clip levels/region sizes are algorithmic counts. They have no single metre conversion that preserves current semantics across LODs; keep as counts or redesign the algorithm around a physical extent. Runtime-only `FVoxelModification` in `VoxelDiffLayer.h` is not reflected (`UPROPERTY`) and is not in this inventory.
|
||||
|
||||
**Losslessness:** 0.125/0.25 m voxels and 4/8 m chunks map exactly to integer micrometres. The Core recipe uses 1/256 Core-voxel fixed point; old values reproduce its old integer Q only when converted from the actual route and old world resolution. Arbitrary new metre values round to that Q grid, so they are not universally lossless. Use one `llround` at the adapter boundary and compare the resulting integer fields.
|
||||
|
||||
## 2. UE5 migration mechanism
|
||||
|
||||
Use a plugin `FGuid` custom version registered with a static `FCustomVersionRegistration`; migrate loaded properties in each owning UObject's `Serialize`. UE documents this GUID/version pattern and `FArchive::UsingCustomVersion` for object-level asset conversion ([asset/package versioning](https://dev.epicgames.com/documentation/unreal-engine/versioning-of-assets-and-packages-in-unreal-engine?lang=en-US), [`FCustomVersionRegistration`](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Core/FCustomVersionRegistration), [`UsingCustomVersion`](https://dev.epicgames.com/documentation/unreal-engine/API/Runtime/Core/FArchive/UsingCustomVersion), [`UObject::Serialize`](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject/Serialize)).
|
||||
|
||||
```cpp
|
||||
// Registered once in the plugin module; increment Version when a later migration is added.
|
||||
Ar.UsingCustomVersion(FVoxelForgeAssetVersion::GUID);
|
||||
Super::Serialize(Ar);
|
||||
if (Ar.IsLoading() && Ar.CustomVer(FVoxelForgeAssetVersion::GUID) < MetersV1)
|
||||
{
|
||||
HeightMeters = double(StrateHeightInChunks_DEPRECATED) * CapturedOldLayoutChunkMeters;
|
||||
TransitionWidthMeters = double(TransitionBlendChunks_DEPRECATED) * CapturedOldLayoutChunkMeters;
|
||||
// Convert each nested parameter by its recorded legacy route, then mark schema state.
|
||||
}
|
||||
```
|
||||
|
||||
Run this for settings, strate, biome, terrain-op, and world owners. A world-specific `CoreVoxelSizeCm` and old effective layout-chunk width must be available to the converter; do not infer either from current CDO defaults. Existing `UVoxelStrateDefinition::PostLoad` is useful for object cleanup, but `PostLoad` is not called for newly-created objects ([UE `PostLoad`](https://dev.epicgames.com/documentation/unreal-engine/API/Runtime/CoreUObject/UObject/UObject/PostLoad?application_version=5.5)). `PostEditChangeProperty` is for editor reactions/previews after a user edit, not load migration; use it only to refresh validation/UI when a new metre field changes ([property-change event API](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/FPropertyChangedEvent)).
|
||||
|
||||
For one transition release, keep each old reflected property as a deprecated `_DEPRECATED` field with the old type and old CDO default, and add the new `...Meters` property with `meta=(Units="m")`. On rename, use exact `PropertyRedirects` from the old serialized name to its deprecated holding property, then copy/convert in `Serialize`; redirects remap names/values on load but do not change units ([Core Redirects](https://dev.epicgames.com/documentation/en-us/unreal-engine/core-redirects-in-unreal-engine)). Keep deprecated BP references compilable until graphs are updated. Epic notes metadata is editor-only, so runtime conversion must use the actual numeric field, not `Units` metadata ([metadata specifiers](https://dev.epicgames.com/documentation/unreal-engine/metadata-specifiers-in-unreal-engine)). Keep the old CDO defaults for this release: old assets may omit values equal to their old CDO default. Convert old-version assets even when the new field has its default; do not mistake that for already migrated data.
|
||||
|
||||
For `USTRUCT` rows in DataTables, add a version-aware `PostSerialize(const FArchive&)` via `TStructOpsTypeTraits::WithPostSerialize`, or run the same conversion in a one-time editor asset upgrader. Reimported CSV/JSON is external source data and will overwrite the table, so migrate that source too. Keep redirects and deprecated fields through a full resave/reference audit, then remove them in a later version.
|
||||
|
||||
## 3. Owner checklist
|
||||
|
||||
- Back up the Windows project/source control state. Before updating, record each world's old `CoreVoxelSizeCm`, effective layout-chunk metres, settings, seed and exported `ResolvedLayoutSnapshotV3` (strata bounds, gaps, transition widths, passage Q values and hash). This resolves the code's 4 m comment / current 8 m default conflict.
|
||||
- Update the plugin, open and compile affected Blueprints, then load/save every referenced Settings, Strate, Biome, TerrainOp, Season, world/level and DataTable asset. Check conversion logs and save every dirty package. An asset never loaded and saved still has its old on-disk values; cooking can consume an in-memory conversion but is not a source-asset resave.
|
||||
- Replace old Blueprint property nodes and fix deprecation/redirect warnings. Convert CSV/JSON sources before reimport; verify Season manifest schema and content hash refreshed.
|
||||
- Re-export the same worlds with the same seed. Compare integer micrometre bounds, transition fields, passage fixed-point values, recipe Q/wavelength fields and `ResolvedLayoutSnapshotV3` stable identity against the pre-update export; inspect a clean cook/reopen for missing assets and redirect warnings.
|
||||
|
||||
## 4. Core adapter mapping
|
||||
|
||||
- `AttachValidatedWorldAssembly` currently resolves layout chunk micrometres once (`VoxelForgeCoreAdapter.cpp:818–822`); snapshot interiors and transition width use chunk indices × that width (`:1098–1108`). Feed it metre-valued bounds/gap/transition as integer micrometres. For converted legacy assets, convert back to the same chunk indices and preserve the same integer fields/hash. Reject or extend the Core snapshot schema for heights that are not integral layout chunks; the current manager/resolved layout is chunk-indexed.
|
||||
- Replace `WorldAssemblyFixed(value)` (`:67–71`, currently `round(value×256)` voxels) with `MetersToCoreQ(m) = round(m×1,000,000×256/CoreVoxelMicrometres)`. Replace `WorldAssemblyWavelength(f, fallbackVoxels)` (`:78–83`) with a metre-frequency form: `MetersToCoreQ(f>0 ? 1/f_cyclesPerMeter : fallbackMeters)`. For old SurfaceWorld and biome recipes, `n Core voxels → n×CoreVoxelMeters`, so current Q remains `n×256`; old frequency `f` becomes `f/CoreVoxelMeters`, preserving its wavelength Q.
|
||||
- For Floating Islands and passages, current code uses the 12.5 cm owner/Core ratio (`:492–524`, `:1061–1085`). Convert old owner values once to `n×0.125m`, then call `MetersToCoreQ`; at 25 cm Core this yields the same `n×128` Q as the existing ratio. Do not multiply by the ratio a second time. Convert hard-coded 12/4 owner-voxel bore/reserve constants to 1.5/0.5 m.
|
||||
- Set resolved `voxelSizeMicrometres` from `round(voxelSizeMeters×1e6)` (`:1098`). Current runtime still has one actor-level Core sampling size and writes that same size for every strate; until Core sampling supports mixed per-strate resolutions, validate that every resolved strate value equals the world's `CoreVoxelSizeCm/100` (normally 0.25 m) rather than publishing conflicting metadata.
|
||||
- Exact parity means the converted adapter emits the same old integer Q, micrometre intervals and canonical V3 layout identity for each old asset. Keep the old schema path/rounding for converted assets until that comparison passes.
|
||||
|
||||
**Owner question:** Can you capture each world's pre-update layout export, including its effective layout chunk width and `CoreVoxelSizeCm` (especially any world not at 25 cm)?
|
||||
@@ -0,0 +1,72 @@
|
||||
# Unreal asset migration notes — resolved metre layout
|
||||
|
||||
Read-only schema review for L41. No Unreal source or assets changed; no build or tests run.
|
||||
|
||||
## Existing authored data
|
||||
|
||||
- `UVoxelStrateDefinition` is a `UPrimaryDataAsset`. Its persisted `StrateHeightInChunks` and
|
||||
`TransitionBlendChunks` drive the vertical slot and lower-boundary transition. Their comments
|
||||
still describe 4 m chunks, while `UVoxelSettings::StrateLayoutChunkSizeMeters` now defaults to
|
||||
8 m. Keep the numeric fields as legacy authoring inputs and resolve them once using the layout
|
||||
width selected for that world. Do not reinterpret the stored integers as metres or infer their
|
||||
scale from the stale tooltip.
|
||||
- `UVoxelSettings::InterStrateGapChunks` is another legacy chunk input. Per-strate
|
||||
`FStratePassageConfig` is persisted with radii, reaches, placement distances, wander, wobble,
|
||||
spiral radius and cascade ledge expressed in legacy voxels; style, connection count, and segment
|
||||
counts are dimensionless/discrete. Retain the exact old effective voxel scale for each legacy
|
||||
asset route, convert dimensional values once, then store the resolved physical passage request
|
||||
and derived seed before any jobs are scheduled.
|
||||
- L40/R6 confirms FI and SW already receive the same `LayoutChunkWidthSubvoxels` for layout
|
||||
planning. Never apply an owner/Core ratio to layout bounds a second time. Keep that ratio only
|
||||
in the legacy FI parameter adapter; convert passage and layout inputs at their own established
|
||||
unit boundary.
|
||||
|
||||
## Season assets need a separate compatibility route
|
||||
|
||||
`UVoxelSeasonAsset` embeds the reviewed JSON plus copied schema/season/seed/hash metadata. Current
|
||||
manifest schema v2 stores `height_in_chunks`, global `inter_strate_gap_chunks`, per-slot seed and
|
||||
`top_world_z`/`bottom_world_z`; the composer derives those bounds with a fixed `* 32` convention.
|
||||
The runtime validates those stored bounds against the live layout chunk width. V2 does not carry a
|
||||
resolved transition width/band or passage config for generated slots; authored slots recover
|
||||
content and passage settings through `source_definition_path`. The asset loader rejects a schema
|
||||
version it does not recognize and verifies both hash copies.
|
||||
|
||||
For migration, keep a v2 reader/converter that interprets v2 under its original coordinate
|
||||
convention, verifies its existing content hash first, then builds the new immutable metre snapshot.
|
||||
Do not rewrite the embedded v2 JSON or silently add default passages: either action changes
|
||||
identity or geometry. If a season is explicitly re-authored/exported in a new schema, include
|
||||
resolved metre bounds, gap, interface transition/band records, per-strate seed and passage settings;
|
||||
bump the manifest schema and generate a new canonical content hash. Record the source schema and
|
||||
conversion policy in diagnostics so stale or ambiguous assets fail closed rather than guessing.
|
||||
|
||||
## Resolved snapshot and edge rules
|
||||
|
||||
The game-thread director should publish integer micrometre records before queueing work: half-open
|
||||
strate bounds `[bottom, top)`, height, inter-strate gap, transition width, the exact L29 upper/lower
|
||||
band bounds and mode, world/strate/passages seeds, and fully resolved passage descriptors. Copy
|
||||
the L29 band endpoints and cap rules exactly; do not recompute a second approximation from chunk
|
||||
indices in the adapter. Derive legacy chunk fields only as projections for streaming, old Blueprint
|
||||
queries and logging.
|
||||
|
||||
Use one conversion and edge policy in manager and adapters. For an aligned half-open interval the
|
||||
first chunk is `floor(bottom / chunkWidth)` and the inclusive last chunk is `ceil(top / chunkWidth)
|
||||
- 1` (equivalently `top / chunkWidth - 1` when aligned). Use mathematical floor / Euclidean
|
||||
division for negative Z, not C++ truncation toward zero. Quantize authored metres to integer
|
||||
micrometres once with a documented tie rule; avoid converting through float chunk positions and
|
||||
rounding again. Test exact lower/upper edges, one micrometre either side, negative boundaries,
|
||||
zero gap, and adjacent bands in both adapter and manager.
|
||||
|
||||
## Version and identity risks
|
||||
|
||||
- Keep authored-schema version, resolved-layout schema, recipe/evaluator version, and source
|
||||
content hash distinct. A derivation-only schema change may alter layout diagnostics; it must not
|
||||
move the owner density/mesh hashes in this pure-refactor step.
|
||||
- For old chunk/voxel assets, preserve physical dimensions with a versioned compatibility
|
||||
conversion. Future metre-authored properties should be explicit and versioned; do not overwrite
|
||||
old fields or treat their raw values as metres. Keep original asset paths/values available for
|
||||
rollback and audit.
|
||||
- `FStratePassageConfig` currently has no persisted passage seed, and season v2 lacks generated-slot
|
||||
passage descriptors. Derive seeds with the existing stable world-seed/slot/connection salts and
|
||||
record the result in the snapshot; do not base it on UObject pointer or load order.
|
||||
- UHT/UBT validation is still required for any reflected schema/property migration. The Linux Core
|
||||
checks cannot validate UObject serialization, cooked season references, or Blueprint compatibility.
|
||||
Reference in new issue
Block a user