fix: aerial fog follows the camera on the tick, not on OrbitControls change
Drive, crow and aircraft follow-cameras write the pose directly each frame and never fire `change`. Fog lived on that event, throttled at 2% of altitude, so a descent under a follow camera fell back to the one-hertz clock and stepped the haze four times instead of easing it. A 300 ms fog dip got at most one sample. The recompute now runs on the city's tick, after `roadTraffic.tick` and the actor/aircraft `setPose` writes — the same pump `applyReliefRamp` already uses, for the same reason: a camera-dependent term cannot live on an event the descent does not fire. The 2% altitude throttle is unchanged, so a chapter flight is still a few dozen recomputations rather than sixty a second. Orbit `change` still drives handover and prefetch; those are overview-only. `aerialFogMoved` is the throttle as a function of two altitudes. `followCameraFog.test.ts` writes a Drive pose without `controls.update`, asserts `change` does not fire, and still pulls the fog in from the new pose.
This commit is contained in:
@@ -54,40 +54,27 @@ things fall out of it that somebody has to pick up.
|
||||
### Aerial perspective is wired, and the coarse seam is now a live defect
|
||||
|
||||
Fog now follows camera altitude (`aerialReach` in `engine/atmosphere.ts`, §12.2).
|
||||
`main.ts` recomputes the rig on the controls' `change` event, throttled at 2% of
|
||||
the current altitude, which collapses a chapter flight to a few dozen
|
||||
recomputations instead of sixty a second.
|
||||
`main.ts` recomputes the rig on the city's tick (`onCameraFrame`, after the
|
||||
follow cameras write the pose), throttled at 2% of the current altitude, which
|
||||
collapses a chapter flight to a few dozen recomputations instead of sixty a
|
||||
second. Drive, crow and aircraft follow-cameras write the pose directly and do
|
||||
not fire OrbitControls `change`; they used to fall back to the one-hertz clock
|
||||
and a fast descent stepped the fog four times. That path is on the frame loop.
|
||||
|
||||
What that does **not** cover is a camera moved by something other than
|
||||
`OrbitControls`: the drive, actor and aircraft follow-cameras set the pose
|
||||
directly each frame and do not fire `change`. Those poses fall back to the
|
||||
one-hertz clock tick, so a fast descent under a follow camera steps the fog up
|
||||
to four times instead of easing it.
|
||||
The state-to-metro transition is a **fog dip** — collapse the outgoing board's
|
||||
`setAerialFog` toward the camera over ~0.3 s, swap in the obscured frame, lift
|
||||
the incoming board's over ~0.5 s — chosen over a crossfade because a crossfade
|
||||
cannot afford the triangles (ca+sf is 2,640,307 against the Bay Area's
|
||||
2,600,000 cap) and because a dip is the only transition that hides the 4.2x
|
||||
vertical-exaggeration pop, the 4 km projection slide and the vanishing 2 km
|
||||
freeways at once, since all three happen at maximum obscuration.
|
||||
|
||||
**Promoted from hypothetical to live, 2026-08-23.** This entry used to end "a
|
||||
continuous state-to-city descent *would* need this on the frame loop rather than
|
||||
on an event". That descent is now the thing being built: the state-to-metro
|
||||
transition is a **fog dip** — collapse the outgoing board's `setAerialFog`
|
||||
toward the camera over ~0.3 s, swap in the obscured frame, lift the incoming
|
||||
board's over ~0.5 s — chosen over a crossfade because a crossfade cannot afford
|
||||
the triangles (ca+sf is 2,640,307 against the Bay Area's 2,600,000 cap) and
|
||||
because a dip is the only transition that hides the 4.2x vertical-exaggeration
|
||||
pop, the 4 km projection slide and the vanishing 2 km freeways at once, since
|
||||
all three happen at maximum obscuration.
|
||||
|
||||
That makes fog a **transition mechanism** rather than an ambience setting, and a
|
||||
mechanism driven by an event the descent does not fire is a mechanism that does
|
||||
not run. Two specific consequences:
|
||||
|
||||
- A dip driven from a follow camera — leaving the corridor drive into the Bay
|
||||
Area is the obvious case — gets the one-hertz tick, so a 300 ms collapse gets
|
||||
at most one step. The dip either does not happen or happens as a single jump.
|
||||
- The 2%-of-altitude throttle is a threshold on *altitude*, and a dip changes
|
||||
near/far with the altitude held still. Nothing in the current trigger notices
|
||||
that at all.
|
||||
|
||||
So this moves onto the frame loop, and it moves as part of the transition work
|
||||
rather than after it.
|
||||
That makes fog a **transition mechanism** rather than an ambience setting. The
|
||||
dip itself is already on the app pump (`stepFogDip`); aerial perspective under
|
||||
a follow camera is now on the same kind of pump. What is still true: the 2%
|
||||
throttle is a threshold on *altitude*, and a dip changes near/far with the
|
||||
altitude held still — which is why the dip writes `setAerialFog` itself rather
|
||||
than going through `refreshAerialFog`.
|
||||
|
||||
### The north is authored, and three things in it are thin
|
||||
|
||||
|
||||
Reference in New Issue
Block a user