8fb85cd2e5933d85cfddc7b4388638d13bd49068
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8fb85cd2e5 |
feat: give the boards a horizon, a sea that reflects, and a state worth flying over
The wide shot, which is what an anonymous visitor actually lands on. **The sea was `MeshLambertMaterial`** — a material with no specular term at all, by construction — on a board where water is half the frame. It is now a low-roughness dielectric that reads `scene.environment`, with a runtime-generated tiling swell normal map sampled twice per fragment at two scales and two headings, so the sun breaks into a moving glitter path instead of a mirror point. An `onBeforeCompile` patch takes the body colour toward the deep value looking straight down and leaves it to the reflection at grazing, and walks roughness up past 1.6 board spans so the far water cannot shimmer. The swell spectrum is 1/k^2 and not 1/k because the first attempt was photographed: at 1/k every component carries the same slope, the shortest wave wins, and the sea renders as hard diagonal corduroy. A test holds it now. **The board no longer ends in a diamond.** The sea plane went from 1.8 board spans to 18, past the fog's far plane from anywhere the orbit reaches, and the sky is a world-space dome rather than a screen-space gradient. That gradient was wrong in a way dusk made obvious: the sunset band was painted along the *bottom* of the picture, under the board, while the true horizon at the top of frame stayed zenith blue. `daylight.ts` pinning the horizon stop to the fog colour to hide the seam was a symptom of it. **Terrain casts shadows.** Left off before because double-sided terrain against a ~16 m-per-texel shadow map gives acne; `shadowSide = BackSide` is the cure, shot at four sun elevations down to +0.0 degrees to confirm no stippling. The caster is a stride-2 decimation appended to the same index buffer and swapped in by `onBeforeShadow`/`onAfterShadow` via `drawRange`: no extra draw call, a quarter of the depth cost, and indistinguishable from the full-resolution caster in a side-by-side crop. Stride 1 was measured at +65,566 triangles and would have missed the budget by ~47,000, so it was not shipped. **California reads as California.** It was a beige kite: the eastern edge one ruled line for five degrees of latitude, the south closing in a diagonal V, the whole south-east a featureless tan wedge. Now the coast runs to the Mexican border with San Diego on it, the eastern edge follows the Colorado and the Nevada diagonal, and the south-east is the Basin and Range — forty parallel desert ridges throwing shadows east, Death Valley as a white pan between the Panamints and the Black Mountains, the Salton Sea the one cool value for two hundred kilometres. The opening pose is retuned to the bigger board; the old 452/392 stand-off left a slab of empty ocean where the state should be. **The aircraft were six pixels.** Measured, by enlarging a screenshot 200% to find one at all — indistinguishable from a dead pixel, on a board whose entire claim is that the sky is live. They are airliners now, with planform and trail, and clicking one raises its card for a signed-out visitor. **The Model X is off the wall.** It stood at floor level outside a studio 188 m up a Transbay tower, reading as a car balanced on a parapet. The apron is now chosen from `site.elevation`, which the pack already carries — not from an office id, which is the bug class this repo already hit once when a door marker gated on `id === "sf"` and would have pinned the Los Angeles building to San Francisco. Also fixed, and nearly shipped: sea z-fighting dithered every flat piece of ground on the Bay Area and SoCal boards. And one test asserted an exact source line for the water material, so the better multi-line implementation failed it — it now asserts the property (dielectric, metalness 0, low roughness) rather than the author's first guess at formatting. Tests 964 -> 1015. California desktop 562/650 draw calls and 728,744/750,000 triangles — 2.8% of triangle headroom left, which is the number the next person should check first. No budget was raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
db074e9cf7 |
feat: tone-mapped render rig, studio devices, LA fidelity pass, UI overhaul
The build the studios needed, across eight workstreams and one strict file partition. **The render rig was the quality ceiling.** The renderer ran three's NoToneMapping default while atmosphere drove the sun to 2.35 and assets set emissives to 3.2, so every value above 1.0 hard-clipped to flat white — which is why walls blew out and every fitting looked like a white rectangle. ACES filmic tone mapping and an explicit output colour space land in `stage.ts`, and the atmosphere intensity table and palette headroom are re-tuned against the new curve rather than left tuned for the clipping we removed. `engine/environmentRig.ts` builds a PMREM environment at runtime, procedurally, so nothing binary is committed. There was no environment map anywhere before, so every `metalness > 0` role had nothing to reflect and rendered dull grey — a defect the code already documented against itself in `office/optimus.ts`, where a whole material role was abandoned over it, and worked around in `modelX.ts` with a fake emissive that this change deletes. Atmosphere remains the sole light owner; the rig derives from the `LightingState` it already produced. **Studio hardware exists.** There was no device concept anywhere in the product: no type, no route, no state. `devices/types.ts` fixes a declaration/state/ capability/command contract that a smart light, a thermostat, a door sensor and a charger all fit without a schema change, and both studios now carry a desk mic and a computer speaker with deterministic simulated behaviour behind an adapter seam a real API can occupy later. Reads are the demo and are open; commands are a signed-in action and are kept off the read body entirely, because a shared cache replaying a GET that turned a microphone on is exactly what the fail-closed cache default exists to prevent. **The ADS-B licence hole is closed.** `TERA_ADSB_ENDPOINT` accepted any URL, the response was served publicly cacheable, and the attribution hardcoded adsb.lol regardless of where the endpoint pointed — one env var away from republishing non-redistributable data under an open-terms credit. The host is now allowlisted, the credit is derived from the host actually configured, public cacheability is conditional on redistributability, and a refused endpoint demotes to simulated flights and says so in `degraded[]`. The gate is on the source, not the feature: live aircraft and their detail cards stay open to anonymous visitors. **The LA studio was never the smaller pack** — 16 rooms and 248 props against SF's 4 and 28. Its deficit was fidelity per square metre: 98 of those props were ceiling troffers, it bound no props to seats, placed none of the habitat kit, and 12 of its 16 rooms had no viewpoint. Density comes from new asset kinds rather than more instances, because `furnish.ts` draws once per kind and folds colour into the batch key, so repeat instances add nothing the eye can read. **The interface stops being forty imperative mutations.** Every visibility decision moves into a pure, tested `ui/chromeState.ts` and one applier, so the chrome has coverage for the first time. Deleted: ~100 lines of CSS and two bindings targeting elements that no longer exist, and a `body:has()` rule that shifted the desktop layout by 160px for touch controls hidden there. Fixed: the office picker tabs that drew their label and their badge on top of each other. Added: a first-run flow, because the product is two verbs and neither was ever stated on screen. Mobile is designed on its own terms instead of being the desktop with things hidden — the plan view comes back, and the keyboard-only shortcuts button is replaced by touch controls. `arena/studioOps.ts` frames the whole thing as the multi-variable environment it is, wrapping the same simulators the renderer drives rather than a headless copy. Also removed `input/vehicle.ts`, which nothing but its own test imported. Tests 385 -> 961, all passing. Typecheck, build, performance budgets across six matrix cells, no-binaries, provenance, dependency licences, zero-config boot and arena source hashes all green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fe58290728 | California gets roads, traffic, and a car to follow | ||
|
|
a229fb2721 |
The sky gets the things above the aeroplanes
Satellites, end to end: CelesTrak element sets behind the same TTL cache
the weather and the flights use, served as TLEs rather than as positions,
and propagated in the browser with SGP4.
Sending elements is the same trick `flights/plan.ts` plays and it has a
better excuse here — a TLE *is* the closed form, valid for days either
side of its epoch, so one cacheable fetch every six hours replaces a poll
and every viewer agrees about where everything is.
Two things are worth knowing about the shape of it:
- There is no region parameter. An aeroplane at 10,000 m is local and
a satellite at 550 km is above the horizon for a circle two thousand
kilometres across, so one catalogue serves both boards and the client
decides what is above its own horizon. Only the observer is per-city,
which is why `main.ts` shares the elements and rebuilds the catalogue.
- The layer draws on a dome, because it cannot draw anywhere else.
`world.metres(550_000)` is 21,000 scene units against a far plane at
3,000. Azimuth and elevation are real; the radius carries nothing.
Off by default: a clone that started pulling CelesTrak on `npm run dev`
would have volunteered somebody else's bandwidth for its onboarding.
Godmode gets the two dials that point at the sky rather than at the
light — fabricated traffic, which composes with a live ADS-B feed instead
of replacing it, and a switch for the satellite layer with a count beside
it. Both are god-only lies about the inputs, in the manner of the weather
override.
`satellite.js` is the second runtime dependency this package has taken.
Its entry point star-exports an Emscripten build that cannot be shaken
out, so `noWasmPropagator` in the Vite config cuts it: 308 kB of WASM
loader for a bulk propagator nothing calls, against 26 kB for the SGP4
that does the work.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
d464459838 |
Spaces: the inside of the world, and a sun that is actually where it should be
Ten agents wrote this in parallel against CONTRACT.md, which exists because the five design agents before them collided on fifteen blocking points — four files specified twice with incompatible contents, three separate backends for one box, and `Environment` exported twice meaning different things. What landed: a Stage owning only the renderer and the loop, with the city and an office as two scenes over it. They cannot share one — San Francisco is ~94 m per scene unit with 3.6x vertical exaggeration and an office is 1 unit = 1 m — and the city is paused rather than disposed on the way in, because rebuilding its 336,864-point heightfield costs about a second on the way back out. Offices are data. `src/offices/lumbridge-hq.ts` is fifteen rooms and seventy-six seats, and it is the file a self-hoster copies. Walls are a segment list with 1-D openings, so doors and windows are holes punched in a wall rather than placed objects, and the pass that splits a wall around its openings hands the walk-mode collider its segments for free. The sun is real. `solar.ts` is a NOAA/Meeus implementation with no imports at all — not even three.js — so time of day keeps working on a laptop in a field. Verified against known values: 75.45 degrees at the June solstice in SF, 28.79 at December, sunset at 03:15Z. The first screenshot after wiring it was a black rectangle, which turned out to be correct: it was midnight in San Francisco. Presence binds to a seat id and never to a coordinate. The pack knows where `eng-04` is; who is sitting in it is private data behind an API. Same shape as the marker rule, one level in. Two corrections to ARCHITECTURE.md are in here. Containment does not discharge ODbL — publishing OSM-derived coordinates is Public Use of a Derivative Database wherever the rows live, so the rule is about the geocoder (US Census, public domain) and not the storage. And a person at a desk is not a Marker; markers are geographic. One contract gap surfaced only in a screenshot: two agents read `height` on a viewpoint differently, so the establishing shot aimed at empty air fourteen metres above the roof. It now means what the same field means for a city. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
67f8df8d53 |
Lumbridge Simulate Engine — the city, and the licence it can actually ship under
LSE is the third of the three, beside lumbridge-compute and lumbridge-bench: a 3D engine for walkable places. This first commit is the outside of the world — San Francisco — plus the seams the inside will attach to. The engine renders a City and a list of Markers and knows nothing else. It does not know markers are usually companies and it will never learn that "rejected" is red; that mapping lives in an adapter. Which is what lets one renderer serve a private map, a public one, and a self-hoster with no Lumbridge account, none of them a fork of the others. Three things were designed around the licence rather than discovered after it, because each one is a promise Apache 2.0 makes that is easy to break by accident. No trademarks in the repo — logos are fetched at runtime, and public/logos/ is gitignored. No OpenStreetMap-derived coordinates, which is why every coastline in cities/sf.ts was traced by hand: Nominatim output is ODbL, share-alike, and would attach to the whole pack. And no FlightRadar24 client — their terms forbid scraping and redistribution, so flights are an interface with a simulator and open community ADS-B behind it. The privacy constraint and the licence constraint turned out to want the same thing. Geocoded company positions and pipeline status both stay behind Workie's API; the open repo holds the city and the renderer. The tempting shortcut — commit an sf-companies.json — breaks both at once. Ported out of Workie, where a 3D city engine had no business living. Workie's /live is deleted rather than deprecated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |