fix(runtime/camera) #429: presented player and chase camera share the object clock

Two halves of the felt run-hitch (the visible one-frame player lurch):

- The presentation lerp normalized the pending object-clock time by the
  fixed 30 Hz MinQuantum, but retail's object clock simulates
  VARIABLE-length quanta (CPhysicsObj::update_object 0x00515D10: capped
  at MaxQuantum, everything above MinQuantum runs as ONE step). After a
  long frame the view froze for the quantum and then fast-replayed it.
  ComputeRenderPosition now spans the ACTUAL last quantum
  (_lastQuantumSeconds), and PresentedDeltaSeconds accounts continuous
  presented time across quantum boundaries.

- The chase camera damped toward the presented player using wall dt
  while the player presents on the object clock, so a long frame
  stepped the camera far past the under-advanced player — measured up
  to ~1 m of camera/player decoherence in a single frame. Retail ties
  camera update to the physics-update callback
  (SmartBox::PlayerPhysicsUpdatedCallback 0x00452d60), i.e. the same
  clock as the body; both chase cameras now integrate
  PresentedDeltaSeconds. Manual zoom/pitch adjustment stays on wall dt
  (a user-input rate, not target chasing).

Owner gate: camera-vs-player boom-length change fell from ~1 m spikes
to 0.2-1.2 cm median on long frames; teleports settle clean. Two
Runtime tests updated to pin the continuous-rate contract. The
temporary PlayerPresentationProbe apparatus that measured this is
retired with the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-24 09:16:41 +02:00
parent 0330fcd0d1
commit 4873c10673
5 changed files with 110 additions and 100 deletions

View file

@ -450,10 +450,6 @@ internal sealed class RuntimeWorldFrameEnvironmentPreparation
private readonly HashSet<uint> _visibleCells = [];
private bool _visibleCellsValid;
/// <summary>TEMPORARY #429 — see <see cref="PlayerPresentationProbe"/>.</summary>
private readonly PlayerPresentationProbe? _playerPresentProbe =
PlayerPresentationProbe.CreateFromEnvironment();
public RuntimeWorldFrameEnvironmentPreparation(
RuntimeOptions options,
WorldTimeService worldTime,
@ -489,10 +485,6 @@ internal sealed class RuntimeWorldFrameEnvironmentPreparation
activeDayGroup,
camera.Position);
// TEMPORARY #429 apparatus — one CSV row per frame; off unless
// ACDREAM_PROBE_PLAYER_PRESENT names a file. Strip with the fix.
_playerPresentProbe?.Observe(camera.Position, roots.PlayerViewPosition);
UpdateSunFromSky(foundation.Sky, roots.PlayerInsideCell);
_lighting.UpdateViewerLight(roots.PlayerViewPosition);
_lighting.Tick(camera.Position);