feat(app): UCG W2 Task 2 — render root from physics CurrCell (FindCameraCell fallback)
Wire the BFS visibility root to DataCache.CellGraph.CurrCell (the physics membership answer written in W2 Task 1) rather than resolving independently from a position via FindCameraCell. Closes the render/physics disagreement that causes the "world from below" spawn-in flicker. Changes: - CellVisibility.GetVisibleCells: extracted BFS body into new private GetVisibleCellsFromRoot(LoadedCell root, Vector3 cameraPos); existing GetVisibleCells delegates to it after FindCameraCell (behavior unchanged). - CellVisibility.ComputeVisibilityFromRoot(LoadedCell? root, Vector3 fallbackPos): new public entry point; when root is null falls through to ComputeVisibility (exact today's behavior), otherwise sets _lastCameraCell = root and delegates to GetVisibleCellsFromRoot — cannot regress below baseline. - GameWindow (line 7156): replaced ComputeVisibility(visRootPos) with ComputeVisibilityFromRoot(physicsRoot, visRootPos) where physicsRoot is resolved from _physicsEngine.DataCache.CellGraph.CurrCell via TryGetCell. physicsRoot is null whenever CurrCell is null or its id is not yet in the render registry, so the fallback fires until the cell loads. - 6 new tests in CellVisibilityFromRootTests: null-root fallback equivalence (3 cases), registered root → CameraCell == root (3 cases). All 160 App.Tests pass, 0 regressions. Visual verification PENDING — behavior change; do not claim it works visually. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0e27a6cc3f
commit
02acac5572
3 changed files with 209 additions and 2 deletions
|
|
@ -313,7 +313,7 @@ public sealed class CellVisibility
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Per-frame entry point
|
||||
// Per-frame entry points
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -336,6 +336,39 @@ public sealed class CellVisibility
|
|||
return LastVisibilityResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UCG W2: compute visibility from a supplied root cell (the physics membership answer)
|
||||
/// rather than resolving the root from a position. Falls back to the position-based
|
||||
/// <see cref="ComputeVisibility(Vector3)"/> when <paramref name="root"/> is null (e.g. the
|
||||
/// cell isn't registered with this renderer yet), so it never regresses below baseline.
|
||||
/// </summary>
|
||||
/// <param name="root">
|
||||
/// The render-registered <see cref="LoadedCell"/> that physics determined the player is inside,
|
||||
/// or null if the physics answer isn't usable yet (no cells registered, or the physics cell id
|
||||
/// hasn't loaded into the render system). Null triggers the legacy <see cref="ComputeVisibility"/>
|
||||
/// path, which preserves today's exact AABB-based behavior.
|
||||
/// </param>
|
||||
/// <param name="fallbackPos">
|
||||
/// Position passed to <see cref="ComputeVisibility"/> when <paramref name="root"/> is null,
|
||||
/// AND used as the viewer position for the portal-side test in the BFS when root is non-null.
|
||||
/// Should be the player/physics position (stable inside the cell), not the chase-camera eye.
|
||||
/// </param>
|
||||
public VisibilityResult? ComputeVisibilityFromRoot(LoadedCell? root, Vector3 fallbackPos)
|
||||
{
|
||||
if (root is null)
|
||||
return ComputeVisibility(fallbackPos);
|
||||
|
||||
if (_cellLookup.Count == 0)
|
||||
{
|
||||
LastVisibilityResult = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
_lastCameraCell = root;
|
||||
LastVisibilityResult = GetVisibleCellsFromRoot(root, fallbackPos);
|
||||
return LastVisibilityResult;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// FindCameraCell
|
||||
// ------------------------------------------------------------------
|
||||
|
|
@ -491,6 +524,20 @@ public sealed class CellVisibility
|
|||
if (cameraCell == null)
|
||||
return null;
|
||||
|
||||
return GetVisibleCellsFromRoot(cameraCell, cameraPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UCG W2: BFS visibility traversal from a pre-resolved root cell.
|
||||
/// The root is assumed to be the correct membership answer (supplied by the
|
||||
/// caller — either <see cref="GetVisibleCells"/> via <see cref="FindCameraCell"/>,
|
||||
/// or <see cref="ComputeVisibilityFromRoot"/> via the physics CurrCell answer).
|
||||
///
|
||||
/// The BFS body is byte-identical to the original <see cref="GetVisibleCells"/>
|
||||
/// implementation — only root acquisition was extracted out.
|
||||
/// </summary>
|
||||
private VisibilityResult? GetVisibleCellsFromRoot(LoadedCell cameraCell, Vector3 cameraPos)
|
||||
{
|
||||
var result = new VisibilityResult { CameraCell = cameraCell };
|
||||
var visited = new HashSet<uint>();
|
||||
var queue = new Queue<LoadedCell>();
|
||||
|
|
|
|||
|
|
@ -7153,7 +7153,17 @@ public sealed class GameWindow : IDisposable
|
|||
var visRootPos = (_playerMode && _playerController is not null)
|
||||
? _playerController.Position
|
||||
: camPos;
|
||||
var visibility = _cellVisibility.ComputeVisibility(visRootPos);
|
||||
// UCG W2: use the physics membership answer (DataCache.CellGraph.CurrCell) as the
|
||||
// BFS root instead of resolving from position via FindCameraCell. Falls back to the
|
||||
// original ComputeVisibility path when the physics answer isn't usable yet (null
|
||||
// CurrCell, or its cell id not yet registered with the render CellVisibility system).
|
||||
// This closes the render/physics disagreement — both now key off the same BSP-based
|
||||
// resolution — which is the root cause of the "world from below" spawn flicker.
|
||||
LoadedCell? physicsRoot = null;
|
||||
if (_physicsEngine.DataCache?.CellGraph.CurrCell is AcDream.Core.World.Cells.EnvCell physCell
|
||||
&& _cellVisibility.TryGetCell(physCell.Id, out var registeredCell))
|
||||
physicsRoot = registeredCell;
|
||||
var visibility = _cellVisibility.ComputeVisibilityFromRoot(physicsRoot, visRootPos);
|
||||
bool cameraInsideCell = visibility?.CameraCell is not null;
|
||||
|
||||
// Phase U.4 (2026-05-30): the [vis] probe moved DOWN to the unified
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue