diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0d6b1ce..dd772ed 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -214,7 +214,8 @@ receiver as the eventual real answer. ## 5. LA is not "SF but more data" SF is 0.20° × 0.36°. At the 45 m cells the SF heightfield uses, that is 336k -lattice points and a 1.0 s build. +lattice points and a 1.0 s build. (The pack has since grown to the whole Bay +Area: 0.53M points and about 2.3 s.) LA + Orange County + Riverside is roughly 0.5° × 1.9° — about **14× the area**. The same approach would be 4.6M lattice points and a mesh nobody can load. This @@ -293,7 +294,8 @@ resolutions: puts one scene unit at ~94 m with 3.6× vertical exaggeration; an office renders at 1 unit = 1 m. There are two scenes over one renderer, and the city scene is *retained and paused* when you step inside rather than disposed — rebuilding - its 336,864-point heightfield costs about a second on the way back out. + its 0.53M-point heightfield costs about two and a half seconds on the way back + out. - **A person at a desk is not a `Marker`.** A marker is geographic — it has a lat/lng. Presence binds to a **seat id** and never to a coordinate, which is what lets the office geometry be public and open-source while who is sitting diff --git a/CONTRACT.md b/CONTRACT.md index be9c380..68a9537 100644 --- a/CONTRACT.md +++ b/CONTRACT.md @@ -37,8 +37,9 @@ asserting `GET /api/v1/health` → 200. ## 1. Stage and scene lifecycle Two designs both created `src/engine/stage.ts` with opposite lifecycles. The -retention argument wins on measured cost: SF's heightfield is 484 × 696 = -336,864 lattice points and `world.ts` records a ~1.0 s build. Disposing the city +retention argument wins on measured cost: the Bay Area's heightfield is 0.53M +lattice points and about 2.3 s to build — it was 336,864 and ~1.0 s when the +pack was San Francisco alone, and it grew, which is the point. Disposing the city every time someone steps into an office and paying a second of rebuild on the way out is not acceptable. diff --git a/server/package.json b/server/package.json index 3db1ed9..d43ca93 100644 --- a/server/package.json +++ b/server/package.json @@ -2,7 +2,7 @@ "name": "@lumbridge/tera-api", "version": "0.1.0", "private": true, - "description": "The one Tera API. Health, flights, weather, markers and office packs on 127.0.0.1:8431.", + "description": "The one Tera API. Health, flights, weather, markers, office packs and session on 127.0.0.1:8431.", "license": "Apache-2.0", "type": "module", "main": "src/index.ts", diff --git a/src/cities/socal.ts b/src/cities/socal.ts index 6302c5e..bd143fb 100644 --- a/src/cities/socal.ts +++ b/src/cities/socal.ts @@ -3088,12 +3088,18 @@ export const SOCAL_CITY: City = { /** * 391 m to the scene unit, against San Francisco's 94. * - * Not a stylistic choice. `scene.ts` caps the orbit at 340 units from the - * target and `cityDaylight` closes its fog at 460, so however large a city is - * on the ground it has to end up roughly the same size in scene units or it - * cannot be looked at. The board this produces is 393 by 308 units — a little - * wider and deeper than San Francisco's 331 by 230, which is about as far as - * the camera will stretch. + * The board this produces is 393 by 308 units, against the Bay Area's 1003 by + * 830 — a metro four times the area rendered on a third of the board, which is + * why one instance here is a city block rather than a building. + * + * This used to be forced rather than chosen: `scene.ts` capped the orbit at + * 340 units and `cityDaylight` closed its fog at 460, both tuned when San + * Francisco was the only city, so however large a place was on the ground it + * had to come out roughly San Francisco's size in scene units or it could not + * be looked at. Both now derive from the board, so a pack is free to pick its + * own scale. This one keeps 285 because the numbers below — lot size, hill + * radii, bridge spans — were all authored against it, not because the engine + * still insists. */ latScale: 285, diff --git a/src/engine/scene.ts b/src/engine/scene.ts index 0821d17..11ca34c 100644 --- a/src/engine/scene.ts +++ b/src/engine/scene.ts @@ -13,8 +13,8 @@ * that ought to be here — is the city itself: which layers go in the scene, * where a chapter puts the camera, and what a pick means. An office builds the * same two pieces with its own answers and swaps in on the same `Stage`, which - * keeps this city alive and paused rather than rebuilding its ~1.0 s - * heightfield on the way back. See CONTRACT.md §1. + * keeps this city alive and paused rather than rebuilding its heightfield — for + * the Bay Area, about 2.3 s — on the way back. See CONTRACT.md §1. */ import * as THREE from "three"; @@ -113,7 +113,7 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S shadowExtent: boardSpan * 0.75, shadowFar: boardSpan * 2.2, }); - kit.applyLighting(options.lighting ?? cityDaylight(pal)); + kit.applyLighting(options.lighting ?? cityDaylight(pal, boardSpan)); scene.add(createWater(world)); scene.add(createShorePlates(world)); @@ -254,12 +254,24 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S * It exists so the engine renders with no server, no clock and no config, which * is the acceptance test the whole repo is held to. */ -export function cityDaylight(palette: ScenePalette): LightingState { +export function cityDaylight(palette: ScenePalette, boardSpan = 230): LightingState { return { sun: { direction: [-0.632, 0.717, 0.295], color: 0xfff3e0, intensity: 2.1 }, hemisphere: { sky: 0xdcecf7, ground: 0x6b6f5e, intensity: 1.05 }, ambient: { color: 0xffffff, intensity: 0.32 }, sky: { top: palette.skyTop, horizon: palette.skyHorizon }, - fog: { color: palette.skyHorizon, near: 210, far: 460 }, + // Scaled to the board for the same reason the camera is, and it was the same + // bug: 210/460 were tuned when San Francisco's 230-unit board was the only + // one, and on the Bay Area's 1003 units they close the fog well inside the + // city. The camera sits about 0.6 spans out on a whole-board view, so a fog + // that starts nearer than that is behind the viewer's own shoulder. + // + // The default keeps the original numbers exactly, for a caller that has a + // palette but no world. + fog: { + color: palette.skyHorizon, + near: boardSpan * 0.91, + far: boardSpan * 2, + }, }; } diff --git a/src/interiors/officeScene.ts b/src/interiors/officeScene.ts index 41bde52..56da1f7 100644 --- a/src/interiors/officeScene.ts +++ b/src/interiors/officeScene.ts @@ -6,8 +6,9 @@ * The renderer and the loop live in `Stage`; the camera, controls, flights and * picking live in a `SceneKit`; what is left here is the office itself. Swap it * in with `stage.setScene(office)` and the city is *paused, not disposed* — - * rebuilding SF's 336,864-point heightfield on the way back out costs about a - * second, which is the measurement CONTRACT.md §1 is built on. + * rebuilding the Bay Area's 0.53M-point heightfield on the way back out costs + * about two and a half seconds, which is the measurement CONTRACT.md §1 is + * built on. * * Whoever builds one of these disposes it. `Stage` disposes nothing it did not * create, and the city handle's `dispose()` does not reach in here.