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:
parent
832001d289
commit
d03fe84845
2 changed files with 49 additions and 1 deletions
|
|
@ -27,6 +27,14 @@ public sealed class RetailChaseCamera : ICamera
|
|||
{
|
||||
// ICamera surface.
|
||||
public Vector3 Position { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cell the collided viewer-sphere ended in (retail <c>viewer_cell =
|
||||
/// sphere_path.curr_cell</c>). Roots the render mode + indoor visibility + the portal
|
||||
/// side-test in <see cref="GameWindow"/> (Phase W single-viewpoint V1) — the ONE viewpoint.
|
||||
/// Equals the passed player cell when camera collision is off / the probe is null.
|
||||
/// </summary>
|
||||
public uint ViewerCellId { get; private set; }
|
||||
public float Aspect { get; set; } = 16f / 9f;
|
||||
public float FovY { get; set; } = MathF.PI / 3f;
|
||||
public Matrix4x4 View { get; private set; } = Matrix4x4.Identity;
|
||||
|
|
@ -152,8 +160,16 @@ public sealed class RetailChaseCamera : ICamera
|
|||
// eye is pressed against a wall. So collide into a separate local and
|
||||
// leave _dampedEye as the clean, uncollided sought position.
|
||||
Vector3 publishedEye = _dampedEye;
|
||||
// The viewer cell defaults to the player cell (collision off / null probe); the sweep
|
||||
// overwrites it with the swept cell (retail viewer_cell). Always set so GameWindow has a
|
||||
// robust per-frame "which cell is the camera in?" answer.
|
||||
ViewerCellId = cellId;
|
||||
if (CameraDiagnostics.CollideCamera && CollisionProbe is not null)
|
||||
publishedEye = CollisionProbe.SweepEye(pivotWorld, _dampedEye, cellId, selfEntityId).Eye;
|
||||
{
|
||||
var swept = CollisionProbe.SweepEye(pivotWorld, _dampedEye, cellId, selfEntityId);
|
||||
publishedEye = swept.Eye;
|
||||
ViewerCellId = swept.ViewerCellId;
|
||||
}
|
||||
|
||||
// 6. Publish renderer surface (from the collided eye; rotation stays the
|
||||
// smoothly-damped look direction toward the pivot).
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue