feat(render): RetailChaseCamera.ViewerCellId — the swept viewer cell (retail viewer_cell)

Update() now always sets ViewerCellId: the camera-collision sweep's swept cell when collision
is on (retail viewer_cell = sphere_path.curr_cell), else the passed player cell. This is the
robust, per-frame, graph-tracked 'which cell is the camera in?' answer that V1 roots the render
on — no AABB, no grace frames (the U.4c flap source). 176 App tests green (2 new).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-03 12:34:07 +02:00
parent 832001d289
commit d03fe84845
2 changed files with 49 additions and 1 deletions

View file

@ -477,6 +477,38 @@ public class RetailChaseCameraTests
Assert.Equal(collided, cam.Position);
}
[Fact]
public void Update_WithProbeAndFlagOn_ExposesSweptViewerCell()
{
CameraDiagnostics.CollideCamera = true;
var probe = new FakeProbe { ReturnEye = new Vector3(1f, 2f, 3f), ReturnCell = 0xA9B40170u };
var cam = new RetailChaseCamera { CollisionProbe = probe };
cam.Update(
playerPosition: Vector3.Zero, playerYaw: 0f, playerVelocity: Vector3.Zero,
isOnGround: true, contactPlaneNormal: Vector3.UnitZ, dt: 1f / 60f,
cellId: 0xA9B40171u, selfEntityId: 0x5);
Assert.Equal(0xA9B40170u, cam.ViewerCellId); // the swept cell, not the player cell
}
[Fact]
public void Update_FlagOff_ViewerCellFallsBackToPlayerCell()
{
CameraDiagnostics.CollideCamera = false;
try
{
var cam = new RetailChaseCamera { CollisionProbe = new FakeProbe { ReturnCell = 0xDEADu } };
cam.Update(
playerPosition: Vector3.Zero, playerYaw: 0f, playerVelocity: Vector3.Zero,
isOnGround: true, contactPlaneNormal: Vector3.UnitZ, dt: 1f / 60f,
cellId: 0xA9B40171u, selfEntityId: 0x5);
Assert.Equal(0xA9B40171u, cam.ViewerCellId); // collision off → the passed player cell
}
finally { CameraDiagnostics.CollideCamera = true; }
}
[Fact]
public void Update_FlagOff_DoesNotConsultProbe()
{