fix(streaming): the player can walk from one strate into the next
Owner playtest: he could not walk from strate A into B. Forcing the pawn through in the editor showed the junction tunnel (and his edits to the lower strate) were there. Cause: UVoxelSettings::StrateContentCutMinLevel defaulted to 0, and DA_Settings does not override it, so the strate content band (the PAWN's strate) also cut LOD0, the collision tiles. While the player stood in A, the passage below A's seal, the gap and strate B had no LOD0 geometry, and the collision gate correctly refused to step onto missing collision. The band only moves once the pawn is inside B: a deadlock. bClampViewToStrate is a different setting, which is why toggling it did not help. Some historical fall-throughs were likely this, before the gate turned "fall" into "blocked". Fix (Codex; reviewed by me): - StrateContentCutMinLevel default 0 -> 1, ClampMin 1, and UVoxelSettings::GetEffectiveStrateContentCutMinLevel() clamps to [1,9] at every runtime consumer, so a legacy asset serialising 0 cannot bring the deadlock back. LOD1+ stay banded inside strates and go full in the gap. - A regression test, -voxel.TestStrateCrossing=1 (off unless passed): it walks the real manager passage control points with AddMovementInput and normal swept character movement, with limits of 10 s max gate, 120 s total and a 2-voxel floor tolerance. Old code: FAIL collision_gate_timeout at route 5/9 (10.0 s gated). Fixed: PASS reached_lower_strate, strate 0 -> 1, max gate 0.092 s. The trace shows band [-7..0] -> full in the gap -> [-17..-10]. Field unchanged: canonical export b3e5f4c3..9b377, capability 20,830 / 10,909. In game, the LOD0 change in the crossing scene is 11 transition-gap tiles going from 0 to 22,390 triangles; no LOD0 interior tile changed. COST, stated plainly (two interleaved runs, baseline vs fixed DLL): static generation CPU 34.1 -> 44.1 s (+29%), density calls +23%, LOD0 request p95 +8.9% moving generation CPU 78.8 -> 91.1 s (+16%), density calls +26%, LOD0 request p95 +3.1% applied triangles identical (669,834 / 1,331,482): the extra work is LOD0 tiles in sealed rock that the old cut skipped. The next round proves them solid instead of sampling them. Visual caveat: the old default of 0 was a deliberate July choice, because LOD0/1 tiles straddling a strate boundary mixed both strates' materials. With LOD0 never cut, that mixing may reappear near seals. The headless runs are -nullrhi, so this still needs checking in the editor. Launches: all serialized and waited by PID. Failures: one malformed first baseline attempt, one over-strict early harness assertion, and one run against stale staging (it logged CutMinLevel=0), all superseded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
1 parent
7ece3ceafc
commit
4c8d06fd8f
5 files changed
+735
-27
No files matched your search
@@ -95,13 +95,26 @@ public:
|
||||
// both strates' materials. From this clip level UP, the mesher only meshes cells inside the
|
||||
// PLAYER's strate Z-band (the other strates are sealed/enclosed ⇒ invisible from here anyway);
|
||||
// loaded coarse tiles re-queue automatically when the band changes (strate transition).
|
||||
// Default 0 = cut at EVERY level (tested verdict 2026-07-05: level 0/1 straddler tiles were
|
||||
// the visible mixers — a higher floor left them mixing and looked like "no improvement").
|
||||
// Raise only if the descent/passage transition needs full tiles near the player. 9 = off.
|
||||
// Default 1 = cut LOD1 and coarser. LOD0 is deliberately never content-cut because it owns pawn
|
||||
// collision: cutting the passage, gap, or next-strate support tile would deadlock a walk at the
|
||||
// current strate boundary. The runtime clamps legacy/hand-authored values below 1 to 1 as well.
|
||||
// Raise only if a broader near-field view needs full coarse tiles. 9 = off.
|
||||
// At ultra-coarse levels where one CELL is taller than the band itself, the tile is skipped
|
||||
// entirely (see LoadTile) — cell-granular cutting there could only render garbage.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "0", ClampMax = "9"))
|
||||
int32 StrateContentCutMinLevel = 0;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Streaming", meta = (ClampMin = "1", ClampMax = "9"))
|
||||
int32 StrateContentCutMinLevel = 1;
|
||||
|
||||
/**
|
||||
* Return the effective content-cut level used by runtime streaming.
|
||||
*
|
||||
* Existing data assets can contain a serialized legacy value of 0 even though the property now
|
||||
* documents 1 as the minimum. Keep the safety rule in code, at the consumer boundary: level 0
|
||||
* tiles are the collision tiles and must never receive a strate content band.
|
||||
*/
|
||||
int32 GetEffectiveStrateContentCutMinLevel() const
|
||||
{
|
||||
return FMath::Clamp(FMath::Max(StrateContentCutMinLevel, 1), 1, 9);
|
||||
}
|
||||
|
||||
// Open-world SKY reach: the sky-cap ceiling of an open strate (SurfaceWorld / FloatingIslands)
|
||||
// is FAR, so the ceiling BAND is streamed across a wider horizontal radius = ViewDistanceXY ×
|
||||
|
||||
Reference in new issue
Block a user