fix(streaming): no false gate on digs, no see-through at tile handoffs; density volume off
- Dig near the pawn: a same-tile collision recook withdrew readiness although the old body was still installed, so the gate held a standing pawn (957 of 1,931 frames over 8 near digs). A tile now stays ready during a recook while collision is still wanted and its existing body is valid. After: 0 gate frames, 0 falling frames over 2,604 frames. Dig directly under the pawn still falls (~127 cm, max ~502 cm/s). The reported "accelerating toward the ground" was not reproduced; the false hold is the measured part. - See-through: the retire logic only waited for replacements that overlapped the old tile in volume and missed desired neighbours touching at a face/edge/corner. Handoff now includes contact. New surface-witness gap audit (24 surface points per tile, skirts excluded, catches an injected early hide). 6000 cm/s walk, 75 s: 3 gaps / 51 frames / longest 603 ms -> 0. Dig session: 0 -> 0. Overlap audit and edit ghost stay at 0. - bEnableDensityVolume defaults to false; when off: no volume, no fill thread, uploads or tick work, no volume-dependent proof bypass; terrain MIDs get VolP5.z = 0 (enable off), other VolP* black, VolTex* null, so M_Test1's volume nodes return their neutral result. Gate: deep gate-on passes (79.9 m, 3 holds, -0.101 cm), gate-off penetrates (-5.1 cm). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
0bcd67a016
commit
ce6812dbeb
7 files changed
+1099
-64
No files matched your search
+17
-11
@@ -117,7 +117,7 @@ remains `0`, and lateral regions remain gated off.
|
|||||||
|------|------|
|
|------|------|
|
||||||
| `../../VoxelForge.uplugin` | Plugin manifest. Runtime module `VoxelForge` plus the Editor-only `VoxelForgeEditor` commandlet module. Beta. |
|
| `../../VoxelForge.uplugin` | Plugin manifest. Runtime module `VoxelForge` plus the Editor-only `VoxelForgeEditor` commandlet module. Beta. |
|
||||||
| `VoxelForge.Build.cs` | Deps: Core, CoreUObject, Engine, **GameplayTags**, **RealtimeMeshComponent**. |
|
| `VoxelForge.Build.cs` | Deps: Core, CoreUObject, Engine, **GameplayTags**, **RealtimeMeshComponent**. |
|
||||||
| `Tools/VoxelForgeTest.ps1` | The single guarded test entry point. `-Scenario canonical|owner|probe|perf|tests|surface-fall|crossing|gate-stress|parity|horizon|horizon-render|determinism`, optional `-Build`, `-Assets owner|default`, `-Cvars`, absolute `-Out`, `-Label`, `-HorizonLevel`, `-HorizonRenderDistanceChunks`, and `-SharedDDC`; stages the host/assets, isolates user/DDC/config state, serializes one Unreal launch at a time, enforces the 8 GiB/30-minute guards, and emits compact `result.json`/`summary.txt` plus full logs. Horizon traces report desired/loaded tiles, resident stream geometry, worker seconds, steady wall time, and peak private memory; `horizon-render` captures the owner view; `determinism` compares per-tile hashes across the default CRT path and `-voxel.CrtFma3=0`. |
|
| `Tools/VoxelForgeTest.ps1` | The single guarded test entry point. `-Scenario canonical|owner|probe|perf|tests|surface-fall|crossing|gate-stress|visual-edit|visual-lod|visual-gap-walk|visual-gap-inject|dig-motion|parity|horizon|horizon-render|determinism`, optional `-Build`, `-Assets owner|default`, `-Cvars`, absolute `-Out`, `-Label`, `-HorizonLevel`, `-HorizonRenderDistanceChunks`, and `-SharedDDC`; stages the host/assets, isolates user/DDC/config state, serializes one Unreal launch at a time, enforces the 8 GiB/30-minute guards, and emits compact `result.json`/`summary.txt` plus full logs. `visual-gap-walk` records camera-frustum surface-point coverage at 6000 cm/s; `visual-gap-inject` proves an early component hide is detected; `dig-motion` logs per-frame vertical velocity, gate, support readiness, and movement mode. `-Cvars @{ 'voxel.TestGapAuditLegacyHandoff'=1 }` replays the old visibility handoff for A/B audit baselines. Horizon traces report desired/loaded tiles, resident stream geometry, worker seconds, steady wall time, and peak private memory; `horizon-render` captures the owner view; `determinism` compares per-tile hashes across the default CRT path and `-voxel.CrtFma3=0`. |
|
||||||
| `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). |
|
| `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). |
|
||||||
| `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; owns bounded render/`VF_` walk/OBJ export orchestration and is not linked by Game/Shipping. |
|
| `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; owns bounded render/`VF_` walk/OBJ export orchestration and is not linked by Game/Shipping. |
|
||||||
| `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. |
|
| `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. |
|
||||||
@@ -135,7 +135,7 @@ remains `0`, and lateral regions remain gated off.
|
|||||||
| `SmoothStep01` | 306 | 3x²-2x³ — used everywhere for blends. |
|
| `SmoothStep01` | 306 | 3x²-2x³ — used everywhere for blends. |
|
||||||
| `VOXEL_NOISE_SCALE` (1.25f) | 313 | Rescales UE PerlinNoise3D to ~[-1,1]. |
|
| `VOXEL_NOISE_SCALE` (1.25f) | 313 | Rescales UE PerlinNoise3D to ~[-1,1]. |
|
||||||
| `EVoxelTileClass` enum (`Mixed`/`AllSolid`/`AllAir`) | 231 | T1.d verdict. Lives here so `VoxelDensityOp.h` can share it without a UCLASS dependency. A false `AllSolid`/`AllAir` is a HOLE; a false `Mixed` only costs CPU. |
|
| `EVoxelTileClass` enum (`Mixed`/`AllSolid`/`AllAir`) | 231 | T1.d verdict. Lives here so `VoxelDensityOp.h` can share it without a UCLASS dependency. A false `AllSolid`/`AllAir` is a HOLE; a false `Mixed` only costs CPU. |
|
||||||
| `FVoxelMeshData` struct | 365-391 | Mesher output (Vertices/Triangles/UVs/Normals/**Colors**). Plain C++, not USTRUCT. `Colors` = F6 material masks (R=dominant biome palette, G=slope, B=border blend weight, A=neighbour biome palette). §8.15. |
|
| `FVoxelMeshData` struct | 365-394 | Mesher output (Vertices/Triangles/UVs/Normals/**Colors**). Plain C++, not USTRUCT. `NumSurfaceVertices` marks the real isosurface prefix before LOD seam skirts are appended; visibility audit witnesses sample only this prefix. `Colors` = F6 material masks (R=dominant biome palette, G=slope, B=border blend weight, A=neighbour biome palette). §8.15. |
|
||||||
|
|
||||||
### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT)
|
### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT)
|
||||||
**The production density path** — `GetDensityAt` builds and evaluates a stack for every strate of
|
**The production density path** — `GetDensityAt` builds and evaluates a stack for every strate of
|
||||||
@@ -257,7 +257,7 @@ Tile identity lives in `FVoxelTileKey` (VoxelTypes.h:326).
|
|||||||
| Streaming | `ViewDistanceXY=16`, `ViewDistanceUp/Down=5`, `MaxConcurrentTasks=16`, `MaxMeshAppliesPerFrame=4`, `PawnCollisionRadiusChunks=1` (defaults — actual values live on the data asset; the collision radius is independent from the render clipmap) |
|
| Streaming | `ViewDistanceXY=16`, `ViewDistanceUp/Down=5`, `MaxConcurrentTasks=16`, `MaxMeshAppliesPerFrame=4`, `PawnCollisionRadiusChunks=1` (defaults — actual values live on the data asset; the collision radius is independent from the render clipmap) |
|
||||||
| Clipmap | `ClipRadius`, `MaxClipLevel=5`, `FullResClipLevels`, `CoarseTileCells`, `RenderDistanceChunks=768` (measured ship defaults; custom horizontal reach: the outermost shell keeps generating until it covers this many chunks; 0 = off), skirts, `LODOctaveDrop` (T2.b octave drop on coarse tiles — 0 = off/byte-identical). Existing serialized data assets retain their authored values until deliberately migrated. |
|
| Clipmap | `ClipRadius`, `MaxClipLevel=5`, `FullResClipLevels`, `CoarseTileCells`, `RenderDistanceChunks=768` (measured ship defaults; custom horizontal reach: the outermost shell keeps generating until it covers this many chunks; 0 = off), skirts, `LODOctaveDrop` (T2.b octave drop on coarse tiles — 0 = off/byte-identical). Existing serialized data assets retain their authored values until deliberately migrated. |
|
||||||
| World bounds | `WorldRadiusVoxels=8192` and `EdgeSealThickness=64` (actor-space XY radial shell; radius 0 = true legacy no-op) |
|
| World bounds | `WorldRadiusVoxels=8192` and `EdgeSealThickness=64` (actor-space XY radial shell; radius 0 = true legacy no-op) |
|
||||||
| Lighting | `bEnableDensityVolume` + DensityVolume* tunables (§3.11 density clipmap / mini-sun shadows) |
|
| Lighting | `bEnableDensityVolume=false` + DensityVolume* tunables (§3.11 density clipmap / mini-sun shadows). The disabled default creates no volume object, fill thread, uploads, or Tick updates; terrain MIDs receive neutral `VolP0..4=black`, `VolP5=(0,25,0)` (`z` enable is 0), `VolP6..9=black`, and `VolTex0..2=null`. |
|
||||||
| Rendering | `VoxelMaterial` (377) |
|
| Rendering | `VoxelMaterial` (377) |
|
||||||
| Strates | Optional cooked `Season`; effective seed/spine/radius/season accessors read it when assigned. Otherwise `Seed`, `CurrentSeason=1`, `StratePool`, `FixedStrates`, and `TotalStrates=10` retain the authored path. |
|
| Strates | Optional cooked `Season`; effective seed/spine/radius/season accessors read it when assigned. Otherwise `Seed`, `CurrentSeason=1`, `StratePool`, `FixedStrates`, and `TotalStrates=10` retain the authored path. |
|
||||||
| Carving budget | `MaxModifications=0` (449), `MaxBrushRadius=15` (454), `MaxTotalVolume=0` (459). 0 = unlimited. |
|
| Carving budget | `MaxModifications=0` (449), `MaxBrushRadius=15` (454), `MaxTotalVolume=0` (459). 0 = unlimited. |
|
||||||
@@ -265,7 +265,9 @@ Tile identity lives in `FVoxelTileKey` (VoxelTypes.h:326).
|
|||||||
### 3.5 World orchestrator — `Public/VoxelWorld.h` + `Private/VoxelWorld.cpp`
|
### 3.5 World orchestrator — `Public/VoxelWorld.h` + `Private/VoxelWorld.cpp`
|
||||||
`AVoxelWorld : AActor` — owns everything, drives streaming. Also `FChunkResult` struct
|
`AVoxelWorld : AActor` — owns everything, drives streaming. Also `FChunkResult` struct
|
||||||
(VoxelWorld.h:99) = async task payload (coord, chunk, meshdata, LOD, **Epoch**, request timing,
|
(VoxelWorld.h:99) = async task payload (coord, chunk, meshdata, LOD, **Epoch**, request timing,
|
||||||
worker-built stream geometry counts/bytes, desired-set epoch and obsolete/cancellation state).
|
worker-built stream geometry counts/bytes, desired-set epoch and obsolete/cancellation state). Explicit
|
||||||
|
visual-gap audits may add 24 sparse pre-skirt surface witnesses per non-empty tile; normal streaming
|
||||||
|
does not capture or retain them.
|
||||||
|
|
||||||
**Owned objects (UPROPERTY):** `Settings`, `Generator`, `Mesher`, `StrateManager`,
|
**Owned objects (UPROPERTY):** `Settings`, `Generator`, `Mesher`, `StrateManager`,
|
||||||
`DiffLayer`, `ContentManager`, `AtmosphereManager`, `DensityVolume` (VoxelWorld.h:188-228).
|
`DiffLayer`, `ContentManager`, `AtmosphereManager`, `DensityVolume` (VoxelWorld.h:188-228).
|
||||||
@@ -297,24 +299,27 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
|
|||||||
| `PostEditChangeProperty` | 1000 | Editor live-edit hook. |
|
| `PostEditChangeProperty` | 1000 | Editor live-edit hook. |
|
||||||
| `OnObjectModifiedInEditor` | 1022 | Regenerates when a strate asset is edited (if `bLiveEditStrates`). |
|
| `OnObjectModifiedInEditor` | 1022 | Regenerates when a strate asset is edited (if `bLiveEditStrates`). |
|
||||||
| `EndPlay` | 1118 | Sets `bShuttingDown`, **waits for `ActiveTaskCount`→0**, unbinds delegate. |
|
| `EndPlay` | 1118 | Sets `bShuttingDown`, **waits for `ActiveTaskCount`→0**, unbinds delegate. |
|
||||||
| `BeginPlay` | 1429 | Constructs Generator/Mesher/StrateManager/DiffLayer, wires services, seeds. |
|
| `BeginPlay` | 1429 | Constructs Generator/Mesher/StrateManager/DiffLayer, wires services, seeds. Creates `UVoxelDensityVolume` only when `bEnableDensityVolume` is true; otherwise seeds terrain MIDs with the disabled neutral parameters and null volume textures. |
|
||||||
| `Tick` | 4395 | `UpdateChunksAroundPosition(player, heading)` + `ProcessPendingChunks()`; the clean LOD0 latency summary is flushed at `EndPlay`. |
|
| `RebuildStrates` | 724 | Reinitializes the volume only when enabled; when disabled, shuts down and clears any existing volume and reapplies neutral terrain material params. |
|
||||||
|
| `Tick` | 5643 | `UpdateChunksAroundPosition(player, heading)` + `ProcessPendingChunks()`; the clean LOD0 latency summary is flushed at `EndPlay`. Density-volume update/material work is inside the non-null volume branch, so it is absent when disabled. |
|
||||||
| `GetPlayerPosition` | 4831 | Pawn position or zero. |
|
| `GetPlayerPosition` | 4831 | Pawn position or zero. |
|
||||||
|
| `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: digs eight times beside a walking pawn, records each frame's vertical velocity, gate, support tile/readiness, movement mode, then digs underfoot and requires ordinary falling. The accompanying gap audit is scoped to the dig session. |
|
||||||
|
| `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. |
|
||||||
| `ProcessPendingChunks` | 5856 | Drains ProcessQueue under per-frame budget; reads the per-request cancellation token before removing its map entry, drops obsolete/aborted results, then applies each live result via `ApplyTileResult`. |
|
| `ProcessPendingChunks` | 5856 | Drains ProcessQueue under per-frame budget; reads the per-request cancellation token before removing its map entry, drops obsolete/aborted results, then applies each live result via `ApplyTileResult`. |
|
||||||
| `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). |
|
| `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). |
|
||||||
| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. |
|
| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. |
|
||||||
| Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). |
|
| Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). |
|
||||||
| `GenerateTileResult` | — | **Shared worker-side gen** for one tile (async `LoadTile` task + sync `SyncRemeshTile`): sealed-solid proof, out-of-layout air proof, optional ClassifyTile (T1.d) + exact validation only when `voxel.OuterClassifierMode=1`; **default 0 since 2026-09-13** → `GenerateMesh` → `BuildTileStreamSet`; records vertex count and resident stream allocation bytes for the horizon trace. Near the player it can capture the exact pre-edit L0 lattice into the result; a cache-hit sync remesh passes an edited per-call shared lattice. Reads Generator/Mesher only → safe on a worker or the game thread; fills `FChunkResult`, no enqueue. |
|
| `GenerateTileResult` | — | **Shared worker-side gen** for one tile (async `LoadTile` task + sync `SyncRemeshTile`): sealed-solid proof, out-of-layout air proof, optional ClassifyTile (T1.d) + exact validation only when `voxel.OuterClassifierMode=1`; **default 0 since 2026-09-13** → `GenerateMesh` → `BuildTileStreamSet`; records vertex count and resident stream allocation bytes for the horizon trace. Density capture is requested only when the enabled volume can use that L0 tile; with the volume off, neither proof nor classifier is bypassed for capture. The explicit gap audit may request sparse surface witnesses, otherwise none are retained. Near the player it can capture the exact pre-edit L0 lattice into the result; a cache-hit sync remesh passes an edited per-call shared lattice. Reads Generator/Mesher only → safe on a worker or the game thread; fills `FChunkResult`, no enqueue. |
|
||||||
| `RecordTileHash` / `WriteTileHashDump` | — | Headless `-voxel.TileHashDump=<absolute path>` parity instrument. Records the exact `FVoxelMeshData` used by the streaming result, including empty/classified tiles, then writes sorted absolute tile keys, LOD step/cells, content-band limits, settings digest context, and the shared `VoxelGeometryHash` at `EndPlay`. |
|
| `RecordTileHash` / `WriteTileHashDump` | — | Headless `-voxel.TileHashDump=<absolute path>` parity instrument. Records the exact `FVoxelMeshData` used by the streaming result, including empty/classified tiles, then writes sorted absolute tile keys, LOD step/cells, content-band limits, settings digest context, and the shared `VoxelGeometryHash` at `EndPlay`. |
|
||||||
| `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread. Uses `PreEditDensityCache` when the tile is resident in the bounded local cache, replays the current DiffLayer over that exact procedural lattice, then meshes/applies inline so a carve is visible THIS frame. A cache miss uses normal generation; it captures the unedited lattice if the tile needs meshing, while uniform-tile proofs remain enabled. |
|
| `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread. Uses `PreEditDensityCache` when the tile is resident in the bounded local cache, replays the current DiffLayer over that exact procedural lattice, then meshes/applies inline so a carve is visible THIS frame. A cache miss uses normal generation; it captures the unedited lattice if the tile needs meshing, while uniform-tile proofs remain enabled. |
|
||||||
| `PreEditDensityCache` / `VoxelForgePreEditDensityCache::BuildEditedGrid` | — | Per-world cache of the pawn's current L0 tile: exact 35³ pre-edit samples, max **171,500 bytes**. It is pruned when the pawn crosses chunk boundaries and cleared on `RegenerateAllChunks`. `BuildEditedGrid` snapshots touched diff chunks once and reproduces `GetDensityAt`'s final `pre-edit - EvaluateMods` operation before the per-call shared-grid mesher path. |
|
| `PreEditDensityCache` / `VoxelForgePreEditDensityCache::BuildEditedGrid` | — | Per-world cache of the pawn's current L0 tile: exact 35³ pre-edit samples, max **171,500 bytes**. It is pruned when the pawn crosses chunk boundaries and cleared on `RegenerateAllChunks`. `BuildEditedGrid` snapshots touched diff chunks once and reproduces `GetDensityAt`'s final `pre-edit - EvaluateMods` operation before the per-call shared-grid mesher path. |
|
||||||
| `UpdateChunksAroundPosition` | 6870 | Builds desired set, cancels pending keys that left it, sorts from the **true pawn position**, submits the occupied/support LOD0 tile and the next tile along the pawn heading as an absolute `BackgroundHigh` prefix, then loads/unloads and handles LOD changes. **Delta cull**: `BuildDesiredTiles` returns the LEAVERS (stamped `DesiredStamped` map, one sweep) — only those + `TransitionHold` are considered per crossing, not every loaded tile. `BuildDesiredTiles` also applies `RenderDistanceChunks`: the outermost shell widens to cover the distance as level-MaxClipLevel MC tiles (`VF_OuterShell` shared with `IsTileInClipRange` so the cull sees the same horizon), dz pre-clamped to the vertical band. **§9.3 anchors:** also prunes dead `StreamingAnchors` + detects their chunk crossings → rebuilds the desired set when an anchor moves/(un)registers (`bAnchorsMoved`/`bForceDesiredRebuild`), same cadence as player movement. §8.10. |
|
| `UpdateChunksAroundPosition` | 6870 | Builds desired set, cancels pending keys that left it, sorts from the **true pawn position**, submits the occupied/support LOD0 tile and the next tile along the pawn heading as an absolute `BackgroundHigh` prefix, then loads/unloads and handles LOD changes. **Delta cull**: `BuildDesiredTiles` returns the LEAVERS (stamped `DesiredStamped` map, one sweep) — only those + `TransitionHold` are considered per crossing, not every loaded tile. `BuildDesiredTiles` also applies `RenderDistanceChunks`: the outermost shell widens to cover the distance as level-MaxClipLevel MC tiles (`VF_OuterShell` shared with `IsTileInClipRange` so the cull sees the same horizon), dz pre-clamped to the vertical band. **§9.3 anchors:** also prunes dead `StreamingAnchors` + detects their chunk crossings → rebuilds the desired set when an anchor moves/(un)registers (`bAnchorsMoved`/`bForceDesiredRebuild`), same cadence as player movement. §8.10. |
|
||||||
| `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` / `MeasureHeadlessCollisionGateStressTerrain` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The normal spawn-height cvar offsets from the measured density surface; `voxel.TestCollisionGateStressDeepDrop=1` carves a process-local box shaft from above the top to just above the floor tile, extending along the post-landing route so the pawn does not run into the shaft sidewall, temporarily raises only the transient test DiffLayer brush-radius allowance for that fixture, removes only the local pawn's redundant streaming anchor for this isolated repro, proves every sample in two complete outside-ring level-0 tiles is air before seeding their known-empty status, prewarms the known-empty in-ring column, and asserts that the support walk reaches the in-layout floor outside the aligned level-0 render ring before the pawn drops. The log reports whether the fixed gate retains that otherwise out-of-ring target. Other streaming anchors remain active. The fixture rejects a target or floor outside the layout, a non-air empty-tile proof, or a floor inside the aligned level-0 ring. The `GetDensityAt` column at capsule feet defines the local air/solid surface; the deep-drop column crossing is cached after its first exact scan. Each frame records actor position, velocity, direct foot tile and gate support readiness, gate state, signed feet clearance, and a ≤0.5-voxel sweep between positions. PASS requires no measured clearance below −5 cm; deep-drop gate-on also requires horizontal travel during a hold. Result logs include the terminal density column, first crossing below spawn height, hold count/time, and gated horizontal progress. |
|
| `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` / `MeasureHeadlessCollisionGateStressTerrain` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The normal spawn-height cvar offsets from the measured density surface; `voxel.TestCollisionGateStressDeepDrop=1` carves a process-local box shaft from above the top to just above the floor tile, extending along the post-landing route so the pawn does not run into the shaft sidewall, temporarily raises only the transient test DiffLayer brush-radius allowance for that fixture, removes only the local pawn's redundant streaming anchor for this isolated repro, proves every sample in two complete outside-ring level-0 tiles is air before seeding their known-empty status, prewarms the known-empty in-ring column, and asserts that the support walk reaches the in-layout floor outside the aligned level-0 render ring before the pawn drops. The log reports whether the fixed gate retains that otherwise out-of-ring target. Other streaming anchors remain active. The fixture rejects a target or floor outside the layout, a non-air empty-tile proof, or a floor inside the aligned level-0 ring. The `GetDensityAt` column at capsule feet defines the local air/solid surface; the deep-drop column crossing is cached after its first exact scan. Each frame records actor position, velocity, direct foot tile and gate support readiness, gate state, signed feet clearance, and a ≤0.5-voxel sweep between positions. PASS requires no measured clearance below −5 cm; deep-drop gate-on also requires horizontal travel during a hold. Result logs include the terminal density column, first crossing below spawn height, hold count/time, and gated horizontal progress. |
|
||||||
| `BuildDesiredTiles` | 6482 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells), then adds streaming anchors and the pending player's collision-only support column before the leaver sweep. It orders from the true local pawn position and builds `CriticalDesiredTiles` for the unresolved support target, occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants) for the reconcile pass. |
|
| `BuildDesiredTiles` | 6482 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells), then adds streaming anchors and the pending player's collision-only support column before the leaver sweep. It orders from the true local pawn position and builds `CriticalDesiredTiles` for the unresolved support target, occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants, including face/edge-touching replacements) for the reconcile pass. |
|
||||||
| `CollisionGateDesiredTiles` / `RequestCollisionGateSupportTile` | 5062 | Retains an unresolved support target and up to three level-0 tiles below it across desired-set rebuilds. The additional keys are hidden collision-only tiles, queued ahead of ordinary streaming, and pruned once resolved or outside the active column. |
|
| `CollisionGateDesiredTiles` / `RequestCollisionGateSupportTile` | 5062 | Retains an unresolved support target and up to three level-0 tiles below it across desired-set rebuilds. The additional keys are hidden collision-only tiles, queued ahead of ordinary streaming, and pruned once resolved or outside the active column. |
|
||||||
| `IsPawnCapsuleOverlappingPendingFloor` / `UpdatePawnCollisionGate` | 4987 / 5195 | Uses the current-to-next-step swept capsule bounds to start a hold only when the pending support tile is about to intersect the pawn. When current support is ready, it also requests and checks the next-step support tile before a horizontal boundary crossing. Character holds use gravity-free `MOVE_Flying` with horizontal input live, then restore movement mode, gravity, and saved downward speed when collision is ready. |
|
| `IsPawnCapsuleOverlappingPendingFloor` / `UpdatePawnCollisionGate` | 4987 / 5195 | Uses the current-to-next-step swept capsule bounds to start a hold only when the pending support tile is about to intersect the pawn. When current support is ready, it also requests and checks the next-step support tile before a horizontal boundary crossing. Character holds use gravity-free `MOVE_Flying` with horizontal input live, then restore movement mode, gravity, and saved downward speed when collision is ready. |
|
||||||
| `VoxelClipmapDesiredTiles::Build` / `AlignedRingBounds` (`Private/VoxelClipmapDesiredTiles.h`) | — | The pure clipmap selector. **Every finer ring snaps outward to a child-pair boundary** (`AlignedRingBounds`, alignment 2; the outermost ring needs none), so a coarser tile is wholly inside or wholly outside the finer ring: **no two desired tiles of different levels overlap** (an unaligned 7-wide odd ring leaves one half-covered coarse tile per axis side, drawn over the fine one: 635 pairs at the owner config). Cost: LOD0 ring 343 → 512 tiles. Guarded by `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage`. Anchor tiles appended later are outside this guarantee. |
|
| `VoxelClipmapDesiredTiles::Build` / `AlignedRingBounds` (`Private/VoxelClipmapDesiredTiles.h`) | — | The pure clipmap selector. **Every finer ring snaps outward to a child-pair boundary** (`AlignedRingBounds`, alignment 2; the outermost ring needs none), so a coarser tile is wholly inside or wholly outside the finer ring: **no two desired tiles of different levels overlap** (an unaligned 7-wide odd ring leaves one half-covered coarse tile per axis side, drawn over the fine one: 635 pairs at the owner config). Cost: LOD0 ring 343 → 512 tiles. Guarded by `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage`. Anchor tiles appended later are outside this guarantee. |
|
||||||
| `ReconcileReadyTransitionVisibility` | — | Visibility-only pass while streaming is unsettled: hides a retired tile once all desired tiles over its footprint are loaded, reveals new tiles once no visible retired tile overlaps them. **Indexed** (dyadic ancestors + `DesiredTransitionDescendants`, visible retired tiles collected once per pass): moving p95 32 ms → 0.31 ms. `voxel.MeasureReadyTransitionVisibility=1` logs per-frame samples. |
|
| `VF_AppendTouchingAncestors` / `ReconcileReadyTransitionVisibility` | 5001 / 7775 | Boundary-safe visibility handoff: a desired tile sharing a closed face/edge with a retired tile counts as a replacement, so the old surface remains visible until the boundary neighbor is loaded. `NeedsHold` uses the same contact-inclusive readiness. Contact does not imply volumetric overlap; overlap audit remains strict. **Indexed** (dyadic ancestors + `DesiredTransitionDescendants`, visible retired tiles collected once per pass): moving p95 32 ms → 0.31 ms. `voxel.MeasureReadyTransitionVisibility=1` logs per-frame samples. |
|
||||||
| `IsTileCollisionDesired` / `ReconcileCollisionDesiredTiles` / `ApplyMeshToTile` | — | Collision is independent of rendering: the player's level-0 `PawnCollisionRadiusChunks` box, each anchor's authored box, and the gate's retained support column union into `CollisionDesiredTiles`. Level-0 meshes outside that set remain visible with `NoCollision` and do not submit an RMC cook. Entering the set queues a remesh for already-loaded render tiles; leaving disables their component immediately; a late cook cannot restore readiness outside the set. |
|
| `IsTileCollisionDesired` / `ReconcileCollisionDesiredTiles` / `ApplyMeshToTile` | — | Collision is independent of rendering: the player's level-0 `PawnCollisionRadiusChunks` box, each anchor's authored box, and the gate's retained support column union into `CollisionDesiredTiles`. Level-0 meshes outside that set remain visible with `NoCollision` and do not submit an RMC cook. Entering the set queues a remesh for already-loaded render tiles; leaving disables their component immediately; a late cook cannot restore readiness outside the set. |
|
||||||
| `VoxelCollisionDesiredTiles::AddBox` (`Private/VoxelCollisionDesiredTiles.h`) | — | Shared pure box selector for the pawn collision radius and anchor extents; tested for pawn radius bounds and an independent anchor box. |
|
| `VoxelCollisionDesiredTiles::AddBox` (`Private/VoxelCollisionDesiredTiles.h`) | — | Shared pure box selector for the pawn collision radius and anchor extents; tested for pawn radius bounds and an independent anchor box. |
|
||||||
| `RequestCollisionGateSupportTile` / `UpdatePawnCollisionGate` | — | The gate submits the pawn's level-0 support tile **immediately** as `CollisionCritical`, even before the first desired set exists, and retains up to three more support-column tiles across desired-set rebuilds. A previously rendered no-collision tile is remeshed before the gate can accept it. The gate holds only at swept contact, keeps horizontal input live, has no timeout, and restores movement mode, gravity and saved fall speed on release. |
|
| `RequestCollisionGateSupportTile` / `UpdatePawnCollisionGate` | — | The gate submits the pawn's level-0 support tile **immediately** as `CollisionCritical`, even before the first desired set exists, and retains up to three more support-column tiles across desired-set rebuilds. A previously rendered no-collision tile is remeshed before the gate can accept it. The gate holds only at swept contact, keeps horizontal input live, has no timeout, and restores movement mode, gravity and saved fall speed on release. |
|
||||||
@@ -323,7 +328,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
|
|||||||
| `RegisterStreamingAnchor` / `UnregisterStreamingAnchor` | — | **BlueprintCallable §9.3:** add/remove an actor as a streaming anchor (`EVoxelAnchorPolicy` CollisionOnly/FullVisual + XY/ZBelow/ZAbove box). Idempotent; forces a rebuild next Tick. §9.4: CollisionOnly tiles cook collision but are hidden (no draw/VSM) unless the player clipmap wants them too. |
|
| `RegisterStreamingAnchor` / `UnregisterStreamingAnchor` | — | **BlueprintCallable §9.3:** add/remove an actor as a streaming anchor (`EVoxelAnchorPolicy` CollisionOnly/FullVisual + XY/ZBelow/ZAbove box). Idempotent; forces a rebuild next Tick. §9.4: CollisionOnly tiles cook collision but are hidden (no draw/VSM) unless the player clipmap wants them too. |
|
||||||
| `LoadTile` | 7291 | Budget check → `UE::Tasks::Launch` background gen+mesh; RAII task guard. Worker runs `Generator->ClassifyTile` first (T1.d): AllSolid/AllAir ⇒ skip `GenerateMesh`, tile stays empty (capture tiles always generate). It carries `GenerationEpoch`/desired epoch and checks `bShuttingDown`, generation pause, and its read-only cancellation token between expensive stages; obsolete work returns through `ProcessQueue`. STRATE CONTENT CUT: tiles ≥ `StrateContentCutMinLevel` pass the player-strate band (`MeshBandChunkLo/Hi` → voxels) to `GenerateMesh` + stamp it on `FChunkResult::BandChunkLo/Hi`; band change re-queues via `BandRemeshQueue` (see `UpdateChunksAroundPosition`). TOO-COARSE SKIP: if one cell is taller than the band (`Step > band height` — level ≥7 territory) the tile is enqueued EMPTY without launching a task (cell-granular cut could only render garbage). §8.10. |
|
| `LoadTile` | 7291 | Budget check → `UE::Tasks::Launch` background gen+mesh; RAII task guard. Worker runs `Generator->ClassifyTile` first (T1.d): AllSolid/AllAir ⇒ skip `GenerateMesh`, tile stays empty (capture tiles always generate). It carries `GenerationEpoch`/desired epoch and checks `bShuttingDown`, generation pause, and its read-only cancellation token between expensive stages; obsolete work returns through `ProcessQueue`. STRATE CONTENT CUT: tiles ≥ `StrateContentCutMinLevel` pass the player-strate band (`MeshBandChunkLo/Hi` → voxels) to `GenerateMesh` + stamp it on `FChunkResult::BandChunkLo/Hi`; band change re-queues via `BandRemeshQueue` (see `UpdateChunksAroundPosition`). TOO-COARSE SKIP: if one cell is taller than the band (`Step > band height` — level ≥7 territory) the tile is enqueued EMPTY without launching a task (cell-granular cut could only render garbage). §8.10. |
|
||||||
| `UnloadTile` | — | Clears tile state; marks any matching generation request obsolete without removing its pending slot, so a late result cannot clear a replacement request; the component is PARKED in the pool (T2.c), not destroyed. |
|
| `UnloadTile` | — | Clears tile state; marks any matching generation request obsolete without removing its pending slot, so a late result cannot clear a replacement request; the component is PARKED in the pool (T2.c), not destroyed. |
|
||||||
| `ApplyMeshToTile` | — | Upload geometry. One component per tile (clipmap keeps count low; supersedes the old region batching); worker-built streams (T1.f) → `CreateSectionGroup(MoveTemp)`. Reuses the component's existing `URealtimeMesh` (no per-apply mesh alloc). **F17: two polygroups** (0 ground / 1 sky-cap, per-triangle class from the mesher) → RMC auto-section per non-empty group; slot 0 = override/default material, slot 1 = `CeilingMaterial` (fallback ground); config gated by `FChunkResult::bHasGroundTris/bHasCeilingTris`. Takes `FChunkResult&`; strate lookups clamp Z into `Result.BandChunkLo/Hi` (strate content cut) — ground at clamped bottom chunk, cap at clamped top (mid = gap fallback). Collision level-0 only (T1.c) both groups; shadow per SECTION: ground casts at level≤1, cap never. **§9.4:** `SetVisibility(false)` when the tile is in `CollisionOnlyTiles` (anchor-only, hidden — collision still cooks). §8.10 + ARCHITECTURE SurfaceWorld row. |
|
| `ApplyMeshToTile` | 9667 | Upload geometry. One component per tile (clipmap keeps count low; supersedes the old region batching); worker-built streams (T1.f) → `CreateSectionGroup(MoveTemp)`. Reuses the component's existing `URealtimeMesh` (no per-apply mesh alloc). **F17: two polygroups** (0 ground / 1 sky-cap, per-triangle class from the mesher) → RMC auto-section per non-empty group; slot 0 = override/default material, slot 1 = `CeilingMaterial` (fallback ground); config gated by `FChunkResult::bHasGroundTris/bHasCeilingTris`. Takes `FChunkResult&`; strate lookups clamp Z into `Result.BandChunkLo/Hi` (strate content cut) — ground at clamped bottom chunk, cap at clamped top (mid = gap fallback). Collision level-0 only (T1.c) both groups; shadow per SECTION: ground casts at level≤1, cap never. During a same-tile re-cook it retains readiness only while collision is still desired and the existing RMC body setup/collision remain valid; a first cook or out-of-range tile withdraws readiness. Routes each terrain base through shared MIDs; disabled volume MIDs bind neutral `VolP*` and null `VolTex*`. **§9.4:** `SetVisibility(false)` when the tile is in `CollisionOnlyTiles` (anchor-only, hidden — collision still cooks). §8.10 + ARCHITECTURE SurfaceWorld row. |
|
||||||
| `AcquireTileComponent` / `ReleaseTileComponent` | — | **T2.c component pool** (`TileComponentPool`, bounded): park on unload (geometry+collision stripped, hidden, stays registered), pop on apply — no `NewObject`/`RegisterComponent`/GC churn during travel & regen. §8.10. |
|
| `AcquireTileComponent` / `ReleaseTileComponent` | — | **T2.c component pool** (`TileComponentPool`, bounded): park on unload (geometry+collision stripped, hidden, stays registered), pop on apply — no `NewObject`/`RegisterComponent`/GC churn during travel & regen. §8.10. |
|
||||||
| `GetStrateAtPosition` | 8351 | Gameplay query → strate index. |
|
| `GetStrateAtPosition` | 8351 | Gameplay query → strate index. |
|
||||||
| `GetBiomeAtWorldLocation` | — | **BlueprintCallable** biome probe at a world point (undoes actor xf → voxel → `Generator::QueryBiomeAt`). Returns `FVoxelBiomeQuery` for BP debug ("what biome / how many decos under the cursor?"). |
|
| `GetBiomeAtWorldLocation` | — | **BlueprintCallable** biome probe at a world point (undoes actor xf → voxel → `Generator::QueryBiomeAt`). Returns `FVoxelBiomeQuery` for BP debug ("what biome / how many decos under the cursor?"). |
|
||||||
@@ -690,6 +695,7 @@ The pre-sampled grid supplies positions AND gradients inline (T1.b).
|
|||||||
| Method | .cpp line | Role |
|
| Method | .cpp line | Role |
|
||||||
|--------|-----------|------|
|
|--------|-----------|------|
|
||||||
| **`GenerateMesh`** | 96 | The MC loop over cells; `Step` controls LOD sampling. Sets the T2.b octave bias for the tile (`TGuardValue` on `VoxelGenLOD::OctaveBias`, from `LODOctaveDrop` × log2(Step); 0 at LOD0/off). Edge `t` + grid-gradient normals computed inline (`SampleG`/`GradAt`). Optional `OutCaptureGrid` (4th arg) captures the already-sampled `CHUNK_SIZE³` R8 density clipmap. Optional `OutPreEditDensityGrid` captures exact float samples before player edits for full-res L0 tiles; optional `InSharedDensityGrid` is per-call (not the mesher's shared mutable setting) so sync carve reuse cannot race async tile tasks. Capture/reuse leaves §8.10's thread-local scratch, two-pass MC and density-grid semantics intact. **F17 surface class**: each unique vertex is classified sol/sky-cap in `GetOrCreateVertex` (down-facing only → memoized `GetSurfaceHeightAt`, nearer `CeilSurf` = cap); triangles bucket by majority into `GroundTris`/`CapTris` (thread_local), skirts emit per bucket, then `Triangles = ground‖cap` + `FVoxelMeshData::NumCeilingTriangles` (→ polygroups in `BuildTileStreamSet`). Same tris, only index ORDER changes. STRATE CONTENT CUT: optional `BandZMin/MaxVox` params restrict the cz cell loop (+ gz sampling rows) to the player-strate band — coarse straddling tiles mesh ONE strate; meshed cells bit-identical. |
|
| **`GenerateMesh`** | 96 | The MC loop over cells; `Step` controls LOD sampling. Sets the T2.b octave bias for the tile (`TGuardValue` on `VoxelGenLOD::OctaveBias`, from `LODOctaveDrop` × log2(Step); 0 at LOD0/off). Edge `t` + grid-gradient normals computed inline (`SampleG`/`GradAt`). Optional `OutCaptureGrid` (4th arg) captures the already-sampled `CHUNK_SIZE³` R8 density clipmap. Optional `OutPreEditDensityGrid` captures exact float samples before player edits for full-res L0 tiles; optional `InSharedDensityGrid` is per-call (not the mesher's shared mutable setting) so sync carve reuse cannot race async tile tasks. Capture/reuse leaves §8.10's thread-local scratch, two-pass MC and density-grid semantics intact. **F17 surface class**: each unique vertex is classified sol/sky-cap in `GetOrCreateVertex` (down-facing only → memoized `GetSurfaceHeightAt`, nearer `CeilSurf` = cap); triangles bucket by majority into `GroundTris`/`CapTris` (thread_local), skirts emit per bucket, then `Triangles = ground‖cap` + `FVoxelMeshData::NumCeilingTriangles` (→ polygroups in `BuildTileStreamSet`). Same tris, only index ORDER changes. STRATE CONTENT CUT: optional `BandZMin/MaxVox` params restrict the cz cell loop (+ gz sampling rows) to the player-strate band — coarse straddling tiles mesh ONE strate; meshed cells bit-identical. |
|
||||||
|
| `FVoxelMeshData::NumSurfaceVertices` | 375 | Prefix length for density-isosurface vertices before seam skirt vertices are appended. The test-only visual-gap audit samples this prefix so artificial skirts cannot become expected terrain points. |
|
||||||
|
|
||||||
**`Public/MarchingCubesTables.h`** — `EdgeTable` + `TriTable` reference data (Paul
|
**`Public/MarchingCubesTables.h`** — `EdgeTable` + `TriTable` reference data (Paul
|
||||||
Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs editing.
|
Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs editing.
|
||||||
@@ -699,7 +705,7 @@ Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs ed
|
|||||||
|------|------|
|
|------|------|
|
||||||
| `Public/Private/VoxelContentManager.h/.cpp` | `UVoxelContentManager` — distance-based world-grid decoration scatter (no LOD pop, surface-snapped via `GetDensityAt`) + level-0 water planes. **F7 companions:** each `FStrateDecoration` may list `FDecoCompanion` satellites (rocks/mushrooms around a tree) — emitted per placed parent in `PlaceAtCrossing`, deterministic (pure fn of the parent hash), inherit the parent's surface point; `FDecoSpawn::CompanionIdx` routes each back to its `Companions[ci].Profile` at apply. **TWO streaming grids** (`FDecoGrid` Near/Far, picked per entry via `FStrateDecoration::StreamTier`): NearGrid = short radius + fine column grid (groundcover); FarGrid = full radius + coarse grid (cheap rare/large props). **Plus `UpdateLandmarks`** — rare deliberately-placed objects: mini-suns, ruins, shrines, monuments (`FStrateLandmark`; set-pieces folded in 2026-07-06). Per entry an `AnchorMode`: **HashLattice** (coarse hash lattice, cell = SpacingChunks chunks → cheap at any radius, no per-chunk freeze) or **PassageMouth** (`GetPassages()` endpoints landing in this strate). Gated by `Profile` + `Profile.Conditions`; optional deterministic **exclusion radius** (Priority+hash rank, `ExclusionRadiusChunks`; 0 = off → pure scatter is unchanged, skips the O(n²) resolve). Synchronous, deterministic, strate-wide; spawns via `SpawnFromProfile` (+ orb wrapper). Exclusion is POP-FREE (gathers a `MaxExcl` ring of suppress-only candidates so a piece's fate is player-position-independent). `bSuppressDecorationsUnder`/`SuppressRadiusChunks` clear grass under a footprint (on spawn + re-cleared in `ApplyRegion`). **`RemoveDecorationsInSphere`** (world sphere → both grids' HISMs via `GetInstancesOverlappingSphere`+`RemoveInstances`) is the shared primitive, also called by `AVoxelWorld::ApplyModification` so player digging removes floating grass instantly. Owned by `AVoxelWorld`. §8.5. |
|
| `Public/Private/VoxelContentManager.h/.cpp` | `UVoxelContentManager` — distance-based world-grid decoration scatter (no LOD pop, surface-snapped via `GetDensityAt`) + level-0 water planes. **F7 companions:** each `FStrateDecoration` may list `FDecoCompanion` satellites (rocks/mushrooms around a tree) — emitted per placed parent in `PlaceAtCrossing`, deterministic (pure fn of the parent hash), inherit the parent's surface point; `FDecoSpawn::CompanionIdx` routes each back to its `Companions[ci].Profile` at apply. **TWO streaming grids** (`FDecoGrid` Near/Far, picked per entry via `FStrateDecoration::StreamTier`): NearGrid = short radius + fine column grid (groundcover); FarGrid = full radius + coarse grid (cheap rare/large props). **Plus `UpdateLandmarks`** — rare deliberately-placed objects: mini-suns, ruins, shrines, monuments (`FStrateLandmark`; set-pieces folded in 2026-07-06). Per entry an `AnchorMode`: **HashLattice** (coarse hash lattice, cell = SpacingChunks chunks → cheap at any radius, no per-chunk freeze) or **PassageMouth** (`GetPassages()` endpoints landing in this strate). Gated by `Profile` + `Profile.Conditions`; optional deterministic **exclusion radius** (Priority+hash rank, `ExclusionRadiusChunks`; 0 = off → pure scatter is unchanged, skips the O(n²) resolve). Synchronous, deterministic, strate-wide; spawns via `SpawnFromProfile` (+ orb wrapper). Exclusion is POP-FREE (gathers a `MaxExcl` ring of suppress-only candidates so a piece's fate is player-position-independent). `bSuppressDecorationsUnder`/`SuppressRadiusChunks` clear grass under a footprint (on spawn + re-cleared in `ApplyRegion`). **`RemoveDecorationsInSphere`** (world sphere → both grids' HISMs via `GetInstancesOverlappingSphere`+`RemoveInstances`) is the shared primitive, also called by `AVoxelWorld::ApplyModification` so player digging removes floating grass instantly. Owned by `AVoxelWorld`. §8.5. |
|
||||||
| `Public/Private/VoxelAtmosphereManager.h/.cpp` | `UVoxelAtmosphereManager` — per-strate fog/skylight + persistent ceiling/floor layer actors + full `AtmosphereActor` override. Owned by `AVoxelWorld`. §8.6. |
|
| `Public/Private/VoxelAtmosphereManager.h/.cpp` | `UVoxelAtmosphereManager` — per-strate fog/skylight + persistent ceiling/floor layer actors + full `AtmosphereActor` override. Owned by `AVoxelWorld`. §8.6. |
|
||||||
| `Public/Private/VoxelDensityVolume.h/.cpp` | `UVoxelDensityVolume` — player-centred DENSITY CLIPMAP (N toroidal R8 levels, fine near / coarse far) streamed to GPU `UVolumeTexture`s for the mini-sun raymarched shadow march. Fills run on ONE dedicated thread (`FVoxelDensityFillRunnable`, off the task pool); level 0 is mostly fed by CAPTURE-DURING-MESHING (mesher grid reuse, gated by `IsTileCaptureUseful` so only tiles near the shadow window pay the capture). Carve → `MarkDirtyVoxelBox` refills locally. `VolumeEpoch` drops stale fills. Owned by `AVoxelWorld` (`bEnableDensityVolume`); shader params pushed via shared per-base-material MIDs (`AVoxelWorld::UpdateTerrainMaterialParams`, change-detected). |
|
| `Public/Private/VoxelDensityVolume.h/.cpp` | `UVoxelDensityVolume` — opt-in player-centred DENSITY CLIPMAP (`bEnableDensityVolume` defaults false) with N toroidal R8 levels streamed to GPU `UVolumeTexture`s for mini-sun shadows. When enabled, fills run on ONE dedicated thread (`FVoxelDensityFillRunnable`); level 0 is mostly fed by CAPTURE-DURING-MESHING (gated by `IsTileCaptureUseful`), and carve → `MarkDirtyVoxelBox` refills locally. `VolumeEpoch` drops stale fills. When disabled, no object/thread/fill/upload/Tick update/capture/proof bypass exists; `SetDensityVolumeMaterialParamsDisabled` sets `VolP0..4`/`VolP6..9` black, `VolP5=(0,25,0)`, and `VolTex0..2` null on shared per-base MIDs. `SetVolumeParamsOnMID` carries these values to newly created MIDs. |
|
||||||
|
|
||||||
> The big 2026 redesign (8 archetypes, (0,0) spine, inter-strate gap, per-strate passages,
|
> The big 2026 redesign (8 archetypes, (0,0) spine, inter-strate gap, per-strate passages,
|
||||||
> disturbances, content/atmosphere, brush shapes, perf invariants) is documented in **§8** —
|
> disturbances, content/atmosphere, brush shapes, perf invariants) is documented in **§8** —
|
||||||
|
|||||||
@@ -1065,6 +1065,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
|||||||
// Émis en DOUBLE FACE (deux orientations) pour s'afficher quel que soit le côté caméra / le
|
// Émis en DOUBLE FACE (deux orientations) pour s'afficher quel que soit le côté caméra / le
|
||||||
// matériau. Une arête de surface posée sur une face de frontière a sa coordonnée d'axe EXACTE
|
// matériau. Une arête de surface posée sur une face de frontière a sa coordonnée d'axe EXACTE
|
||||||
// (l'interpolation MC garde fixe l'axe de la face) → comparaison flottante exacte fiable.
|
// (l'interpolation MC garde fixe l'axe de la face) → comparaison flottante exacte fiable.
|
||||||
|
MeshData.NumSurfaceVertices = MeshData.Vertices.Num();
|
||||||
if (bGenerateSkirts && (GroundTris.Num() + CapTris.Num()) > 0)
|
if (bGenerateSkirts && (GroundTris.Num() + CapTris.Num()) > 0)
|
||||||
{
|
{
|
||||||
const float ExtentCm = (float)(CellsPerAxis * Step) * VOXEL_SIZE;
|
const float ExtentCm = (float)(CellsPerAxis * Step) * VOXEL_SIZE;
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -305,7 +305,7 @@ public:
|
|||||||
|
|
||||||
// Master switch. OFF = no volume built, no fill tasks, no GPU cost (terrain unlit by orbs).
|
// Master switch. OFF = no volume built, no fill tasks, no GPU cost (terrain unlit by orbs).
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting")
|
||||||
bool bEnableDensityVolume = true;
|
bool bEnableDensityVolume = false;
|
||||||
|
|
||||||
// Per-axis resolution of EACH clip level (cells). Memory per level ≈ Res³ bytes (R8). 128 →
|
// Per-axis resolution of EACH clip level (cells). Memory per level ≈ Res³ bytes (R8). 128 →
|
||||||
// ~2 MB/level; 192 → ~7 MB; 256 → ~16 MB. Higher = crisper near shadows + bigger startup fill.
|
// ~2 MB/level; 192 → ~7 MB; 256 → ~16 MB. Higher = crisper near shadows + bigger startup fill.
|
||||||
|
|||||||
@@ -370,6 +370,9 @@ struct FVoxelMeshData
|
|||||||
TArray<FVector> Normals; // Normale lissée (gradient de densité)
|
TArray<FVector> Normals; // Normale lissée (gradient de densité)
|
||||||
TArray<FColor> Colors; // Masques matériau F6 (R=palette biome dominant, G=pente,
|
TArray<FColor> Colors; // Masques matériau F6 (R=palette biome dominant, G=pente,
|
||||||
// B=poids de fondu de bordure, A=palette biome voisin)
|
// B=poids de fondu de bordure, A=palette biome voisin)
|
||||||
|
// Number of vertices on the actual density isosurface. LOD seam skirts are appended after
|
||||||
|
// this prefix and must not be used as witnesses for terrain visibility audits.
|
||||||
|
int32 NumSurfaceVertices = 0;
|
||||||
|
|
||||||
// F17 — classe de surface par TRIANGLE, portée par l'ORDRE du buffer d'indices : les
|
// F17 — classe de surface par TRIANGLE, portée par l'ORDRE du buffer d'indices : les
|
||||||
// NumCeilingTriangles DERNIERS triangles de Triangles sont la classe "plafond sky-cap"
|
// NumCeilingTriangles DERNIERS triangles de Triangles sont la classe "plafond sky-cap"
|
||||||
@@ -384,6 +387,7 @@ struct FVoxelMeshData
|
|||||||
UVs.Empty();
|
UVs.Empty();
|
||||||
Normals.Empty();
|
Normals.Empty();
|
||||||
Colors.Empty();
|
Colors.Empty();
|
||||||
|
NumSurfaceVertices = 0;
|
||||||
NumCeilingTriangles = 0;
|
NumCeilingTriangles = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ struct FChunkResult
|
|||||||
// Exact pre-edit level-0 lattice for the bounded local-player dig cache. Sampled on the worker
|
// Exact pre-edit level-0 lattice for the bounded local-player dig cache. Sampled on the worker
|
||||||
// with the tile's normal generator context and moved through the result queue.
|
// with the tile's normal generator context and moved through the result queue.
|
||||||
FVoxelSharedDensityGrid PreEditDensityGrid;
|
FVoxelSharedDensityGrid PreEditDensityGrid;
|
||||||
|
// Sparse points on the prior surface, used only by the explicit rendered-coverage audit.
|
||||||
|
// Captured from the mesher output and kept out of normal streaming results.
|
||||||
|
bool bCaptureSurfaceWitnesses = false;
|
||||||
|
TArray<FVector> SurfaceWitnesses;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** One worker-produced mesh identity emitted only by the explicit parity harness. */
|
/** One worker-produced mesh identity emitted only by the explicit parity harness. */
|
||||||
@@ -223,17 +227,16 @@ public:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
UVoxelAtmosphereManager* AtmosphereManager;
|
UVoxelAtmosphereManager* AtmosphereManager;
|
||||||
|
|
||||||
/** Player-centred density CLIPMAP streamed onto the GPU for the mini-sun raymarched shadow
|
/** Optional player-centred density CLIPMAP for mini-sun raymarched shadows. It is absent when
|
||||||
* system (forward rendering). Created in BeginPlay when Settings->bEnableDensityVolume is on.
|
* Settings->bEnableDensityVolume is false, so no fill thread or texture uploads run. */
|
||||||
* Filled on worker threads (re-evaluating GetDensityAt), recentred toroidally as the player
|
|
||||||
* moves, refilled locally on carve. See UVoxelDensityVolume. */
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
UVoxelDensityVolume* DensityVolume;
|
UVoxelDensityVolume* DensityVolume;
|
||||||
|
|
||||||
/** Shared Material Instance Dynamics that bind the density-volume textures + per-frame shadow params
|
/** Shared Material Instance Dynamics that bind the density-volume textures + per-frame shadow params
|
||||||
* (clipmap transform + nearest orb) onto the terrain material(s). Keyed by BASE material so every
|
* (clipmap transform + nearest orb) onto the terrain material(s). Keyed by BASE material so every
|
||||||
* tile of a given base shares ONE MID (no batching cost). Created lazily in ApplyMeshToTile,
|
* tile of a given base shares ONE MID (no batching cost). Created lazily in ApplyMeshToTile;
|
||||||
* refreshed each Tick by UpdateTerrainMaterialParams. */
|
* refreshed each Tick only while DensityVolume exists. When disabled, each MID receives neutral
|
||||||
|
* VolP* values and null VolTex* bindings at creation. */
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TMap<TObjectPtr<UMaterialInterface>, TObjectPtr<UMaterialInstanceDynamic>> TerrainMIDs;
|
TMap<TObjectPtr<UMaterialInterface>, TObjectPtr<UMaterialInstanceDynamic>> TerrainMIDs;
|
||||||
|
|
||||||
@@ -541,10 +544,15 @@ private:
|
|||||||
/** Apply the current TVP0..4 + level-0 volume texture to one MID (also used on MID creation). */
|
/** Apply the current TVP0..4 + level-0 volume texture to one MID (also used on MID creation). */
|
||||||
void SetVolumeParamsOnMID(UMaterialInstanceDynamic* MID) const;
|
void SetVolumeParamsOnMID(UMaterialInstanceDynamic* MID) const;
|
||||||
|
|
||||||
|
/** Put all terrain MIDs into the material's neutral, volume-disabled state. */
|
||||||
|
void SetDensityVolumeMaterialParamsDisabled();
|
||||||
|
|
||||||
/** Explicitly opt-in headless streaming validation. This is a command-line-only harness path;
|
/** Explicitly opt-in headless streaming validation. This is a command-line-only harness path;
|
||||||
* normal gameplay never moves the pawn here. It supplies a repeatable moving session and lets
|
* normal gameplay never moves the pawn here. It supplies a repeatable moving session and lets
|
||||||
* -nullrhi request a graceful EndPlay so the clean latency summary is flushed. */
|
* -nullrhi request a graceful EndPlay so the clean latency summary is flushed. */
|
||||||
void ConfigureHeadlessStreamingTest();
|
void ConfigureHeadlessStreamingTest();
|
||||||
|
void AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APawn* Pawn);
|
||||||
|
void RecordHeadlessDigMotionFrame(APawn* Pawn, const FVector& PlayerPosition, float DeltaTime);
|
||||||
void AdvanceHeadlessStreamingTest(FVector& InOutPlayerPosition,
|
void AdvanceHeadlessStreamingTest(FVector& InOutPlayerPosition,
|
||||||
FVector& InOutPlayerHeading, APawn* PlayerPawn);
|
FVector& InOutPlayerHeading, APawn* PlayerPawn);
|
||||||
void AdvanceHeadlessCollisionGateStressTest(FVector& InOutPlayerPosition,
|
void AdvanceHeadlessCollisionGateStressTest(FVector& InOutPlayerPosition,
|
||||||
|
|||||||
+115
-4
@@ -1,6 +1,6 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'visual-edit', 'visual-lod', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity', 'horizon', 'horizon-render', 'determinism')]
|
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity', 'horizon', 'horizon-render', 'determinism')]
|
||||||
[string]$Scenario = 'canonical',
|
[string]$Scenario = 'canonical',
|
||||||
[switch]$Build,
|
[switch]$Build,
|
||||||
[ValidateSet('owner', 'default')]
|
[ValidateSet('owner', 'default')]
|
||||||
@@ -64,9 +64,11 @@ $SurfaceFallMetrics = [ordered]@{
|
|||||||
}
|
}
|
||||||
$StrateCrossingMetrics = [ordered]@{ requested = $Scenario -eq 'crossing'; status = 'not_run' }
|
$StrateCrossingMetrics = [ordered]@{ requested = $Scenario -eq 'crossing'; status = 'not_run' }
|
||||||
$VisualStalenessMetrics = [ordered]@{
|
$VisualStalenessMetrics = [ordered]@{
|
||||||
requested = $Scenario -in @('visual-edit', 'visual-lod')
|
requested = $Scenario -in @('visual-edit', 'visual-lod', 'visual-gap-walk')
|
||||||
status = 'not_run'
|
status = 'not_run'
|
||||||
}
|
}
|
||||||
|
$VisualGapMetrics = [ordered]@{ requested = $Scenario -in @('dig-motion', 'visual-gap-walk', 'visual-gap-inject'); status = 'not_run' }
|
||||||
|
$DigMotionMetrics = [ordered]@{ requested = $Scenario -eq 'dig-motion'; status = 'not_run' }
|
||||||
$CollisionGateStressMetrics = [ordered]@{
|
$CollisionGateStressMetrics = [ordered]@{
|
||||||
requested = $Scenario -eq 'gate-stress' -or
|
requested = $Scenario -eq 'gate-stress' -or
|
||||||
($Scenario -eq 'tests' -and [string]::IsNullOrWhiteSpace($TestFilter))
|
($Scenario -eq 'tests' -and [string]::IsNullOrWhiteSpace($TestFilter))
|
||||||
@@ -540,7 +542,7 @@ function Sync-OwnerAssets {
|
|||||||
$null = Copy-Item -LiteralPath $source -Destination $target -Force -PassThru
|
$null = Copy-Item -LiteralPath $source -Destination $target -Force -PassThru
|
||||||
[void]$rows.Add([ordered]@{
|
[void]$rows.Add([ordered]@{
|
||||||
name = $assetName
|
name = $assetName
|
||||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'horizon', 'horizon-render', 'determinism')
|
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism')
|
||||||
source_path = $source
|
source_path = $source
|
||||||
source_sha256 = Get-Sha256 $source
|
source_sha256 = Get-Sha256 $source
|
||||||
staged_path = $target
|
staged_path = $target
|
||||||
@@ -588,7 +590,7 @@ function Get-AssetEvidence {
|
|||||||
$target = Join-Path $HostRoot "Content\VoxelForge\$assetName.uasset"
|
$target = Join-Path $HostRoot "Content\VoxelForge\$assetName.uasset"
|
||||||
[void]$rows.Add([ordered]@{
|
[void]$rows.Add([ordered]@{
|
||||||
name = $assetName
|
name = $assetName
|
||||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'horizon', 'horizon-render', 'determinism')
|
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism')
|
||||||
source_path = $source
|
source_path = $source
|
||||||
source_sha256 = Get-Sha256 $source
|
source_sha256 = Get-Sha256 $source
|
||||||
staged_path = $target
|
staged_path = $target
|
||||||
@@ -1066,6 +1068,53 @@ function Get-VisualStalenessMetrics([string]$LogPath) {
|
|||||||
return $metrics
|
return $metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-VisualGapMetrics([string]$LogPath) {
|
||||||
|
$lineMatch = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeVisualGapAudit\] result=' -ErrorAction SilentlyContinue |
|
||||||
|
Select-Object -Last 1)
|
||||||
|
if ($lineMatch.Count -eq 0) { return [ordered]@{ status = 'missing'; log = $LogPath } }
|
||||||
|
$line = [string]$lineMatch[0].Line
|
||||||
|
$metrics = [ordered]@{ status = 'failed'; log = $LogPath; raw = $line }
|
||||||
|
$result = [regex]::Match($line, 'result=(?<v>PASS|FAIL)')
|
||||||
|
if ($result.Success -and $result.Groups['v'].Value -eq 'PASS') { $metrics.status = 'passed' }
|
||||||
|
foreach ($key in @('window_s', 'window_frames', 'camera_view_frames', 'surface_point_frames', 'gap_count', 'gap_frames',
|
||||||
|
'gap_point_frames', 'max_missing_points', 'longest_gap_ms',
|
||||||
|
'longest_gap_frames', 'injected_early_hide', 'injection_caught')) {
|
||||||
|
$match = [regex]::Match($line, ('{0}=(?<v>[^ ]+)' -f $key))
|
||||||
|
if (-not $match.Success) { continue }
|
||||||
|
if ($key -in @('window_frames', 'camera_view_frames', 'surface_point_frames', 'gap_count', 'gap_frames', 'gap_point_frames',
|
||||||
|
'longest_gap_frames',
|
||||||
|
'max_missing_points', 'injected_early_hide', 'injection_caught')) {
|
||||||
|
$metrics[$key] = [int]$match.Groups['v'].Value
|
||||||
|
} else {
|
||||||
|
$metrics[$key] = [double]$match.Groups['v'].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DigMotionMetrics([string]$LogPath) {
|
||||||
|
$lineMatch = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeDigMotion\] result=' -ErrorAction SilentlyContinue |
|
||||||
|
Select-Object -Last 1)
|
||||||
|
if ($lineMatch.Count -eq 0) { return [ordered]@{ status = 'missing'; log = $LogPath } }
|
||||||
|
$line = [string]$lineMatch[0].Line
|
||||||
|
$metrics = [ordered]@{ status = 'failed'; log = $LogPath; raw = $line }
|
||||||
|
$result = [regex]::Match($line, 'result=(?<v>PASS|FAIL)')
|
||||||
|
if ($result.Success -and $result.Groups['v'].Value -eq 'PASS') { $metrics.status = 'passed' }
|
||||||
|
foreach ($key in @('near_digs', 'near_frames', 'near_gate_frames', 'near_falling_frames',
|
||||||
|
'near_vertical_motion_frames', 'under_saw_falling', 'under_gate_frames',
|
||||||
|
'under_fall_cm', 'under_max_downward_velocity_cm_s')) {
|
||||||
|
$match = [regex]::Match($line, ('{0}=(?<v>[^ ]+)' -f $key))
|
||||||
|
if (-not $match.Success) { continue }
|
||||||
|
if ($key -in @('near_digs', 'near_frames', 'near_gate_frames', 'near_falling_frames',
|
||||||
|
'near_vertical_motion_frames', 'under_saw_falling', 'under_gate_frames')) {
|
||||||
|
$metrics[$key] = [int]$match.Groups['v'].Value
|
||||||
|
} else {
|
||||||
|
$metrics[$key] = [double]$match.Groups['v'].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $metrics
|
||||||
|
}
|
||||||
|
|
||||||
function Get-TestMetrics($Ledger) {
|
function Get-TestMetrics($Ledger) {
|
||||||
if ($null -eq $Ledger) { return @() }
|
if ($null -eq $Ledger) { return @() }
|
||||||
$logPaths = @($Ledger.log, $Ledger.stdout, $Ledger.stderr) | Where-Object {
|
$logPaths = @($Ledger.log, $Ledger.stdout, $Ledger.stderr) | Where-Object {
|
||||||
@@ -1455,6 +1504,64 @@ try {
|
|||||||
Add-Failure "visual LOD repro did not pass: $($VisualStalenessMetrics.status)"
|
Add-Failure "visual LOD repro did not pass: $($VisualStalenessMetrics.status)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
'visual-gap-walk' {
|
||||||
|
$baseCvars['voxel.TestVisualGapAudit'] = 1
|
||||||
|
$baseCvars['voxel.TestVisualStaleness'] = 2
|
||||||
|
$baseCvars['voxel.TestMove'] = 1
|
||||||
|
$baseCvars['voxel.TestMoveSpeedCmPerSecond'] = 6000
|
||||||
|
$baseCvars['voxel.TestMoveStartSeconds'] = 15
|
||||||
|
$baseCvars['voxel.TestMoveDirection'] = '1, 0, 0.15'
|
||||||
|
$baseCvars['voxel.TestExitSeconds'] = 90
|
||||||
|
$baseCvars['voxel.TestMaxClipLevel'] = 6
|
||||||
|
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||||
|
$visualLog = Join-Path $RunRoot 'Logs\visual-gap-walk.log'
|
||||||
|
$args = New-CommonArguments 'game' $visualLog (Merge-Cvars $baseCvars)
|
||||||
|
$gameLedger = Invoke-UnrealLaunch 'visual-gap-walk-game' $args `
|
||||||
|
$visualLog (Join-Path $RunRoot 'Logs\visual-gap-walk.stdout.log') (Join-Path $RunRoot 'Logs\visual-gap-walk.stderr.log')
|
||||||
|
$VisualGapMetrics = Get-VisualGapMetrics $visualLog
|
||||||
|
$VisualStalenessMetrics = Get-VisualStalenessMetrics $visualLog
|
||||||
|
if ($VisualGapMetrics.status -ne 'passed') {
|
||||||
|
Add-Failure "visual gap walk audit did not pass: $($VisualGapMetrics.status)"
|
||||||
|
}
|
||||||
|
if ($VisualStalenessMetrics.status -ne 'passed') {
|
||||||
|
Add-Failure "visual LOD overlap audit did not pass: $($VisualStalenessMetrics.status)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'visual-gap-inject' {
|
||||||
|
$baseCvars['voxel.TestVisualGapAudit'] = 1
|
||||||
|
$baseCvars['voxel.TestVisualGapInjectEarlyHide'] = 1
|
||||||
|
$baseCvars['voxel.TestExitSeconds'] = 45
|
||||||
|
$baseCvars['voxel.TestMaxClipLevel'] = 6
|
||||||
|
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||||
|
$visualLog = Join-Path $RunRoot 'Logs\visual-gap-inject.log'
|
||||||
|
$args = New-CommonArguments 'game' $visualLog (Merge-Cvars $baseCvars)
|
||||||
|
$gameLedger = Invoke-UnrealLaunch 'visual-gap-inject-game' $args `
|
||||||
|
$visualLog (Join-Path $RunRoot 'Logs\visual-gap-inject.stdout.log') (Join-Path $RunRoot 'Logs\visual-gap-inject.stderr.log')
|
||||||
|
$VisualGapMetrics = Get-VisualGapMetrics $visualLog
|
||||||
|
if ($VisualGapMetrics.status -ne 'passed' -or $VisualGapMetrics.injection_caught -ne 1) {
|
||||||
|
Add-Failure "visual gap audit failed its injected early-hide sensitivity check: $($VisualGapMetrics.status)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'dig-motion' {
|
||||||
|
$baseCvars['voxel.TestDigMotion'] = 1
|
||||||
|
$baseCvars['voxel.TestDigMotionNearDigCount'] = 8
|
||||||
|
$baseCvars['voxel.TestDigMotionIntervalSeconds'] = 0.35
|
||||||
|
$baseCvars['voxel.TestVisualGapAudit'] = 1
|
||||||
|
$baseCvars['voxel.TestMaxClipLevel'] = 6
|
||||||
|
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||||
|
$digLog = Join-Path $RunRoot 'Logs\dig-motion.log'
|
||||||
|
$args = New-CommonArguments 'game' $digLog (Merge-Cvars $baseCvars)
|
||||||
|
$gameLedger = Invoke-UnrealLaunch 'dig-motion-game' $args `
|
||||||
|
$digLog (Join-Path $RunRoot 'Logs\dig-motion.stdout.log') (Join-Path $RunRoot 'Logs\dig-motion.stderr.log')
|
||||||
|
$DigMotionMetrics = Get-DigMotionMetrics $digLog
|
||||||
|
$VisualGapMetrics = Get-VisualGapMetrics $digLog
|
||||||
|
if ($DigMotionMetrics.status -ne 'passed') {
|
||||||
|
Add-Failure "dig-motion repro did not pass: $($DigMotionMetrics.status)"
|
||||||
|
}
|
||||||
|
if ($VisualGapMetrics.status -ne 'passed') {
|
||||||
|
Add-Failure "dig-session visual gap audit did not pass: $($VisualGapMetrics.status)"
|
||||||
|
}
|
||||||
|
}
|
||||||
'surface-fall' {
|
'surface-fall' {
|
||||||
$baseCvars['voxel.TestSurfaceFall'] = 1
|
$baseCvars['voxel.TestSurfaceFall'] = 1
|
||||||
$baseCvars['voxel.TestSurfaceFallSpawnHeightVoxels'] = 20
|
$baseCvars['voxel.TestSurfaceFallSpawnHeightVoxels'] = 20
|
||||||
@@ -1652,6 +1759,8 @@ $result = [ordered]@{
|
|||||||
surface_fall = $SurfaceFallMetrics
|
surface_fall = $SurfaceFallMetrics
|
||||||
strate_crossing = $StrateCrossingMetrics
|
strate_crossing = $StrateCrossingMetrics
|
||||||
visual_staleness = $VisualStalenessMetrics
|
visual_staleness = $VisualStalenessMetrics
|
||||||
|
visual_gap = $VisualGapMetrics
|
||||||
|
dig_motion = $DigMotionMetrics
|
||||||
collision_gate_stress = $CollisionGateStressMetrics
|
collision_gate_stress = $CollisionGateStressMetrics
|
||||||
worker_seconds = [ordered]@{
|
worker_seconds = [ordered]@{
|
||||||
commandlet = if ($CommandletMetrics.Contains('worker_seconds')) { $CommandletMetrics.worker_seconds } else { $null }
|
commandlet = if ($CommandletMetrics.Contains('worker_seconds')) { $CommandletMetrics.worker_seconds } else { $null }
|
||||||
@@ -1686,6 +1795,8 @@ try {
|
|||||||
[void]$summaryLines.Add("surface_fall=$(ConvertTo-CompactJson $result.surface_fall)")
|
[void]$summaryLines.Add("surface_fall=$(ConvertTo-CompactJson $result.surface_fall)")
|
||||||
[void]$summaryLines.Add("strate_crossing=$(ConvertTo-CompactJson $result.strate_crossing)")
|
[void]$summaryLines.Add("strate_crossing=$(ConvertTo-CompactJson $result.strate_crossing)")
|
||||||
[void]$summaryLines.Add("visual_staleness=$(ConvertTo-CompactJson $result.visual_staleness)")
|
[void]$summaryLines.Add("visual_staleness=$(ConvertTo-CompactJson $result.visual_staleness)")
|
||||||
|
[void]$summaryLines.Add("visual_gap=$(ConvertTo-CompactJson $result.visual_gap)")
|
||||||
|
[void]$summaryLines.Add("dig_motion=$(ConvertTo-CompactJson $result.dig_motion)")
|
||||||
[void]$summaryLines.Add("collision_gate_stress=$(ConvertTo-CompactJson $result.collision_gate_stress)")
|
[void]$summaryLines.Add("collision_gate_stress=$(ConvertTo-CompactJson $result.collision_gate_stress)")
|
||||||
[void]$summaryLines.Add("probe_amplitudes=$(ConvertTo-CompactJson $result.probe_amplitudes)")
|
[void]$summaryLines.Add("probe_amplitudes=$(ConvertTo-CompactJson $result.probe_amplitudes)")
|
||||||
[void]$summaryLines.Add("worker_seconds=$(ConvertTo-CompactJson $result.worker_seconds)")
|
[void]$summaryLines.Add("worker_seconds=$(ConvertTo-CompactJson $result.worker_seconds)")
|
||||||
|
|||||||
Reference in new issue
Block a user