SoCal, the whole bay, a moon, and gates that actually run
Six agents in parallel, and the two city packs independently reported the same blocker: `focusRegions` and `coarseFactor` existed on the `City` type and nothing implemented them. Uniform lattices would have been 2.9M points for Southern California and 3.7M for the expanded bay. Both packs were unloadable as written. `buildAxis` is the answer, and it is honest about its limits: refinement is per axis, not per rectangle, so a focus region sharpens its whole row *and* its whole column. Two regions at opposite corners refine nearly everything between them. Measured, not guessed — the bay went 0.53M points with one region and 1.64M with three, for detail nobody is looking at from a board this wide. One region each, coarse factor ten, and the builds land at 3.8 s and 2.3 s. Then three things that were only ever right because San Francisco was the only city. `maxDistance: 340` and a 170-unit shadow box were constants tuned for a 230-unit board; the bay is 1003 units across and the camera physically could not retreat far enough to frame it. Fog distances were scene units pinned to the same assumption. And `minVisibilityM` defaulted to 4.5 km of honest weather, which over ninety-four kilometres of bay correctly hides three quarters of it — the night view was a black rectangle for a completely reasonable reason. All three now derive from the board. The moon is a real ephemeris and its light is a deliberate lie: 1.15, against a physical ratio of one to four hundred thousand. What is being reproduced is what a moonlit night looks like on a screen in a lit room. The CI gate caught itself, which is the part worth keeping. Port 8431 was already held by a server from an earlier session, so the boot check polled a healthy stranger while the process it started died on EADDRINUSE. It now refuses to run rather than pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+40
-6
@@ -19,6 +19,7 @@
|
||||
|
||||
import * as THREE from "three";
|
||||
import { createBlocks, createLandmarks } from "./blocks.ts";
|
||||
import { createNightLights, type NightLights } from "./nightlights.ts";
|
||||
import { createFlightLayer, type FlightLayer } from "./flights.ts";
|
||||
import { createMarkerLayer, type MarkerLayer } from "./markers.ts";
|
||||
import { createSceneKit, type Pose } from "./scenekit.ts";
|
||||
@@ -63,6 +64,12 @@ export interface SceneHandle {
|
||||
stageScene: StageScene;
|
||||
/** Applies a rig computed elsewhere. The scene never works one out itself. */
|
||||
setLighting(state: LightingState): void;
|
||||
/**
|
||||
* Solar elevation in degrees, for the layers that need the sun's position
|
||||
* rather than the rig it implies. `LightingState` deliberately carries no
|
||||
* elevation, so the number has to arrive separately.
|
||||
*/
|
||||
setSolarElevation(degrees: number): void;
|
||||
flyTo(chapterId: string): void;
|
||||
current(): string;
|
||||
onChapterChange(fn: (id: string) => void): void;
|
||||
@@ -78,16 +85,33 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S
|
||||
const stage = createStage(canvas);
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
/**
|
||||
* Every camera limit is derived from how big this city's board actually is.
|
||||
*
|
||||
* These were constants tuned for San Francisco — `maxDistance: 340`,
|
||||
* `far: 900`, a 170-unit shadow box. That silently made board size a fixed
|
||||
* property of the engine rather than of a city: expanding the pack from San
|
||||
* Francisco to the whole Bay Area took the board from 230 units across to
|
||||
* 1003, and the camera physically could not retreat far enough to frame it.
|
||||
* You got a close-up of the peninsula with everything else off-screen, and
|
||||
* nothing in the types said why.
|
||||
*
|
||||
* A city pack now chooses its own `latScale` freely and the camera follows.
|
||||
*/
|
||||
const [westX, northZ] = world.project(city.bounds.maxLat, city.bounds.minLng);
|
||||
const [eastX, southZ] = world.project(city.bounds.minLat, city.bounds.maxLng);
|
||||
const boardSpan = Math.max(Math.abs(eastX - westX), Math.abs(southZ - northZ));
|
||||
|
||||
const kit = createSceneKit({
|
||||
scene,
|
||||
dom: stage.renderer.domElement,
|
||||
fov: 42,
|
||||
near: 0.1,
|
||||
far: 900,
|
||||
minDistance: 12,
|
||||
maxDistance: 340,
|
||||
shadowExtent: 170,
|
||||
shadowFar: 520,
|
||||
far: boardSpan * 3,
|
||||
minDistance: Math.max(4, boardSpan * 0.02),
|
||||
maxDistance: boardSpan * 1.5,
|
||||
shadowExtent: boardSpan * 0.75,
|
||||
shadowFar: boardSpan * 2.2,
|
||||
});
|
||||
kit.applyLighting(options.lighting ?? cityDaylight(pal));
|
||||
|
||||
@@ -95,10 +119,18 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S
|
||||
scene.add(createShorePlates(world));
|
||||
scene.add(createTerrain(world));
|
||||
scene.add(createRoads(world));
|
||||
scene.add(createBlocks(world));
|
||||
const blocks = createBlocks(world);
|
||||
scene.add(blocks);
|
||||
scene.add(createLandmarks(world));
|
||||
scene.add(createBridges(world));
|
||||
|
||||
/**
|
||||
* The city switching itself on after sunset. Built after `blocks` because it
|
||||
* patches the material that `createBlocks` made — order is load-bearing.
|
||||
*/
|
||||
const nightLights: NightLights = createNightLights({ world, blocks });
|
||||
scene.add(nightLights.group);
|
||||
|
||||
const markerLayer: MarkerLayer = createMarkerLayer(world, options.markerPalette ?? {});
|
||||
scene.add(markerLayer.group);
|
||||
|
||||
@@ -176,6 +208,7 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S
|
||||
dispose() {
|
||||
options.flights?.dispose?.();
|
||||
flightLayer?.dispose();
|
||||
nightLights.dispose();
|
||||
markerLayer.dispose();
|
||||
kit.dispose();
|
||||
scene.traverse((obj) => {
|
||||
@@ -195,6 +228,7 @@ export function createScene(canvas: HTMLCanvasElement, options: SceneOptions): S
|
||||
stage,
|
||||
stageScene,
|
||||
setLighting: (state) => kit.applyLighting(state),
|
||||
setSolarElevation: (degrees) => nightLights.setSolarElevation(degrees),
|
||||
flyTo,
|
||||
current: () => currentChapter,
|
||||
onChapterChange(fn) {
|
||||
|
||||
Reference in New Issue
Block a user