diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dc1e754
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+background-standalone.html
diff --git a/LICENSE b/LICENSE
index 7b68daf..82883b0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2026 zink
+Copyright (c) 2026 Richard Zink
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index fd992eb..addb92e 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,46 @@
# Animated Background
-An animated desktop background that runs in a browser. Features:
+A browser-based animated desktop background: a WebGL flight scene over
+real terrain, driven by the real sun, moon, stars, and live weather.
-- Time-of-day simulation with realistic color gradients driven by the
- actual sun position (computed from latitude/longitude via SunCalc).
-- WebGL flight scene (`vendor/clouds3d.js`): raymarched cloud deck, 3D
- terrain from Copernicus DEM, real star sky (Yale BSC + Milky Way),
- moon with phases, animated highway car lights and settlement lights
- from OpenStreetMap/Overpass at night.
-- Night brightness coupled to the moon phase (new moon = minimum, full
- moon = maximum), with a configurable minimum so the night is never
- pitch-black.
-- Live weather widget (Open-Meteo) showing current conditions and a
- 4-day forecast (morning, evening, full) at every timestamp.
-- Configurable units (metric °C+km/h or imperial °F+mph).
-- Configurable language (with English as the base; translations for
- German, Italian, Spanish, French, and Russian).
-- Configurable color theme (Ash, Gruvbox, Nord) - drop a new
- `*.css` file in the same directory and add an entry to `window.THEMES`
- to add a new theme.
-- Customisable location: set `CONFIG.location.lat/lon`, or let the
- browser's geolocation API pick one up automatically (falls back to
- the built-in default Passau).
-- Optional custom location label (`CONFIG.location.label`) that
- overrides the OpenStreetMap reverse-geocoding lookup.
-- Configurable digital clock format string using ISO-8601-ish tokens
- (YYYY, MM, DD, HH, mm, ss, DDD, DDDD, MMM, MMMM, a, A, sss, T, escape
- sequences \n \t \\, and full weekday/month names via the i18n dict).
-- Configurable debug panel (toggle collapse/expand with theme, units,
- and language dropdowns that apply at runtime).
+## Features
-## Run with Docker
+- **WebGL flight scene** (`vendor/clouds3d.js`): raymarched cloud deck,
+ 3D terrain mesh from real elevation data (Copernicus DEM), satellite
+ or street basemap, figure-eight flight path around the configured
+ location with a vertical bob between **15 and 35 km** flight altitude.
+- **Real day/night cycle** driven by the actual sun and moon position
+ (SunCalc) for the configured latitude/longitude — or an accelerated
+ 24 h simulation (`CONFIG.day`) for wall-decoration purposes.
+- **Realistic night sky**: ~8700 stars from the Yale Bright Star
+ Catalogue (crisp GL points, size by magnitude, colour by temperature),
+ rotated with the sidereal rate for the site's latitude, plus a Milky
+ Way band and a phase-correct moon.
+- **Aircraft**: low-poly jets (CC0 model) cruising the region on curved
+ routes between real airfields, rolling a random destination at every
+ take-off - sun-lit silhouettes with contrails by day, strobing position
+ lights by night (all parameters in `CONFIG.flights`).
+- **Night lights**: animated car lights along the real motorway/road
+ network and pale-yellow settlement lights (OSM place nodes; bigger
+ towns get more and brighter dots), optionally a NASA VIIRS night-lights
+ satellite overlay. All faded in from dusk, density shaped by a
+ rush-hour curve.
+- **Live weather** (Open-Meteo): current conditions + 4-day forecast
+ widget; the weather code selects the scene's cloud density and colour
+ mood (sunny / cloudy / stormy / night).
+- **Fully cached, offline-capable**: basemap tiles, elevation grid,
+ Overpass road/place data, star catalogue and the last weather payload
+ live permanently in IndexedDB. Once warm, the scene renders fully
+ offline; a configurable expiry (`CONFIG.tiles.expireDays`) can refresh
+ basemap tiles periodically.
+- **Location-aware**: set `CONFIG.location.lat/lon`; everything
+ (flight anchor, terrain tiles, sun/moon, weather, star rotation) is
+ computed from it. If the location is omitted, browser geolocation is
+ tried, falling back to the built-in default (London).
+- Units (°C/°F), six UI languages, CSS-swappable colour themes
+ (Ash/Gruvbox/Nord), configurable clock format, optional debug panel.
+
+## Run with Docker (recommended)
```sh
docker compose up --build
@@ -38,41 +48,131 @@ docker compose up --build
Then open .
-## Run locally
+The bundled nginx config (`default.conf`) is more than a file server: it
+provides CORS proxies for the tile sources (`/esri/`, `/carto/`,
+`/terrarium/`) that the scene needs for basemap and elevation data.
+A plain static server without these proxies (e.g.
+`python3 -m http.server 58881`) will run the UI but terrain and map
+tiles will fail to load.
-The app is a single HTML page. With any static file server:
+## Single-file build
+
+`build.py` bundles everything into one self-contained HTML file
+(`background-standalone.html`) — all scripts, the star catalogue and all
+theme CSS are inlined, and theme switching keeps working:
```sh
-python3 -m http.server 58881
+python3 build.py # -> background-standalone.html (no sibling files needed)
```
-Open .
+Runtime *data* is still fetched at load (tiles, Overpass, weather): serve
+the file from the Docker image for the full experience; opened via
+`file://` or a proxy-less server it renders sky, clouds and UI with an
+"OFFLINE"-labelled ground grid instead of basemap tiles.
## Configuration
-Edit `config.js` and reload the page:
+Edit `config.js` and reload. All keys (with defaults):
```javascript
window.CONFIG = {
- debug: 0, // 0 = hide debug panel, 1 = show
theme: 'ash', // 'ash' | 'gruvbox' | 'nord'
- units: 'metric', // 'metric' | 'imperial'
- language: 'de', // 'en' | 'de' | 'it' | 'es' | 'fr' | 'ru'
+ language: 'en', // 'en' | 'de' | 'it' | 'es' | 'fr' | 'ru'
timeFormat: 'DD. MMMM, YYYY \u2012 HH:mm:SS',
- location: {
- lat: 48.5667, // latitude, -90 to +90, north positive
- lon: 13.4319, // longitude, -180 to +180, east positive
- label: '' // optional custom place name;
- // if set, skips reverse-geocoding
+ debug: 0, // 1 = show right-side debug panel
+ units: { temperature: 'C' }, // 'C' | 'F'
+ credits: true, // data-attribution block in info box
+ renderScale: 1, // 0.3..1 internal 3D resolution
+
+ location: { // drives scene anchor, sun/moon, weather
+ lat: 51.5074, // -90..+90, north positive
+ lon: -0.1278, // -180..+180, east positive
+ label: '' // optional name, skips reverse geocoding
},
- orbit: { // omit/null = straight-line flight
- radiusKm: 15, // circular flight around location;
- // clamped to ~50 km at zoom 11
- clockwise: true // turn direction
+
+ day: {
+ simulation: false, // true: run 24 h in cycleSeconds;
+ cycleSeconds: 20, // false: real sun/moon positions
+ fixedTime: null // 'HH:MM' freezes the sim clock at
+ // that time of day (cars, clouds,
+ // weather keep moving); null = flow
+ },
+ weatherSim: false, // true: cycle weather themes, ignore API
+ weatherSimInterval: 30000, // ms per sim step
+
+ clouds: { top: 0.9, bottom: -0.3 },// cloud deck, world units
+ // (1 unit = ~12.9 km at z11 / 48.6 deg N)
+
+ orbit: { // null/omit = straight flight
+ radiusKm: 60, // lemniscate lobe tip (clamped ~190 km)
+ clockwise: true,
+ reorient: true, // random heading at each crossing
+ altBob: { // vertical bob, flight band 15-35 km
+ enabled: true, // false = constant 25 km
+ amp: 0.77, // half-band in world units
+ periodS: 90, // seconds per bob cycle
+ tilt: 0.3 // pitch reaction to climb/descent
+ }
+ },
+ fog: { distanceKm: 120 }, // ground haze; 0/false = off
+
+ terrain: {
+ enabled: true, // false = flat plane
+ exaggeration: 8, // vertical DEM scale factor
+ wireframe: false, // debug: orange mesh edges
+ nightLights: false, // NASA VIIRS city-lights overlay
+ carLights: true, // moving lights on real roads
+ cityLights: true // settlement glow dots
+ },
+
+ flights: { // see section above; all distances km
+ enabled: true,
+ altitudeKm: 14, climbKm: 100, // cruise / climb-descent run
+ count: 84, speedKmh: 850, speedVar: 0.18, minLegKm: 60,
+ holdMinS: 50, holdMaxS: 150, // ground hold at destination
+ wHub: 4, wField: 2, wStrip: 1, // destination roll weights
+ contrailChance: 0.6, // share of aircraft with a trail
+ contrailStartKm: 6, // formation altitude
+ contrailLifeS: 300, // visibility duration
+ contrailWidthM: 310 // full trail width
+ },
+
+ ground: { style: 'satellite' }, // 'satellite' | 'streets'
+ tiles: {
+ expireDays: null, // >0: re-fetch basemap tiles after n days
+ debug: false // tile borders + coords on the ground
}
};
```
+## URL hash parameters
+
+`http://host:58881/#flight=N&fetch=S`
+
+- `flight=N|NE|E|SE|S|SW|W|NW` — heading of the straight flight corridor
+ (used when `CONFIG.orbit` is disabled)
+- `fetch=N|NE|E|SE|S|SW|W|NW` — direction the tile mosaic is prefetched
+ ahead of the camera
+
+## Time format tokens
+
+```
+YYYY 4-digit year (2026) MM month 01-12 (10)
+DD day 01-31 (07) HH hour 00-23 (14)
+mm minute 00-59 (35) ss second 00-59 (22)
+sss millisecond 000-999 (456) a am/pm (pm)
+A AM/PM (PM)
+
+MMM month short name (Oct, Okt, Ott, ...)
+MMMM month full name (October, Oktober, ...)
+DDD weekday short name (Mon, Mo, Lu, ...)
+dddd weekday full name (Monday, Montag, ...) lowercase!
+
+Any other character is emitted verbatim (so a literal T in ISO-8601
+needs no special token).
+\n newline \t tab \\ literal backslash
+```
+
## Adding a new language
Edit `i18n.js` and add entries for each existing key under your new
@@ -85,65 +185,43 @@ window.TRANSLATIONS['Sunrise']['xx'] = 'MyTranslation';
## Adding a new theme
Drop a new `*.css` file next to the others and add an entry to
-`window.THEMES`:
+`window.THEMES` in `config.js`:
```javascript
window.THEMES['mytheme'] = 'mytheme.css';
```
-## Time format tokens
-
-```
-YYYY 4-digit year (2026)
-MM month 01-12 (10)
-DD day 01-31 (07)
-HH hour 00-23 (24h) (14)
-mm minute 00-59 (35) lowercase!
-ss second 00-59 (22) lowercase!
-sss millisecond 000-999 (456)
-a am/pm (lowercase) (pm)
-A AM/PM (uppercase) (PM)
-T literal 'T'
-
-MMM month short name (Oct, Okt, Ott, ...)
-MMMM month full name (October, Oktober, ...)
-DDD weekday short name (Mon, Mo, Lu, ...)
-DDDD weekday full name (Monday, Montag, ...)
-
-\n newline
-\t tab
-\\ literal backslash
-```
-
## Files
```
.
-├── background.html the page (formerly index.html)
+├── background.html the page (UI, weather, day/night driver)
├── config.js user-facing configuration
├── i18n.js translation strings (en/de/it/es/fr/ru)
├── ash.css default color theme
├── gruvbox.css alternative color theme
├── nord.css alternative color theme
-├── vendor/ vendored third-party files (no network needed)
-│ ├── clouds3d.js WebGL scene: raymarched clouds, 3D terrain,
-│ │ real sun/moon/stars, car + city lights
+├── vendor/
+│ ├── clouds3d.js WebGL scene renderer (clouds, terrain, sky, lights)
│ ├── suncalc.js ISC (SunCalc by mourner)
│ ├── stars.json Yale Bright Star Catalogue subset
│ └── LICENSE per-library license attributions
-├── default.conf nginx server config (port 58881, tile CORS proxies)
+├── default.conf nginx config: port 58881 + tile CORS proxies
+├── build.py single-file HTML bundler (see "Single-file build")
├── Dockerfile builds nginx:alpine image
-└── docker-compose.yml one-service Compose file
+├── docker-compose.yml one-service Compose file
+└── LICENSE MIT (application code)
```
-## Licenses
+## Licenses & data attribution
-- **Background application code**: MIT
+- **Background application code**: MIT (see `LICENSE`)
- **SunCalc** (vendored): ISC -
- **Open-Meteo** (weather API): CC BY 4.0 -
-- **OpenStreetMap** (basemap tiles, Overpass road/place data): ODbL -
-
-- **Copernicus DEM** (terrain via terrarium tiles): open data
+- **OpenStreetMap** (basemap tiles via ESRI/Carto, Overpass road and
+ place data): ODbL -
+- **Copernicus DEM** (terrain via Mapzen terrarium tiles): open data
- **NASA GIBS VIIRS** (optional night-lights overlay): public domain
+- **Yale Bright Star Catalogue** (star positions, via `vendor/stars.json`)
- **OpenStreetMap Nominatim** (reverse geocoding): ODbL -
-
\ No newline at end of file
+
diff --git a/ash.css b/ash.css
index 3b33ab6..0c8ad42 100644
--- a/ash.css
+++ b/ash.css
@@ -1,5 +1,4 @@
-/* Ash palette by zink (this project)
- * Original: https://git.familie-zink.org/zink/omarchy-ash-theme
+/* Ash palette by Richard Zink (this project)
* License: See repository LICENSE
*
* Generic color system - simple to customize for other themes.
diff --git a/background.html b/background.html
index 0fecd99..4ea1cec 100644
--- a/background.html
+++ b/background.html
@@ -12,6 +12,15 @@
var cssFile = window.THEMES && window.THEMES[theme];
var css = document.getElementById('theme-css');
if (css) css.href = cssFile || (window.THEMES && window.THEMES.ash) || 'ash.css';
+ // standalone build (build.py): themes are embedded in
+ // window.__inlineThemeCss and the external link is disabled
+ if (window.__inlineThemeCss && window.__inlineThemeCss[theme]) {
+ if (css) css.media = 'not all';
+ var s = document.createElement('style');
+ s.id = 'theme-css-inline';
+ s.textContent = window.__inlineThemeCss[theme];
+ document.head.appendChild(s);
+ }
})();
@@ -519,6 +528,7 @@
+
inside an inline block would end it early
+ return s.replace('', '<\\/')
+
+
+def inline_script(html, tag, body):
+ old = '' % tag
+ new = '' % body
+ if old not in html:
+ raise SystemExit('build.py: expected tag not found: ' + tag)
+ return html.replace(old, new, 1)
+
+
+def parse_themes(config_src):
+ m = re.search(r'window\.THEMES\s*=\s*\{(.*?)\}', config_src, re.S)
+ if not m:
+ raise SystemExit('build.py: window.THEMES not found in config.js')
+ return dict(re.findall(r"'([^']+)'\s*:\s*'([^']+\.css)'", m.group(1)))
+
+
+def main():
+ out = sys.argv[1] if len(sys.argv) > 1 else 'background-standalone.html'
+ html = read('background.html')
+
+ config_src = read('config.js')
+ themes = parse_themes(config_src)
+
+ # embedded data preamble (before clouds3d's own scripts run)
+ stars = json.loads(read('vendor/stars.json'))
+ theme_css = {name: read(fname) for name, fname in themes.items()}
+ embed = (
+ '\n'
+ % (json.dumps(stars, separators=(',', ':')),
+ json.dumps(theme_css))
+ )
+ anchor = ''
+ html = html.replace(anchor, embed + anchor, 1)
+
+ html = inline_script(html, 'config.js', read('config.js'))
+ html = inline_script(html, 'i18n.js', read('i18n.js'))
+ html = inline_script(html, 'vendor/suncalc.js', read('vendor/suncalc.js'))
+ html = inline_script(html, 'vendor/plane3d.js', read('vendor/plane3d.js'))
+ html = inline_script(html, 'vendor/clouds3d.js', read('vendor/clouds3d.js'))
+
+ # the default stays but is
+ # disabled by the inline-theme loader; keep a guarded fallback anyway
+ html = html.replace('\n 0) FIG8 = buildFigure8(ORBIT_R);
// Anchor tile at zoom 11, computed from the user's CONFIG.location with
// the same Web Mercator math as the OSM->world transforms (carLonToX/
-// carLatToZ). Fallback if unset: Passau 1100/707.
+// carLatToZ). Fallback if unset: central London (tile 1023/761).
var _baseLoc = (window.CONFIG && window.CONFIG.location) || {};
-var _baseLat = typeof _baseLoc.lat === 'number' ? _baseLoc.lat : 48.5667;
-var _baseLon = typeof _baseLoc.lon === 'number' ? _baseLoc.lon : 13.4319;
+var _baseLat = typeof _baseLoc.lat === 'number' ? _baseLoc.lat : 51.5074;
+var _baseLon = typeof _baseLoc.lon === 'number' ? _baseLoc.lon : -0.1278;
var _baseN = 1 << GROUND_TILE_Z;
var _baseRad = _baseLat * Math.PI / 180;
var GROUND_BASE_X = Math.floor((_baseLon + 180) / 360 * _baseN);
@@ -1804,6 +1804,8 @@ function placeNightTile(tileX, tileY, canvasX, canvasY) {
var CAR_CFG = (window.CONFIG && window.CONFIG.terrain) || {};
var CAR_ON = CAR_CFG.carLights !== false;
var CITY_ON = CAR_CFG.cityLights !== false;
+// re-entrancy guard for the periodic fetch cycles (15 min timer)
+var carFetching = false, cityFetching = false;
function carCells(idBase, tagRx, s, w, n, e, latStep, lonStep) {
var out = [];
@@ -1835,6 +1837,85 @@ var CAR_ENDPOINTS = [
'https://overpass-api.de/api/interpreter',
'https://overpass.kumi.systems/api/interpreter'
];
+// spread first-attempt load across the mirrors per cell index
+CAR_QUERIES.forEach(function (e, i) { e.start = i % CAR_ENDPOINTS.length; });
+
+// ---- Polite global Overpass gate ---------------------------------------
+// Cars, places and airports share ONE gate: at most 2 queries in flight
+// and at least 900 ms between starts. Three fetchers bursting their
+// grids concurrently used to trip the mirrors' rate limits, which made
+// whole layers fail. One opQuery = one HTTP attempt (rotated mirror,
+// 30 s client-side timeout so a hanging instance never blocks the gate).
+var _opActive = 0, _opLast = 0, _opQueue = [];
+function isValidOverpass(t) {
+ if (!t) return false;
+ try {
+ var d = JSON.parse(t);
+ return !!(d && Array.isArray(d.elements));
+ } catch (e) { return false; }
+}
+function _opPump() {
+ var now = Date.now();
+ while (_opActive < 2 && _opQueue.length && now - _opLast >= 900) {
+ _opActive++; _opLast = now; _opQueue.shift()();
+ }
+ if (_opQueue.length) setTimeout(_opPump, Math.max(50, 950 - (Date.now() - _opLast)));
+}
+function opGate() {
+ return new Promise(function (go) {
+ _opQueue.push(function () {
+ go(function () { _opActive--; _opPump(); });
+ });
+ _opPump();
+ });
+}
+function opQuery(q, startIdx, attempt) {
+ var release;
+ return opGate().then(function (r) {
+ release = r;
+ var ep = CAR_ENDPOINTS[((attempt || 0) + (startIdx || 0)) % CAR_ENDPOINTS.length];
+ var ctl = null, timer = null;
+ try { ctl = new AbortController(); timer = setTimeout(function () { ctl.abort(); }, 30000); } catch (e) {}
+ return fetch(ep, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: 'data=' + encodeURIComponent(q),
+ signal: ctl ? ctl.signal : undefined
+ }).then(function (resp) {
+ if (timer) clearTimeout(timer);
+ if (resp.status !== 200) return null;
+ return resp.text().then(function (t) {
+ // only ever hand back - and cache - a valid Overpass JSON;
+ // a stored HTML error page once poisoned a cell forever
+ return isValidOverpass(t) ? t : null;
+ });
+ }, function () {
+ if (timer) clearTimeout(timer);
+ return null;
+ }).then(function (txt) {
+ release();
+ return txt;
+ }, function () {
+ // resp.text() can still reject (abort mid-body) - the gate
+ // slot MUST be returned or the whole queue deadlocks
+ release();
+ return null;
+ });
+ });
+}
+// full retry ladder over all mirrors, 2.5 s pause between failed attempts
+function opTry(q, startIdx) {
+ var MAX = CAR_ENDPOINTS.length * 2;
+ function go(a) {
+ if (a >= MAX) return Promise.resolve(null);
+ return opQuery(q, startIdx, a).then(function (txt) {
+ if (txt) return txt;
+ return new Promise(function (r) { setTimeout(r, 2500); })
+ .then(function () { return go(a + 1); });
+ });
+ }
+ return go(0);
+}
var CAR_EVERY = 0.16; // world units (~2 km) between ribbon nodes
var CAR_W = 0.005; // ribbon half width (world units)
var CAR_Y = 0.004; // lift above terrain surface (~50 m)
@@ -1900,7 +1981,9 @@ function resamplePoly(pts) {
}
function fetchMotorways() {
- if (!CAR_ON) return;
+ if (!CAR_ON || carFetching) return;
+ carFetching = true;
+ var fresh = 0; // cells that were NOT already cached this cycle
var groups = { motorway: [], secondary: [] };
function parseWays(txt) {
var data;
@@ -1918,9 +2001,6 @@ function fetchMotorways() {
if (rp.length > 1) groups[key].push(rp);
});
}
- function carDelay(ms) {
- return new Promise(function (res) { setTimeout(res, ms); });
- }
// rebuild ribbons shortly after each arriving cell - the lights grow
// in region by region instead of appearing after the whole chain
var rebuildT = null;
@@ -1935,50 +2015,34 @@ function fetchMotorways() {
function loadCell(entry) {
return openTileDB().then(function (db) {
return tileDbGet(db, entry.id).then(function (rec) {
- if (rec && rec.json) { entry._cached = true; return rec.json; }
- // two rounds over all endpoints; transient 504s are routine
- var MAX_ATTEMPTS = CAR_ENDPOINTS.length * 2;
- function tryAttempt(a) {
- if (a >= MAX_ATTEMPTS) return null;
- var ep = CAR_ENDPOINTS[a % CAR_ENDPOINTS.length];
- return (a === 0 ? Promise.resolve() : carDelay(2500))
- .then(function () {
- return fetch(ep, {
- method: 'POST',
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- body: 'data=' + encodeURIComponent(entry.q)
- });
- }).then(function (r) {
- if (!r.ok) return tryAttempt(a + 1); // 504: next attempt
- return r.text();
- }, function () {
- return tryAttempt(a + 1); // network/CORS error
- }).then(function (txt) {
- if (txt && db) {
- tileDbPut(db, { id: entry.id, json: txt, savedAt: Date.now() });
- }
- return txt;
- });
- }
- return tryAttempt(0);
+ // validate stored JSON - a poisoned cache record (HTML error
+ // page saved by an older build) must trigger a refetch
+ if (rec && rec.json && isValidOverpass(rec.json)) { entry._cached = true; return rec.json; }
+ return opTry(entry.q, entry.start).then(function (txt) {
+ if (txt && db) tileDbPut(db, { id: entry.id, json: txt, savedAt: Date.now() });
+ return txt;
+ });
});
}).then(function (txt) {
- if (txt) { parseWays(txt); scheduleRebuild(); }
+ if (txt) {
+ parseWays(txt);
+ // cached cells are re-parsed but never trigger a rebuild;
+ // only freshly fetched data changes the ribbons
+ if (!entry._cached) { fresh++; scheduleRebuild(); }
+ }
else console.warn('[clouds3d] overpass cell failed: ' + entry.id);
});
}
- // sequential with breathing room for FETCHED cells; cached cells
- // stream through without delay. Failed cells are retried on the next
- // page load (successful ones stay cached forever)
- var chain = Promise.resolve();
- CAR_QUERIES.forEach(function (entry) {
- chain = chain.then(function () {
- return loadCell(entry).then(function () {
- return carDelay(entry._cached ? 0 : 700);
- });
- });
- });
- chain.then(function () {
+ // cached cells resolve instantly; fresh ones are paced by the global
+ // gate. Failed cells are retried on the next 15-min cycle
+ // (successful ones stay cached forever).
+ runPool(CAR_QUERIES, 2, function (entry) {
+ return loadCell(entry);
+ }).then(function () {
+ carFetching = false;
+ // a fully cached cycle with no prior failure means the ribbons
+ // are already in the state this cycle would rebuild - skip it
+ if (!fresh && carGeo) return;
carGeo = groups;
buildCarwayVBO(elevGl || (clouds3dState && clouds3dState.gl));
console.log('[clouds3d] car layer: ' + groups.motorway.length +
@@ -2007,24 +2071,35 @@ var CITY_CLASS = [
];
function fetchSettlements() {
- if (!CITY_ON) return;
- // same tiled pattern as the road queries (a single full-box place
- // dump would be ~100k nodes); fine 0.55 x 0.65 deg cells answer in
- // seconds even on busy instances - full 1 deg boxes drew 504s
+ if (!CITY_ON || cityFetching) return;
+ cityFetching = true;
+ var fresh = 0;
+ // fine 0.55 x 0.65 deg cells: every query answers in <20 s, which is
+ // the only reliable regime on the public mirrors (big boxes server-
+ // time out at any timeout: value and come back as unparseable junk).
+ // The shared 2-lane gate keeps the 78-cell grid polite over time.
var cells = [];
+ var ci = 0;
for (var la = 46.8; la < 50.09; la += 0.55) {
for (var lo = 9.4; lo < 17.29; lo += 0.65) {
+ var bb = la.toFixed(2) + ',' + lo.toFixed(2) + ',' +
+ Math.min(50.1, la + 0.55).toFixed(2) + ',' +
+ Math.min(17.3, lo + 0.65).toFixed(2);
+ var rx = '["place"~"^(city|town|suburb|village|hamlet)$"]';
cells.push({
id: 'geo/place_' + la.toFixed(2) + '_' + lo.toFixed(2),
- q: '[out:json][timeout:45];node["place"~"^(city|town|suburb|village|hamlet)$"](' +
- la.toFixed(2) + ',' + lo.toFixed(2) + ',' +
- Math.min(50.1, la + 0.55).toFixed(2) + ',' +
- Math.min(17.3, lo + 0.65).toFixed(2) + ');out tags;'
+ start: ci % CAR_ENDPOINTS.length, // spread mirror load
+ // all three geometry types: many places (esp. German city
+ // districts) exist only as way/relation, not as a node
+ q: '[out:json][timeout:40];(node' + rx + '(' + bb + ');' +
+ 'way' + rx + '(' + bb + ');' +
+ 'relation' + rx + '(' + bb + '););out center tags;'
});
+ ci++;
}
}
var list = [];
- function delay(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
+ var seenB = {}; // bucket -> index in list: dedupe node/way/rel of one place
var rebuildT = null;
function scheduleRebuild() {
if (rebuildT) return;
@@ -2038,55 +2113,54 @@ function fetchSettlements() {
var data;
try { data = JSON.parse(txt); } catch (e) { return; }
(data.elements || []).forEach(function (el) {
- if (el.type !== 'node' || !el.tags || el.lat == null || el.lon == null) return;
+ if (!el.tags) return;
var rank = PLACE_RANK[el.tags.place];
if (rank == null) return;
- var x = carLonToX(el.lon), z = carLatToZ(el.lat);
+ // nodes carry lat/lon; ways/relations a computed center
+ var lat = el.lat != null ? el.lat : (el.center && el.center.lat);
+ var lon = el.lon != null ? el.lon : (el.center && el.center.lon);
+ if (lat == null || lon == null) return;
+ var x = carLonToX(lon), z = carLatToZ(lat);
if (Math.abs(x) > groundTilesX() / 2 + 2 || Math.abs(z) > groundTilesY() / 2 + 2) return;
- list.push({ x: x, z: z, rank: rank, pop: parseFloat(el.tags.population) || 0 });
+ var popv = parseFloat(el.tags.population) || 0;
+ // many cities exist BOTH as a named node and as a place boundary
+ // relation - merge per ~3 km bucket instead of double-drawing
+ var key = Math.round(x * 4) + '_' + Math.round(z * 4);
+ var prev = seenB[key];
+ if (prev !== undefined) {
+ var old = list[prev];
+ if (rank < old.rank) old.rank = rank;
+ if (popv > old.pop) old.pop = popv;
+ return;
+ }
+ seenB[key] = list.length;
+ list.push({ x: x, z: z, rank: rank, pop: popv });
});
}
function loadCell(entry) {
return openTileDB().then(function (db) {
return tileDbGet(db, entry.id).then(function (rec) {
- if (rec && rec.json) { entry._cached = true; return rec.json; }
- var MAX_ATTEMPTS = CAR_ENDPOINTS.length * 2;
- function tryAttempt(a) {
- if (a >= MAX_ATTEMPTS) return null;
- var ep = CAR_ENDPOINTS[a % CAR_ENDPOINTS.length];
- return (a === 0 ? Promise.resolve() : delay(2500))
- .then(function () {
- return fetch(ep, {
- method: 'POST',
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- body: 'data=' + encodeURIComponent(entry.q)
- });
- }).then(function (r) {
- if (!r.ok) return tryAttempt(a + 1);
- return r.text();
- }, function () {
- return tryAttempt(a + 1);
- }).then(function (txt) {
- if (txt && db) tileDbPut(db, { id: entry.id, json: txt, savedAt: Date.now() });
- return txt;
- });
- }
- return tryAttempt(0);
+ // validate stored JSON - a poisoned cache record (HTML error
+ // page saved by an older build) must trigger a refetch
+ if (rec && rec.json && isValidOverpass(rec.json)) { entry._cached = true; return rec.json; }
+ return opTry(entry.q, entry.start).then(function (txt) {
+ if (txt && db) tileDbPut(db, { id: entry.id, json: txt, savedAt: Date.now() });
+ return txt;
+ });
});
}).then(function (txt) {
- if (txt) { parse(txt); scheduleRebuild(); }
+ if (txt) {
+ parse(txt);
+ if (!entry._cached) { fresh++; scheduleRebuild(); }
+ }
else console.warn('[clouds3d] place cell failed: ' + entry.id);
});
}
- var chain = Promise.resolve();
- cells.forEach(function (entry) {
- chain = chain.then(function () {
- return loadCell(entry).then(function () {
- return delay(entry._cached ? 0 : 700);
- });
- });
- });
- chain.then(function () {
+ runPool(cells, 2, function (entry) {
+ return loadCell(entry);
+ }).then(function () {
+ cityFetching = false;
+ if (!fresh && cityGeo) return; // fully cached cycle - nothing changed
cityGeo = list.slice();
buildCityVBO(elevGl || (clouds3dState && clouds3dState.gl));
console.log('[clouds3d] settlements: ' + list.length + ' places -> ' + cityCount + ' lights');
@@ -2112,7 +2186,10 @@ var CAR_VERT_SRC = [
'float dir = aMeta.y >= 0.0 ? 1.0 : -1.0;',
'vec3 toCam = uCamPos - aPos;',
'vDist = length(toCam);',
- 'vApproach = dot(vec3(aMeta.z, 0.0, aMeta.w) * dir, normalize(toCam));',
+ // NOTE the minus: the flow equation moves a pulse toward DECREASING
+ // arc length, i.e. actual travel = -tangent for side +1. Without
+ // the flip, approaching cars would show taillights (red).
+ 'vApproach = -dot(vec3(aMeta.z, 0.0, aMeta.w) * dir, normalize(toCam));',
'gl_Position = uProj * uView * vec4(aPos, 1.0);',
'}'
].join('\n');
@@ -2139,12 +2216,20 @@ var CAR_FRAG_SRC = [
// far distance: fade the lights out smoothly toward ~150 km
'float df = 1.0 - smoothstep(7.0, 12.0, vDist);',
'if (df <= 0.0) discard;',
- 'float body = smoothstep(0.0, 0.5, 1.0 - abs(vSide));',
+ // distance-driven cross-section: widen the bright plateau as the
+ // ribbon shrinks below a pixel (kills lateral shimmer). ~0.3 at
+ // >6.5 units (~85 km), flat near the camera.
+ 'float grow = clamp((vDist - 3.0) * 0.09, 0.0, 0.3);',
+ 'float body = 1.0 - smoothstep(0.5 + grow, 1.0, abs(vSide));',
'float dir = vSide >= 0.0 ? 1.0 : -1.0;',
// cars are slotted pulses; hash1(s) jitters each slot's position
// (varying spacing) and a per-slot sine wobble makes every vehicle
// crawl or overtake a bit (varying speeds). No trails - points only.
'float flow = vLen / uDash + dir * uTime * uSpeed;',
+ // pulse width grows with distance (~1.5 px floor at the fade edge)
+ // so distant cars read as slightly larger, steady lights instead
+ // of one-pixel flicker
+ 'float slot = 0.11 + clamp((vDist - 2.5) * 0.05, 0.0, 0.4);',
'float n = floor(flow);',
'float inten = 0.0;',
'for (int k = -2; k <= 2; k++) {',
@@ -2154,10 +2239,16 @@ var CAR_FRAG_SRC = [
'float off = 0.05 + hash1(s) * 0.8;',
'float w = 0.25 * sin(uTime * uSpeed * (1.5 + hash1(s + 7.1) * 4.0) + hash1(s + 3.7) * 6.283);',
'float d = abs(flow - (s + off + w));',
- 'inten = max(inten, max(0.0, 1.0 - d / 0.11));',
+ 'inten = max(inten, max(0.0, 1.0 - d / slot));',
'}',
'}',
- 'if (inten <= 0.0) discard;',
+ // beyond ~55 km merge the pulses into a steady traffic glow:
+ // a constant reads as a line of lights, individual slots would
+ // pop as they cross pixel boundaries
+ 'float far = smoothstep(4.0, 6.5, vDist);',
+ 'float steady = clamp(dens * 0.55, 0.0, 0.55);',
+ 'inten = max(inten * (1.0 - 0.75 * far), steady * far);',
+ 'if (inten <= 0.001) discard;',
'inten = pow(inten, 1.8);',
// approaching the camera -> white headlights, driving away -> red',
'vec3 col = mix(vec3(1.0, 0.12, 0.08), vec3(1.0, 0.96, 0.88),',
@@ -2321,7 +2412,7 @@ var CITY_VERT_SRC = [
'void main() {',
'vDist = length(uCamPos - aPos);',
'vBri = aBri;',
- 'gl_PointSize = clamp(aSize * uScale * 750.0 / max(vDist, 0.1), 1.2, 18.0);',
+ 'gl_PointSize = clamp(aSize * uScale * 750.0 / max(vDist, 0.1), 1.6, 18.0);',
'gl_Position = uProj * uView * vec4(aPos, 1.0);',
'}'
].join('\n');
@@ -2349,6 +2440,26 @@ function cityRand(seed) {
return x - Math.floor(x);
}
+// small worker pool: run per-cell fetchers with N concurrent lanes
+// (Overpass instances tolerate ~2-3 parallel queries politely)
+function runPool(entries, lanes, worker) {
+ return new Promise(function (resolve) {
+ var idx = 0, active = 0;
+ function next() {
+ if (idx >= entries.length && active === 0) { resolve(); return; }
+ while (active < lanes && idx < entries.length) {
+ (function (entry) {
+ active++;
+ worker(entry).then(function () {
+ active--; next();
+ });
+ })(entries[idx++]);
+ }
+ }
+ next();
+ });
+}
+
// same guard as the car rebuild: after deleting the old city VBO every
// still-enabled attrib slot must point at a live buffer, or the next
// full-screen draw warns "no buffer is bound to enabled attribute".
@@ -2416,6 +2527,395 @@ function buildCityVBO(gl) {
}
}
+// ---- Aircraft traffic ---------------------------------------------------
+// Civil aviation between the real aerodromes of the region (OSM
+// aeroway=aerodrome, one Overpass query, cached like the road/place
+// data). Each aircraft rolls a RANDOM destination airport at take-off
+// (hubs weighted via CONFIG.flights wHub/wField/wStrip) and flies a
+// gently curved leg there - a quadratic
+// Bezier resampled to arc-length, so constant speed means constant
+// ground speed - with the full profile: take-off and climb, cruise
+// (CONFIG.flights altitudeKm), descent, landing, a short ground hold
+// (holdMinS..holdMaxS), then a fresh destination. Low phase hugs the
+// terrain mesh (climb/descent start at field elevation, not at sea
+// level). By night they blink (white strobes + red beacon), by day they
+// cross the sky as sun-shaded low-poly silhouettes with condensation
+// trails (see the contrail section for all trail parameters). Traffic
+// density is deliberately UNIFORM around the clock - aviation does not
+// follow the car rush-hour curve.
+// Data (c) OpenStreetMap contributors, ODbL.
+var FLT_CFG = (window.CONFIG && window.CONFIG.flights) || {};
+function fltNum(v, d) { return (typeof v === 'number' && isFinite(v)) ? v : d; }
+var FLT_ON = FLT_CFG.enabled !== false;
+var FLT_ALT_KM = fltNum(FLT_CFG.altitudeKm, 14);
+var FLT_Y = FLT_ALT_KM / KM_PER_TILE; // cruise altitude
+var FLT_CLIMB_U = fltNum(FLT_CFG.climbKm, 100) / KM_PER_TILE;
+var FLT_MAX = Math.max(4, Math.min(240, Math.round(fltNum(FLT_CFG.count, 84))));
+var FLT_SPD = fltNum(FLT_CFG.speedKmh, 850) / 3600 / KM_PER_TILE;
+var FLT_SVAR = Math.max(0, Math.min(0.5, fltNum(FLT_CFG.speedVar, 0.18)));
+var FLT_MINLEG = fltNum(FLT_CFG.minLegKm, 60) / KM_PER_TILE;
+var FLT_HOLD0 = fltNum(FLT_CFG.holdMinS, 50);
+var FLT_HOLD1 = Math.max(FLT_HOLD0, fltNum(FLT_CFG.holdMaxS, 150));
+var FLT_W = [fltNum(FLT_CFG.wHub, 4), fltNum(FLT_CFG.wField, 2), fltNum(FLT_CFG.wStrip, 1)];
+var CT_CHANCE = Math.max(0, Math.min(1, fltNum(FLT_CFG.contrailChance, 0.6)));
+var CT_START_FRAC = Math.max(0.02, fltNum(FLT_CFG.contrailStartKm, 6) / FLT_ALT_KM);
+var CT_T = Math.max(10, fltNum(FLT_CFG.contrailLifeS, 300));
+var CT_HALF_W = Math.max(0.002, fltNum(FLT_CFG.contrailWidthM, 310) / 2000 / KM_PER_TILE);
+var CT_MAX = FLT_MAX;
+var CT_SEG = 8; // curve resolution per trail
+var fltAirs = null; // [{x,z,rank}] - destination pool
+var fltData = []; // live aircraft: {v,leg,s,gnd,gndEnd,seed,phase,big}
+var fltBuf = null, fltVbo = null, fltCount = 0;
+var fltState = []; // per frame render poses
+var fltProg = null, fltAttribs = null, fltUniforms = null;
+var fltFetching = false;
+var _fltLast = -1;
+
+function fltRand(seed) {
+ var x = Math.sin(seed * 127.1 + 311.7) * 43758.5453;
+ return x - Math.floor(x);
+}
+
+// weighted random destination at each take-off (CONFIG.flights w*)
+function fltPickDest(from, seed) {
+ var i, tot = 0, wt = function (a) {
+ var w = FLT_W[a.rank]; return (w > 0 ? w : 1);
+ };
+ for (i = 0; i < fltAirs.length; i++) if (i !== from) tot += wt(fltAirs[i]);
+ if (!tot) return -1;
+ var r = fltRand(seed) * tot;
+ for (i = 0; i < fltAirs.length; i++) {
+ if (i === from) continue;
+ r -= wt(fltAirs[i]);
+ if (r <= 0) return i;
+ }
+ return -1;
+}
+
+// gently curved leg: quadratic Bezier A->B whose control point sits
+// 3-14 % of the distance off the straight line (long-radius turns),
+// resampled to ARC LENGTH so constant v means constant ground speed
+function fltMakeLeg(a, b, seed) {
+ var A = fltAirs[a], B = fltAirs[b];
+ var dx = B.x - A.x, dz = B.z - A.z;
+ var L = Math.hypot(dx, dz) || 0.01;
+ var bend = (fltRand(seed * 3.7 + 1.1) - 0.5) * 0.28 * Math.min(L, 30);
+ var px = -dz / L, pz = dx / L;
+ var cx = (A.x + B.x) / 2 + px * bend * 2, cz = (A.z + B.z) / 2 + pz * bend * 2;
+ var n = Math.max(12, Math.min(40, Math.round(L * 3)));
+ var pts = [], lx = 0, lz = 0, dsum = 0;
+ for (var i = 0; i <= n; i++) {
+ var t = i / n, it = 1 - t;
+ var x = it * it * A.x + 2 * it * t * cx + t * t * B.x;
+ var z = it * it * A.z + 2 * it * t * cz + t * t * B.z;
+ if (i > 0) {
+ var sx = x - lx, sz = z - lz, sl = Math.hypot(sx, sz) || 1;
+ pts[pts.length - 1].tx = sx / sl; pts[pts.length - 1].tz = sz / sl;
+ dsum += sl;
+ }
+ pts.push({ x: x, z: z, d: dsum, tx: 0, tz: 0 });
+ lx = x; lz = z;
+ }
+ pts[pts.length - 1].tx = pts[pts.length - 2].tx;
+ pts[pts.length - 1].tz = pts[pts.length - 2].tz;
+ return { a: a, b: b, pts: pts, len: dsum };
+}
+
+// world pos + unit tangent at arc-length dist along a leg
+function fltLegAt(leg, dist, out) {
+ var p = leg.pts, i = 1;
+ while (i < p.length - 1 && p[i].d < dist) i++;
+ var p0 = p[i - 1], p1 = p[i];
+ var seg = (p1.d - p0.d) || 1;
+ var f = Math.max(0, Math.min(1, (dist - p0.d) / seg));
+ out.x = p0.x + (p1.x - p0.x) * f;
+ out.z = p0.z + (p1.z - p0.z) * f;
+ out.tx = p0.tx; out.tz = p0.tz;
+ return out;
+}
+
+function buildFlights(airports) {
+ fltAirs = airports;
+ fltData = [];
+ fltState = [];
+ if (!airports || airports.length < 2) {
+ fltCount = 0; fltBuf = new Float32Array(0); return;
+ }
+ var N = FLT_MAX;
+ for (var i = 0; i < airports.length * 6 && fltData.length < N; i++) {
+ var from = Math.floor(fltRand(i * 2.71 + 0.3) * airports.length) % airports.length;
+ var to = fltPickDest(from, i * 5.13 + 7.7);
+ if (to < 0) continue;
+ var leg = fltMakeLeg(from, to, i + 1);
+ if (leg.len < FLT_MINLEG) continue; // skip taxi hops
+ fltData.push({
+ v: FLT_SPD * (1 - FLT_SVAR + 2 * FLT_SVAR * fltRand(i * 3.1)),
+ leg: leg,
+ s: leg.len * fltRand(i * 7.7 + 2.2),
+ gnd: false,
+ gndEnd: 0,
+ seed: i + 1,
+ phase: fltRand(i * 13.3),
+ big: airports[to].rank === 0 ? 1.2 : (airports[to].rank === 1 ? 1.0 : 0.75)
+ });
+ }
+ fltCount = fltData.length;
+ fltBuf = new Float32Array(fltCount * 6); // pos3 + bri + phase + size
+}
+
+// advance every aircraft with the frame clock; the destination is rolled
+// FRESH at each take-off, so flows drift organically over the day
+function stepFlights(now) {
+ var dt = _fltLast < 0 ? 0.016 : Math.min(0.25, now - _fltLast);
+ _fltLast = now;
+ for (var i = 0; i < fltCount; i++) {
+ var f = fltData[i];
+ if (f.gnd) {
+ if (now >= f.gndEnd) {
+ var dest = fltPickDest(f.leg.b, now * 0.017 + f.seed * 91.7);
+ var nl = dest >= 0 ? fltMakeLeg(f.leg.b, dest, f.seed * 17.3 + now * 0.37) : null;
+ if (nl && nl.len >= FLT_MINLEG) {
+ f.leg = nl; f.s = 0; f.gnd = false;
+ f.big = fltAirs[dest].rank === 0 ? 1.2 : (fltAirs[dest].rank === 1 ? 1.0 : 0.75);
+ } else {
+ f.gndEnd = now + 30; // pick collapsed: brief wait, retry
+ }
+ }
+ continue;
+ }
+ f.s += f.v * dt;
+ if (f.s >= f.leg.len) {
+ f.s = f.leg.len;
+ f.gnd = true;
+ f.gndEnd = now + FLT_HOLD0 + (FLT_HOLD1 - FLT_HOLD0) * fltRand(f.seed * 5.3 + now * 0.11);
+ }
+ }
+}
+
+// current pose: world x/z, tangent, eased altitude fraction (0..1)
+var _fPos = { x: 0, z: 0, tx: 1, tz: 0 };
+function fltPose(f, out) {
+ var leg = f.leg, cl = Math.min(leg.len * 0.4, FLT_CLIMB_U), a;
+ if (f.gnd) a = 0;
+ else if (f.s < cl) a = f.s / cl;
+ else if (f.s > leg.len - cl) a = (leg.len - f.s) / cl;
+ else a = 1;
+ a = a * a * (3 - 2 * a);
+ fltLegAt(leg, f.s, _fPos);
+ out.x = _fPos.x; out.z = _fPos.z; out.tx = _fPos.tx; out.tz = _fPos.tz;
+ out.alt = a; out.s = f.s; out.gnd = f.gnd; out.leg = leg;
+ return out;
+}
+
+function fetchAirports() {
+ if (!FLT_ON || fltFetching) return;
+ fltFetching = true;
+ var q = '[out:json][timeout:30];(node["aeroway"="aerodrome"](46.8,9.4,50.1,17.3);' +
+ 'way["aeroway"="aerodrome"](46.8,9.4,50.1,17.3););out center tags;',
+ id = 'geo/airports_v1';
+ openTileDB().then(function (db) {
+ return tileDbGet(db, id).then(function (rec) {
+ if (rec && rec.json && isValidOverpass(rec.json)) return rec.json;
+ return opTry(q, 0).then(function (txt) {
+ if (txt && db) tileDbPut(db, { id: id, json: txt, savedAt: Date.now() });
+ return txt;
+ });
+ });
+ }).then(function (txt) {
+ fltFetching = false;
+ if (!txt) { console.warn('[clouds3d] aerodrome query failed'); return; }
+ var data;
+ try { data = JSON.parse(txt); } catch (e) { return; }
+ var seenXY = [], airports = [];
+ (data.elements || []).forEach(function (el) {
+ var lat = el.lat != null ? el.lat : (el.center && el.center.lat);
+ var lon = el.lon != null ? el.lon : (el.center && el.center.lon);
+ if (lat == null || lon == null) return;
+ var t = el.tags || {};
+ var rank = t.iata ? 0 : (t.icao ? 1 : (t.name ? 2 : 3));
+ var x = carLonToX(lon), z = carLatToZ(lat);
+ // collapse runways of one field: aerodromes ~ >4 km apart merge
+ for (var s2 = 0; s2 < seenXY.length; s2++) {
+ if (Math.hypot(seenXY[s2].x - x, seenXY[s2].z - z) < 0.3) {
+ if (rank < seenXY[s2].rank) { seenXY[s2].rank = rank; seenXY[s2].x = (seenXY[s2].x + x) / 2; seenXY[s2].z = (seenXY[s2].z + z) / 2; }
+ return;
+ }
+ }
+ if (rank > 2) return; // nameless strips: skip
+ var a = { x: x, z: z, rank: rank };
+ seenXY.push(a); airports.push(a);
+ });
+ if (!airports.length) return;
+ buildFlights(airports);
+ console.log('[clouds3d] flights: ' + airports.length + ' airfields -> ' +
+ fltData.length + ' aircraft (destinations rolled per take-off)');
+ });
+}
+
+var FLT_VERT_SRC = [
+ 'attribute vec3 aPos;',
+ 'attribute vec4 aBlink;', // x: brightness, y: blink phase, z: size, w: unused
+ 'uniform mat4 uProj;',
+ 'uniform mat4 uView;',
+ 'uniform vec3 uCamPos;',
+ 'uniform float uScale;',
+ 'varying float vBri;',
+ 'varying float vPhase;',
+ 'varying float vDist;',
+ 'void main() {',
+ 'vDist = length(uCamPos - aPos);',
+ 'vBri = aBlink.x;',
+ 'vPhase = aBlink.y;',
+ 'gl_PointSize = clamp(aBlink.z * uScale * 2.6, 3.0, 6.0);',
+ 'gl_Position = uProj * uView * vec4(aPos, 1.0);',
+ '}'
+].join('\n');
+
+var FLT_FRAG_SRC = [
+ 'precision highp float;',
+ 'uniform float uTime;',
+ 'uniform float uNight;',
+ 'varying float vBri;',
+ 'varying float vPhase;',
+ 'varying float vDist;',
+ 'void main() {',
+ 'float df = 1.0 - smoothstep(10.0, 16.0, vDist);', // ~130-200 km
+ 'if (df <= 0.0) discard;',
+ 'vec2 pc = gl_PointCoord - 0.5;',
+ 'float d = length(pc) * 2.0;',
+ // small bright disk with a faint halo -> reads as a light even at 3px
+ 'float core = max(exp(-d * d * 9.0), (1.0 - smoothstep(0.3, 0.55, d)) * 0.5);',
+ 'if (core <= 0.01) discard;',
+ // white strobe: fast double-flash every ~1.1 s
+ 'float t = fract(uTime * 0.9 + vPhase);',
+ 'float p1 = max(0.0, 1.0 - t * 8.0);',
+ 'float p2 = max(0.0, 1.0 - abs(t - 0.11) * 8.0);',
+ 'float flash = p1 * p1 + 0.7 * p2 * p2;',
+ // red anti-collision beacon: slower single blink
+ 'float red = 0.35 * pow(max(0.0, sin(uTime * 2.4 + vPhase * 6.283)), 4.0);',
+ 'vec3 col = vec3(1.0) * flash * 1.7 + vec3(1.0, 0.12, 0.06) * red;',
+ 'float a = uNight * vBri * df; // day: never drawn',
+ 'gl_FragColor = vec4(col * core * a, 1.0);',
+ '}'
+].join('\n');
+
+// ---- Daytime airliner model pass ---------------------------------------
+// Low-poly jet (vendor/plane3d.js, CC0-derived) for the SAME traffic: by
+// day the aircraft are sun-shaded silhouettes in a fixed stylized scale -
+// nose-to-tail exactly half the contrail width, so jet and trail always
+// read as one object at any distance.
+var PLANE_VERT_SRC = [
+ 'attribute vec3 aPos;', // unit-extent mesh, forward = +Z, up = +Y
+ 'attribute vec3 aNrm;',
+ 'attribute float aCol;', // 0 body, 1 glass
+ 'uniform mat4 uProj;',
+ 'uniform mat4 uView;',
+ 'uniform vec3 uOrigin;', // world anchor (cruise position)
+ 'uniform vec4 uOrit;', // cos(yaw), sin(yaw), sin(pitch), fade
+ 'uniform float uS;', // model radius in world units
+ 'varying float vCol;',
+ 'varying vec3 vN;',
+ 'void main() {',
+ 'float cy = uOrit.x, sy = uOrit.y, sp = uOrit.z, cp2 = sqrt(1.0 - sp * sp);',
+ 'vec3 p = aPos * uS;',
+ 'p = vec3(p.x, p.y * cp2 + p.z * sp, -p.y * sp + p.z * cp2);', // pitch
+ 'p = vec3(p.x * cy + p.z * sy, p.y, -p.x * sy + p.z * cy);', // yaw
+ 'vec3 n = aNrm;',
+ 'n = vec3(n.x, n.y * cp2 + n.z * sp, -n.y * sp + n.z * cp2);',
+ 'n = vec3(n.x * cy + n.z * sy, n.y, -n.x * sy + n.z * cy);',
+ 'vN = n; vCol = aCol;',
+ 'gl_Position = uProj * uView * vec4(uOrigin + p, 1.0);',
+ '}'
+].join('\n');
+
+var PLANE_FRAG_SRC = [
+ 'precision highp float;',
+ 'uniform vec3 uSun;', // world sun direction (x,y,z like __currentSunDir)',
+ 'varying float vCol;',
+ 'varying vec3 vN;',
+ 'void main() {',
+ 'vec3 alb = vCol < 0.5 ? vec3(0.93, 0.93, 0.95) : vec3(0.14, 0.16, 0.21);',
+ 'float l = 0.22 + 0.78 * max(0.0, dot(normalize(vN), uSun));',
+ 'gl_FragColor = vec4(alb * l, 1.0);',
+ '}'
+].join('\n');
+
+var planeV = null, planeVbo = null, planeProg = null, planeU = null, planeA = null, planeN = 0;
+
+// ---- Condensation trails -------------------------------------------------
+// Aircraft above the configured formation altitude (CONFIG.flights
+// contrailStartKm) may leave a contrail - contrailChance of them do (the
+// ice-humidity window is not everywhere), and the stripe follows the
+// curved leg: the leg polyline is walked back contrailLifeS worth of
+// ground distance in 8 curve segments and swept into a ribbon
+// (contrailWidthM wide). The fragment shader dissolves it into a cloud
+// stripe: density patches along the length, wavy eroded edges (per-trail
+// noise phase), alpha ramping up toward the head. Fades out with dayf
+// (dark exhaust is invisible anyway).
+var ctBuf = null, ctVbo = null, ctVerts = 0;
+var ctProg = null, ctA = null, ctU = null;
+
+function _ctVert(x, y, z, u, v, s, p) {
+ var i = ctVerts * 7;
+ ctBuf[i] = x; ctBuf[i + 1] = y; ctBuf[i + 2] = z;
+ ctBuf[i + 3] = u; ctBuf[i + 4] = v; ctBuf[i + 5] = s; ctBuf[i + 6] = p;
+ ctVerts++;
+}
+
+var CT_VERT_SRC = [
+ 'attribute vec3 aPos;',
+ 'attribute float aU;', // 0 tail .. 1 head
+ 'attribute float aV;', // -1 .. +1 across the ribbon
+ 'attribute float aS;', // per-trail strength (gate x day x fade)',
+ 'attribute float aP;', // per-trail noise phase
+ 'uniform mat4 uProj;',
+ 'uniform mat4 uView;',
+ 'varying float vU;',
+ 'varying float vV;',
+ 'varying float vS;',
+ 'varying float vP;',
+ 'void main() {',
+ 'vU = aU; vV = aV; vS = aS; vP = aP;',
+ 'gl_Position = uProj * uView * vec4(aPos, 1.0);',
+ '}'
+].join('\n');
+
+var CT_FRAG_SRC = [
+ 'precision mediump float;',
+ 'varying float vU;',
+ 'varying float vV;',
+ 'varying float vS;',
+ 'varying float vP;',
+ 'void main() {',
+ // density patches drifting along the stripe
+ 'float along = vU * 26.0 + vP * 31.0;',
+ 'float dens = 0.62 + 0.38 * sin(along) * sin(along * 2.7 + 1.3);',
+ 'if (dens < 0.0) dens = 0.0;',
+ // eroded, uneven edges: the stripe narrows and widens along u
+ 'float wob = 0.45 + 0.55 * (0.5 + 0.5 * sin(along * 3.1 + 7.0));',
+ 'float side = abs(vV);',
+ 'float edge = 1.0 - smoothstep(wob * 0.30, wob, side);',
+ // older tail also thins out across its width
+ 'edge *= 0.35 + 0.65 * pow(vU, 0.6);',
+ 'if (edge <= 0.0) discard;',
+ 'float a = vS * pow(vU, 1.25) * 0.95 * dens * edge;',
+ 'if (a <= 0.004) discard;',
+ 'gl_FragColor = vec4(0.95, 0.96, 1.0, a);',
+ '}'
+].join('\n');
+
+function decodePlaneVerts() {
+ if (planeV || !window.__planeVerts) return;
+ try {
+ var bin = atob(window.__planeVerts);
+ var buf = new ArrayBuffer(bin.length);
+ var u8 = new Uint8Array(buf);
+ for (var i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
+ var f = new Float32Array(buf);
+ if (f.length % 7 === 0 && f.length > 21) { planeV = f; planeN = f.length / 7; }
+ } catch (e) { /* malformed data: silently stay point-only */ }
+}
+
// ---- Real-sky: star catalogue + Milky Way ------------------------------
// Yale Bright Star Catalog (vendor/stars.json): 8.7k real stars.
// The catalogue stars are rendered as GL_POINTS (crisp discs + glow,
@@ -2425,7 +2925,7 @@ function buildCityVBO(gl) {
var skyTexCanvas = null, skyTexture = null, skyTexGl = null;
var skyPts = null; // { prog, vbo, count, a0, a1, u: {...} }
var STAR_PH = 2.9; // phase so the plausible constellations sit high at night
-var _skyLat = ((window.CONFIG && window.CONFIG.location && window.CONFIG.location.lat) || 48.5667) * Math.PI / 180;
+var _skyLat = ((window.CONFIG && window.CONFIG.location && window.CONFIG.location.lat) || 51.5074) * Math.PI / 180;
var SKY_SIN_LAT = Math.sin(_skyLat), SKY_COS_LAT = Math.cos(_skyLat);
function kelvinToRGB(k) {
@@ -2471,7 +2971,7 @@ var SKYPTS_FRAG = [
'float d = length(pc) * 2.0;',
'float disc = 1.0 - smoothstep(0.45, 1.0, d);',
'float glow = 0.22 * exp(-d * 2.8);',
- 'float twk = 0.85 + 0.15 * sin(uTime * 1.6 + vC.w * 33.0 + vAlt * 451.7 + vC.r * 180.3);',
+ 'float twk = 0.96 + 0.04 * sin(uTime * 1.6 + vC.w * 33.0 + vAlt * 451.7 + vC.r * 180.3);',
'float altf = smoothstep(-0.02, 0.12, vAlt);',
// vC.w = per-star brightness (mag-based, floored so faint stars
// stay clearly visible), giving an even overall look
@@ -2585,6 +3085,12 @@ function buildSkyTexture(stars) {
function loadSkyTexture(gl) {
if (skyTexture || skyTexCanvas) return;
skyTexGl = gl;
+ // build.py-generated standalone HTML injects the catalogue into
+ // window.__starsData so no fetch (and no vendor/ folder) is needed
+ if (window.__starsData) {
+ buildSkyTexture(window.__starsData);
+ return;
+ }
fetch('vendor/stars.json')
.then(function (r) { if (!r.ok) throw new Error('stars http ' + r.status); return r.json(); })
.then(function (stars) { buildSkyTexture(stars); })
@@ -2633,6 +3139,13 @@ function initGroundSystem(glCtx) {
initNightSystem(glCtx);
fetchMotorways();
fetchSettlements();
+ fetchAirports();
+ // periodic re-runs (same rhythm as the weather poll): cached cells
+ // short-circuit, so a cycle only ever costs the retries of cells
+ // that previously failed; running cycles never overlap (flags)
+ setInterval(fetchMotorways, 15 * 60 * 1000);
+ setInterval(fetchSettlements, 15 * 60 * 1000);
+ setInterval(fetchAirports, 15 * 60 * 1000);
loadSkyTexture(glCtx);
}
@@ -3092,6 +3605,243 @@ window.clouds3dRender = function(target) {
gl.depthMask(true);
}
+ // ---- Aircraft pass --------------------------------------------------
+ // Shared position update (take-off/climb/cruise/descent/land/hold
+ // profile), then: night = strobe point sprites, day = sun-shaded
+ // low-poly silhouettes sized to half the contrail width, plus the
+ // trails themselves.
+ // Density is uniform over the whole day (flights follow neither the
+ // car rush curve nor a night cut-off).
+ if (FLT_ON && fltCount && fltAirs) {
+ stepFlights(now);
+ var fl = { x: 0, z: 0, alt: 0, s: 0, tx: 1, tz: 0, gnd: false, leg: null };
+ for (var fi = 0; fi < fltCount; fi++) {
+ var f = fltData[fi];
+ fltPose(f, fl);
+ var gy = carYAt(fl.x, fl.z);
+ var yF = gy + (FLT_Y - gy) * fl.alt;
+ var k = fi * 6;
+ fltBuf[k] = fl.x; fltBuf[k + 1] = yF; fltBuf[k + 2] = fl.z;
+ fltBuf[k + 3] = f.big;
+ fltBuf[k + 4] = f.phase;
+ fltBuf[k + 5] = 1.0;
+ var st = fltState[fi] || (fltState[fi] = {});
+ st.y = yF; st.alt = fl.alt; st.s = fl.s; st.gnd = fl.gnd; st.leg = fl.leg;
+ st.tx = fl.tx; st.tz = fl.tz;
+ }
+ // -- night: blinking lights --
+ if (carNight > 0.06) {
+ if (!fltVbo) fltVbo = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, fltVbo);
+ gl.bufferData(gl.ARRAY_BUFFER, fltBuf, gl.STREAM_DRAW);
+ if (!fltProg) {
+ var fp = makeProgram(gl, FLT_VERT_SRC, FLT_FRAG_SRC);
+ if (!fp) { FLT_ON = false; }
+ else {
+ fltProg = fp;
+ fltAttribs = {
+ pos: gl.getAttribLocation(fltProg, 'aPos'),
+ blink: gl.getAttribLocation(fltProg, 'aBlink')
+ };
+ fltUniforms = {
+ proj: gl.getUniformLocation(fltProg, 'uProj'),
+ view: gl.getUniformLocation(fltProg, 'uView'),
+ camPos: gl.getUniformLocation(fltProg, 'uCamPos'),
+ scale: gl.getUniformLocation(fltProg, 'uScale'),
+ time: gl.getUniformLocation(fltProg, 'uTime'),
+ night: gl.getUniformLocation(fltProg, 'uNight')
+ };
+ }
+ }
+ if (fltProg) {
+ gl.useProgram(fltProg);
+ gl.uniformMatrix4fv(fltUniforms.proj, false, proj);
+ gl.uniformMatrix4fv(fltUniforms.view, false, view);
+ gl.uniform3f(fltUniforms.camPos, camX, camY, camZ);
+ gl.uniform1f(fltUniforms.scale, gl.drawingBufferHeight / 600);
+ gl.uniform1f(fltUniforms.time, now);
+ gl.uniform1f(fltUniforms.night, Math.min(1, carNight * 1.2));
+ gl.enable(gl.DEPTH_TEST); // mountains can pass in front
+ gl.depthMask(false);
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.ONE, gl.ONE);
+ gl.enableVertexAttribArray(fltAttribs.pos);
+ gl.vertexAttribPointer(fltAttribs.pos, 3, gl.FLOAT, false, 24, 0);
+ gl.enableVertexAttribArray(fltAttribs.blink);
+ gl.vertexAttribPointer(fltAttribs.blink, 3, gl.FLOAT, false, 24, 12);
+ gl.drawArrays(gl.POINTS, 0, fltCount);
+ gl.disable(gl.BLEND);
+ gl.depthMask(true);
+ }
+ }
+ // -- day: silhouettes --
+ decodePlaneVerts();
+ var sunA = window.__currentSunAlt || 0;
+ var dayf = 1 - Math.min(1, Math.max(0, -sunA / 0.30));
+ if (planeN && dayf > 0.02) {
+ if (!planeVbo) {
+ planeVbo = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, planeVbo);
+ gl.bufferData(gl.ARRAY_BUFFER, planeV, gl.STATIC_DRAW);
+ }
+ if (!planeProg) {
+ var pp = makeProgram(gl, PLANE_VERT_SRC, PLANE_FRAG_SRC);
+ if (pp) {
+ planeProg = pp;
+ planeA = {
+ pos: gl.getAttribLocation(pp, 'aPos'),
+ nrm: gl.getAttribLocation(pp, 'aNrm'),
+ col: gl.getAttribLocation(pp, 'aCol')
+ };
+ planeU = {
+ proj: gl.getUniformLocation(pp, 'uProj'),
+ view: gl.getUniformLocation(pp, 'uView'),
+ origin: gl.getUniformLocation(pp, 'uOrigin'),
+ orit: gl.getUniformLocation(pp, 'uOrit'),
+ s: gl.getUniformLocation(pp, 'uS'),
+ sun: gl.getUniformLocation(pp, 'uSun')
+ };
+ }
+ }
+ if (planeProg) {
+ var sunv = window.__currentSunDir || [0, 1, 0];
+ gl.useProgram(planeProg);
+ gl.uniformMatrix4fv(planeU.proj, false, proj);
+ gl.uniformMatrix4fv(planeU.view, false, view);
+ gl.uniform3f(planeU.sun, sunv[0], sunv[1], sunv[2]);
+ gl.bindBuffer(gl.ARRAY_BUFFER, planeVbo);
+ gl.enableVertexAttribArray(planeA.pos);
+ gl.vertexAttribPointer(planeA.pos, 3, gl.FLOAT, false, 28, 0);
+ gl.enableVertexAttribArray(planeA.nrm);
+ gl.vertexAttribPointer(planeA.nrm, 3, gl.FLOAT, false, 28, 12);
+ gl.enableVertexAttribArray(planeA.col);
+ gl.vertexAttribPointer(planeA.col, 1, gl.FLOAT, false, 28, 24);
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthMask(true); // opaque little solids
+ gl.disable(gl.BLEND);
+ gl.disable(gl.CULL_FACE);
+ var S0 = CT_HALF_W * 0.5; // model is unit-extent: tip-to-tail
+ // = CT_HALF_W = half the contrail width
+ for (var dj = 0; dj < fltCount; dj++) {
+ var briJ = fltBuf[dj * 6 + 3];
+ if (briJ <= 0) continue;
+ var px = fltBuf[dj * 6], py = fltBuf[dj * 6 + 1], pz = fltBuf[dj * 6 + 2];
+ var ddx = px - camX, ddy = py - camY, ddz = pz - camZ;
+ var dist = Math.sqrt(ddx * ddx + ddy * ddy + ddz * ddz);
+ var dfa = 1 - (dist - 10) / 6;
+ if (dfa > 1) dfa = 1; else if (dfa <= 0.02) continue;
+ var stj = fltState[dj];
+ var yaw = Math.atan2(stj.tx, stj.tz);
+ var cln = Math.min(stj.leg.len * 0.4, FLT_CLIMB_U);
+ var pitch = 0;
+ if (stj.alt > 0.02) {
+ if (stj.s < cln) pitch = 0.16;
+ else if (stj.s > stj.leg.len - cln) pitch = -0.16;
+ }
+ // fixed physical size: nose-to-tail = half the trail width
+ var scl = S0;
+ gl.uniform3f(planeU.origin, px, py, pz);
+ gl.uniform4f(planeU.orit, Math.cos(yaw), Math.sin(yaw), pitch, 0);
+ gl.uniform1f(planeU.s, scl);
+ gl.drawArrays(gl.TRIANGLES, 0, planeN);
+ }
+ gl.depthMask(true);
+ }
+ }
+ // -- contrails --
+ // Only CONFIG.flights.contrailChance of the aircraft leave a
+ // persistent stripe (the ice-humidity window is not everywhere),
+ // and the stripe follows the curved leg: walked backwards from
+ // the aircraft in fixed steps.
+ if (dayf > 0.02) {
+ if (!ctBuf) ctBuf = new Float32Array(CT_MAX * CT_SEG * 6 * 7);
+ ctVerts = 0;
+ var _cs = { x: 0, z: 0, tx: 1, tz: 0 };
+ var _sk0 = { x: 0, z: 0, px: 0, pz: 0, u: 0 };
+ var _sk1 = { x: 0, z: 0, px: 0, pz: 0, u: 0 };
+ for (var cj = 0; cj < fltCount; cj++) {
+ if (ctVerts + CT_SEG * 6 > CT_MAX * CT_SEG * 6) break;
+ var stc = fltState[cj];
+ if (!stc || stc.gnd) continue;
+ if (fltRand(cj * 3.7 + 0.77) > CT_CHANCE) continue;
+ // formation gate: above the configured trail altitude, smooth
+ var gt = (stc.alt - (CT_START_FRAC - 0.06)) / 0.12;
+ gt = gt < 0 ? 0 : (gt > 1 ? 1 : gt);
+ gt = gt * gt * (3 - 2 * gt);
+ if (gt < 0.05) continue;
+ var tl = Math.min(CT_T * fltData[cj].v, stc.s);
+ if (tl < 0.05) continue;
+ fltLegAt(stc.leg, stc.s - tl * 0.5, _cs);
+ var cdx = _cs.x - camX, cdy = stc.y - camY, cdz = _cs.z - camZ;
+ var distc = Math.sqrt(cdx * cdx + cdy * cdy + cdz * cdz);
+ // gentle distance thinning only - trails should stay
+ // readable across the whole visible sky
+ var dfc = 1 - (distc - 12) / 12;
+ if (dfc > 1) dfc = 1; else if (dfc <= 0.02) continue;
+ var strg = gt * dayf * dfc * 0.9;
+ var ph = fltData[cj].phase;
+ var hy = stc.y;
+ var prev = null, cur = _sk0;
+ for (var sk = 0; sk <= CT_SEG; sk++) {
+ var dd = stc.s - tl + tl * (sk / CT_SEG);
+ if (dd < 0) dd = 0;
+ fltLegAt(stc.leg, dd, _cs);
+ cur.x = _cs.x; cur.z = _cs.z;
+ cur.px = _cs.tz * CT_HALF_W; cur.pz = -_cs.tx * CT_HALF_W;
+ cur.u = sk / CT_SEG;
+ if (prev) {
+ _ctVert(prev.x + prev.px, hy, prev.z + prev.pz, prev.u, 1, strg, ph);
+ _ctVert(prev.x - prev.px, hy, prev.z - prev.pz, prev.u, -1, strg, ph);
+ _ctVert(cur.x - cur.px, hy, cur.z - cur.pz, cur.u, -1, strg, ph);
+ _ctVert(prev.x + prev.px, hy, prev.z + prev.pz, prev.u, 1, strg, ph);
+ _ctVert(cur.x - cur.px, hy, cur.z - cur.pz, cur.u, -1, strg, ph);
+ _ctVert(cur.x + cur.px, hy, cur.z + cur.pz, cur.u, 1, strg, ph);
+ }
+ prev = cur; cur = (cur === _sk0) ? _sk1 : _sk0;
+ }
+ }
+ if (ctVerts > 0) {
+ if (!ctVbo) ctVbo = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, ctVbo);
+ gl.bufferData(gl.ARRAY_BUFFER, ctBuf.subarray(0, ctVerts * 7), gl.STREAM_DRAW);
+ if (!ctProg) {
+ var cpm = makeProgram(gl, CT_VERT_SRC, CT_FRAG_SRC);
+ if (cpm) {
+ ctProg = cpm;
+ ctA = { pos: gl.getAttribLocation(cpm, 'aPos'),
+ u: gl.getAttribLocation(cpm, 'aU'),
+ v: gl.getAttribLocation(cpm, 'aV'),
+ s: gl.getAttribLocation(cpm, 'aS'),
+ p: gl.getAttribLocation(cpm, 'aP') };
+ ctU = { proj: gl.getUniformLocation(cpm, 'uProj'),
+ view: gl.getUniformLocation(cpm, 'uView') };
+ }
+ }
+ if (ctProg) {
+ gl.useProgram(ctProg);
+ gl.uniformMatrix4fv(ctU.proj, false, proj);
+ gl.uniformMatrix4fv(ctU.view, false, view);
+ gl.enableVertexAttribArray(ctA.pos);
+ gl.vertexAttribPointer(ctA.pos, 3, gl.FLOAT, false, 28, 0);
+ gl.enableVertexAttribArray(ctA.u);
+ gl.vertexAttribPointer(ctA.u, 1, gl.FLOAT, false, 28, 12);
+ gl.enableVertexAttribArray(ctA.v);
+ gl.vertexAttribPointer(ctA.v, 1, gl.FLOAT, false, 28, 16);
+ gl.enableVertexAttribArray(ctA.s);
+ gl.vertexAttribPointer(ctA.s, 1, gl.FLOAT, false, 28, 20);
+ gl.enableVertexAttribArray(ctA.p);
+ gl.vertexAttribPointer(ctA.p, 1, gl.FLOAT, false, 28, 24);
+ gl.depthMask(false);
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+ gl.drawArrays(gl.TRIANGLES, 0, ctVerts);
+ gl.disable(gl.BLEND);
+ gl.depthMask(true);
+ }
+ }
+ }
+ }
+
// ---- Cloud pass (uMode = 1) --------------------------------------
// Alpha-blend over the framebuffer (terrain mesh + sky+stars).
// Depth test/write OFF — the manual discard in the cloud shader
diff --git a/vendor/plane3d.js b/vendor/plane3d.js
new file mode 100644
index 0000000..590c262
--- /dev/null
+++ b/vendor/plane3d.js
@@ -0,0 +1,4 @@
+/* Airliner low-poly mesh (599 tris). Derived from 'Passenger Airplane' by Grab3D,
+ * CC0 (https://grab3d.com) - decimated and flattened for distant viewing.
+ * Non-indexed interleaved: pos(3) nrm(3) colIdx(1); unit extent 1.0. */
+window.__planeVerts = '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';