1
0

The office learns where it stands, and the sky stops being a backdrop

**Aircraft actually move now, and the reason they did not is the headline.**
`HttpFlights` holds a frozen snapshot between network refreshes and is
polled at 1 Hz, so a live feed handed the layer the same position five to
fifteen times and then jumped. `span` therefore measured the poll interval
rather than the gap between the two positions that differ, the teleport
test saw an airliner covering 36 units in a "second" against a ceiling of
8, and **every live track's history was wiped on every refresh** — so no
aircraft on the deployed site could ever grow a trail, however long
TRAIL_POINTS was set. Skipping the repeat fixes the motion and the trail
at once. Trails then go to 72 points / 240 s, which is about seventy
seconds of flying.

Three more defects in the same file, found while looking: trail
truncation dropped the segments nearest the aircraft (leaving a streak
with no aeroplane attached), MAX_TRACKS was declared and never enforced,
and one missing target deleted its whole trail. The buffer now uploads
only what it wrote, rather than 46 MB/s of untouched array.

**You can get above the constellation.** Dome to 1.05 board *radii* and
the orbit to 2.0 spans. Radii, not spans: scene space is centred on the
city and the Bay Area board runs forty kilometres down the peninsula, so
the furthest corner is 0.94 spans out where the half-diagonal is 0.65 —
sized off the half-diagonal the dome sat inside its own city. The far
plane goes to 4 spans to stop clipping the sky from off-centre chapters,
and `PointsMaterial` defaults `fog: true`, which was quietly dimming the
whole constellation with the city's haze.

**An office can say where it stands.** `Office.site` — lat, lng, height
above the ground outside, and the compass bearing the pack's −Z points
along — and with one it gets the same sun the city does, a sky, and a
horizon at `-elevation`. CONTRACT §4 reserved this as "a later
refinement"; it is taken up rather than overturned, and `daylight.ts`
computes no light of its own. It does the two things a room needs that a
map does not: turn the sun into the building's frame, and move the fog
outdoors before it greys out the far wall.

Two buildings now, and they are deliberately unalike: Lumbridge HQ 188 m
up a Transbay tower facing 205°, and **Frontier Valley**, a startup in a
hangar at Alameda Point — one room, 54 x 30 m, nine metres to the
trusses, four metres above reclaimed ground.

Floor-to-floor in the reference pack is now 16.8 m: the interstitial is
ten times a real one, so the space between the slabs is somewhere things
can hang. It is frankly not architecture, `PLENUM` is the one number to
change, and the file says so.

Also fixed, all found by review rather than by looking at the screen:

  - `sun.shadow.camera.updateProjectionMatrix()` was never called, so
    three's default ±5 unit box has been in force this whole time and
    every `shadowExtent` this repo passes — including the city's ±752 —
    has been silently ignored.
  - A missing aircraft was kept alive by the new grace period and *drawn*,
    so it froze in mid-air at full opacity for 32 s.
  - Frontier Valley's mezzanine was a `Room`, which carries no height: its
    slab lay on the concrete, its chairs floated 4.4 m over it, and its
    balustrade fenced off a patch of ground floor. It is a `Level`.
  - Overlapping floor slabs z-fought. The format permits overlap and
    resolves later-first, so `shell.ts` now lifts a slab a hair per
    earlier slab it overlaps — and by nothing at all in a pack, like the
    reference office, whose rooms only ever abut.
  - `switchOffice` bypassed the `entering` guard (leaking a whole scene
    per double-click) and tore down the old room before knowing the new
    one would load, with no way back.

Known and not fixed: raising MAX_SPAN to 30 s doubles the worst-case
re-base snap when a feed's gap shortens. It is bounded, pre-existing in
kind, and the fix wants carrying the live head into the next leg.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 23:30:45 -07:00
parent d8afc42d15
commit 06455f7424
15 changed files with 2061 additions and 75 deletions
+40 -13
View File
@@ -309,21 +309,37 @@ export interface SatelliteLayer {
}
/**
* The dome's radius, as a fraction of the board's longest side.
* The dome's radius, as a fraction of **how far the board reaches from the scene
* origin** — `boardRadius` in `scene.ts`, not the board's width.
*
* Three constraints, and the middle one is the one that is easy to miss:
* That distinction was got wrong once and is worth stating plainly. Scene space
* is centred on `city.center`, and the Bay Area board runs forty kilometres down
* the peninsula from there, so its furthest corner is 0.94 spans from the origin
* while its half-diagonal is 0.65. A dome sized at 0.7 *spans* therefore sat
* **inside the southern third of its own city** — satellites rendering below the
* terrain and behind the hills, which looks like a depth bug and is a units bug.
*
* 1. Outside the city, or the buildings occlude the sky.
* 2. **Outside the camera's maximum orbit**, which `scene.ts` sets at
* `boardSpan * 1.5`. This was 1.2 first, which put the far end of the zoom
* *outside the dome* — pull back far enough and you were looking at the sky
* from space, with half the constellation behind the camera. A sky you can
* leave is not a sky.
* 3. Inside the far plane, which `scene.ts` sets at `boardSpan * 3`.
* **You are meant to be able to get above this.** That is a deliberate reversal:
* an earlier version put the dome at 2.0 spans specifically so the camera could
* never leave it, on the theory that a sky you can step outside of is not a sky.
* That theory loses to the thing people actually want to look at — a
* constellation is an object, and the view of it from above, with the city
* underneath, is the shot. Standing inside a sphere of dots is a planetarium.
*
* 2.0 sits in the middle of the only window that satisfies all three.
* So the geometry is worked out from the other end. The board's half-diagonal is
* 0.649 spans for San Francisco and 0.635 for the Southland, so 0.70 clears
* every corner of the city while sitting well inside the camera's new 2.0-span
* orbit. At the far end of the zoom the whole dome is in frame at this fov and
* the board still fills about two thirds of the screen height.
*
* Two bugs died with the old number, and both were invisible from the default
* pose. At 2.0 the dome reached 3.5 spans from the far side of the orbit against
* a far plane at 3.0, so roughly a sixth of the sky was being **clipped**; and
* everything past 2.8 spans was **fully fogged** by the city's own linear fog.
* The band where the constellation was both unclipped and unfogged did not
* overlap the band where it fitted on screen at all.
*/
const DOME_RADIUS_FACTOR = 2.0;
const DOME_RADIUS_FACTOR = 1.05;
/**
* How large a dot is drawn, in **pixels**, at any camera distance.
@@ -386,11 +402,11 @@ const HORIZON_FADE_DEG = 8;
*/
const SHADOW_ALPHA = 0.16;
export function createSatelliteLayer(boardSpan: number): SatelliteLayer {
export function createSatelliteLayer(boardRadius: number): SatelliteLayer {
const group = new THREE.Group();
group.name = "satellites";
const radius = boardSpan * DOME_RADIUS_FACTOR;
const radius = boardRadius * DOME_RADIUS_FACTOR;
const positions = new Float32Array(MAX_DOTS * 3);
const colors = new Float32Array(MAX_DOTS * 4);
@@ -418,6 +434,17 @@ export function createSatelliteLayer(boardSpan: number): SatelliteLayer {
// and additive blending is what makes a lit satellite read as a light source
// rather than as a grey sticker.
blending: THREE.AdditiveBlending,
/**
* Satellites are not in the weather.
*
* `PointsMaterial` defaults `fog: true`, and the city runs a linear fog
* whose far plane is 2.8 board spans — so every dot was being mixed toward
* the fog colour by distance, and the constellation dimmed as the camera
* pulled back, exactly when more of it came into view. Haze is a property of
* the twelve kilometres of air a city sits in; an object 550 km up is on the
* far side of all of it.
*/
fog: false,
});
const points = new THREE.Points(geo, material);