progress logs behind CONFIG.debug, CONFIG.antialias + high-performance context, cheaper cloud sampling (skip far detail octave and redundant self-shading probe), brighter night contrails via CONFIG.flights.contrailNight, window.__flightDbg counters, real sun/moon by default
This commit is contained in:
1 parent
113e4c522d
commit
6d21b37e8f
2 files changed
+98
-37
No files matched your search
@@ -17,6 +17,7 @@
|
|||||||
* tile/DEM/Overpass origin, figure-eight centre
|
* tile/DEM/Overpass origin, figure-eight centre
|
||||||
* renderScale internal 3D resolution (0.2..1) with blocky upscale
|
* renderScale internal 3D resolution (0.2..1) with blocky upscale
|
||||||
* maxFps scene frame cap (0 = free running)
|
* maxFps scene frame cap (0 = free running)
|
||||||
|
* antialias MSAA on the WebGL framebuffer (true/false)
|
||||||
* weatherSim ignore Open-Meteo and cycle the weather themes
|
* weatherSim ignore Open-Meteo and cycle the weather themes
|
||||||
* tiles basemap/elevation cache behaviour + debug overlays
|
* tiles basemap/elevation cache behaviour + debug overlays
|
||||||
* ground basemap style
|
* ground basemap style
|
||||||
@@ -51,7 +52,7 @@ window.CONFIG = {
|
|||||||
theme: 'ash', // 'ash' | 'nord' | 'gruvbox' (see window.THEMES)
|
theme: 'ash', // 'ash' | 'nord' | 'gruvbox' (see window.THEMES)
|
||||||
language: 'en', // 'en', 'de' - keys of the i18n.js dictionaries
|
language: 'en', // 'en', 'de' - keys of the i18n.js dictionaries
|
||||||
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
||||||
debug: 0, // 1 = debug panel with the theme palette
|
debug: 0, // 1 = debug panel + progress logs in the console
|
||||||
credits: true, // false = hide the attribution line
|
credits: true, // false = hide the attribution line
|
||||||
units: { temperature: 'C' }, // 'F' for Fahrenheit
|
units: { temperature: 'C' }, // 'F' for Fahrenheit
|
||||||
|
|
||||||
@@ -67,6 +68,10 @@ window.CONFIG = {
|
|||||||
// Scene frame cap. All motion is time-based, so this only changes
|
// Scene frame cap. All motion is time-based, so this only changes
|
||||||
// smoothness, never speed. 0 = free running (vsync).
|
// smoothness, never speed. 0 = free running (vsync).
|
||||||
maxFps: 30,
|
maxFps: 30,
|
||||||
|
// MSAA on the WebGL framebuffer. Costs fill rate, and with renderScale
|
||||||
|
// below 1 the pixel blocks dominate anyway - set false if the scene is
|
||||||
|
// GPU bound. Read once at context creation, so a reload is needed.
|
||||||
|
antialias: true,
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// Location
|
// Location
|
||||||
@@ -169,11 +174,16 @@ window.CONFIG = {
|
|||||||
wStrip: 1,
|
wStrip: 1,
|
||||||
// Contrails: contrailChance of the aircraft leaves a trail once
|
// Contrails: contrailChance of the aircraft leaves a trail once
|
||||||
// above contrailStartKm, visible for contrailLifeS seconds.
|
// above contrailStartKm, visible for contrailLifeS seconds.
|
||||||
contrailChance: 0.85,
|
contrailChance: 1.00,
|
||||||
contrailStartKm: 6,
|
contrailStartKm: 6,
|
||||||
contrailLifeS: 300,
|
contrailLifeS: 300,
|
||||||
contrailWidthM: 620, // full trail width
|
contrailWidthM: 620, // full trail width
|
||||||
contrailFadeKm: 100, // fully faded at this distance (ramp starts at 35 %)
|
contrailFadeKm: 100, // fully faded at this distance (ramp starts at 35 %)
|
||||||
|
// Night contrast: a physically scaled contrail is a faint grey thread
|
||||||
|
// that disappears at 4x pixel scale, so this is the night share of its
|
||||||
|
// opacity (0.34 was the old value) and it also drives the gain on the
|
||||||
|
// moon/star light. 0 = night contrails off, 1 = strongest.
|
||||||
|
contrailNight: 0.85,
|
||||||
// Night strobes. Point size shrinks with distance but never below
|
// Night strobes. Point size shrinks with distance but never below
|
||||||
// lightPx and never above lightMaxPx for a jet directly overhead.
|
// lightPx and never above lightMaxPx for a jet directly overhead.
|
||||||
// The white strobe is a double flash: two flashes blinkFlashS long,
|
// The white strobe is a double flash: two flashes blinkFlashS long,
|
||||||
@@ -301,11 +311,12 @@ window.CONFIG = {
|
|||||||
// east, culminates south, sets west) and the clock plus the sun/moon
|
// east, culminates south, sets west) and the clock plus the sun/moon
|
||||||
// tables follow the simulated date. false uses the real SunCalc positions.
|
// tables follow the simulated date. false uses the real SunCalc positions.
|
||||||
day: {
|
day: {
|
||||||
simulation: true,
|
simulation: false, // false = the real sun and moon (default)
|
||||||
cycleSeconds: 60, // 60 s = one simulated day per minute
|
cycleSeconds: 60, // only used when simulation is true:
|
||||||
|
// 60 s = one simulated day per minute
|
||||||
fixedTime: null // 'HH:MM' or 0-24 freezes the sim clock
|
fixedTime: null // 'HH:MM' or 0-24 freezes the sim clock
|
||||||
// (clouds, traffic and flights keep moving)
|
// (clouds, traffic and flights keep moving)
|
||||||
// day: { simulation: false } // real sun and moon
|
// day: { simulation: true, cycleSeconds: 60 } // accelerated day
|
||||||
},
|
},
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|||||||
Vendored
+82
-32
@@ -10,6 +10,13 @@
|
|||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
// Progress and build diagnostics are only interesting while the debug panel
|
||||||
|
// is on; a wall display must not fill the console with them.
|
||||||
|
var DEBUG_LOG = !!(window.CONFIG && window.CONFIG.debug);
|
||||||
|
function dbgLog() {
|
||||||
|
if (DEBUG_LOG) console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
function makeShader(gl, type, src) {
|
function makeShader(gl, type, src) {
|
||||||
var s = gl.createShader(type);
|
var s = gl.createShader(type);
|
||||||
gl.shaderSource(s, src);
|
gl.shaderSource(s, src);
|
||||||
@@ -147,8 +154,13 @@ var FRAG_SRC = [
|
|||||||
// the fine structure that cannot be resolved disappears.
|
// the fine structure that cannot be resolved disappears.
|
||||||
'float cloudDensity(vec3 p, float baseThresh, float soft) {',
|
'float cloudDensity(vec3 p, float baseThresh, float soft) {',
|
||||||
'float n = fbm3(p * 0.35 + vec3(0.0, 0.0, uTime * 0.06));',
|
'float n = fbm3(p * 0.35 + vec3(0.0, 0.0, uTime * 0.06));',
|
||||||
'float d = fbm3(p * 1.4 + vec3(uTime * 0.04, 7.3, uTime * 0.03));',
|
// The detail octave weighs at most 0.20 and fades to zero with
|
||||||
'n = n + d * mix(0.20, 0.0, soft);',
|
// distance; past soft = 0.95 it contributes under 1 %, so the whole
|
||||||
|
// 4-octave lookup is skipped for the (numerous) far samples.
|
||||||
|
'if (soft < 0.95) {',
|
||||||
|
'float d = fbm3(p * 1.4 + vec3(uTime * 0.04, 7.3, uTime * 0.03));',
|
||||||
|
'n = n + d * mix(0.20, 0.0, soft);',
|
||||||
|
'}',
|
||||||
// Cloud layer between uCloudBot and uCloudTop, densest in the
|
// Cloud layer between uCloudBot and uCloudTop, densest in the
|
||||||
// middle (bell curve).
|
// middle (bell curve).
|
||||||
'float h = clamp((p.y - uCloudBot) / (uCloudTop - uCloudBot), 0.0, 1.0);',
|
'float h = clamp((p.y - uCloudBot) / (uCloudTop - uCloudBot), 0.0, 1.0);',
|
||||||
@@ -343,7 +355,10 @@ var FRAG_SRC = [
|
|||||||
'vec3 albC = uCloudAlbedo * mix(0.78, 1.0, lit);',
|
'vec3 albC = uCloudAlbedo * mix(0.78, 1.0, lit);',
|
||||||
// Self-shading: probe the density towards the sun, so
|
// Self-shading: probe the density towards the sun, so
|
||||||
// thick cloud interior stays dark and thin edges light up.
|
// thick cloud interior stays dark and thin edges light up.
|
||||||
'float thick = cloudDensity(p + uSunDir * 2.2, baseThresh, cSoft);',
|
// d > 0.85 already clamps selfLit to its floor, so the
|
||||||
|
// second density lookup (a whole fbm) is skipped there.
|
||||||
|
'float thick = (d > 0.85) ? 1.0 :',
|
||||||
|
'cloudDensity(p + uSunDir * 2.2, baseThresh, cSoft);',
|
||||||
'float selfLit = clamp(1.0 - thick * 1.3, 0.12, 1.0);',
|
'float selfLit = clamp(1.0 - thick * 1.3, 0.12, 1.0);',
|
||||||
// Forward scattering - clouds brighten towards the sun
|
// Forward scattering - clouds brighten towards the sun
|
||||||
// (silver lining) instead of a flat day/night ramp.
|
// (silver lining) instead of a flat day/night ramp.
|
||||||
@@ -484,8 +499,13 @@ var CLOUD_SAMPLER_GLSL = [
|
|||||||
'}',
|
'}',
|
||||||
'float cloudDensity(vec3 p, float baseThresh, float soft) {',
|
'float cloudDensity(vec3 p, float baseThresh, float soft) {',
|
||||||
'float n = fbm3(p * 0.35 + vec3(0.0, 0.0, uTime * 0.06));',
|
'float n = fbm3(p * 0.35 + vec3(0.0, 0.0, uTime * 0.06));',
|
||||||
'float d = fbm3(p * 1.4 + vec3(uTime * 0.04, 7.3, uTime * 0.03));',
|
// The detail octave weighs at most 0.20 and fades to zero with
|
||||||
'n = n + d * mix(0.20, 0.0, soft);',
|
// distance; past soft = 0.95 it contributes under 1 %, so the whole
|
||||||
|
// 4-octave lookup is skipped for the (numerous) far samples.
|
||||||
|
'if (soft < 0.95) {',
|
||||||
|
'float d = fbm3(p * 1.4 + vec3(uTime * 0.04, 7.3, uTime * 0.03));',
|
||||||
|
'n = n + d * mix(0.20, 0.0, soft);',
|
||||||
|
'}',
|
||||||
'float h = clamp((p.y - uCloudBot) / (uCloudTop - uCloudBot), 0.0, 1.0);',
|
'float h = clamp((p.y - uCloudBot) / (uCloudTop - uCloudBot), 0.0, 1.0);',
|
||||||
'float profile = 4.0 * h * (1.0 - h);',
|
'float profile = 4.0 * h * (1.0 - h);',
|
||||||
'profile = max(profile, 0.0);',
|
'profile = max(profile, 0.0);',
|
||||||
@@ -627,8 +647,12 @@ var TERRAIN_FRAG_SRC = [
|
|||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
function init(canvas) {
|
function init(canvas) {
|
||||||
var gl = canvas.getContext('webgl2', { antialias: false, alpha: false })
|
// CONFIG.antialias (default on) asks for MSAA on the default framebuffer.
|
||||||
|| canvas.getContext('webgl', { antialias: false, alpha: false });
|
// It costs fill rate, and at renderScale < 1 the pixel blocks dominate
|
||||||
|
// anyway - set it to false if the scene is GPU bound.
|
||||||
|
var AA_ON = !(window.CONFIG && window.CONFIG.antialias === false);
|
||||||
|
var glOpts = { antialias: AA_ON, alpha: false, powerPreference: 'high-performance' };
|
||||||
|
var gl = canvas.getContext('webgl2', glOpts) || canvas.getContext('webgl', glOpts);
|
||||||
if (!gl) {
|
if (!gl) {
|
||||||
console.error('clouds3d: WebGL not available');
|
console.error('clouds3d: WebGL not available');
|
||||||
return null;
|
return null;
|
||||||
@@ -1195,7 +1219,7 @@ function tileProbeProxy() {
|
|||||||
var type = (r.headers.get('Content-Type') || '').toLowerCase();
|
var type = (r.headers.get('Content-Type') || '').toLowerCase();
|
||||||
if (r.ok && type.indexOf('image/') === 0) {
|
if (r.ok && type.indexOf('image/') === 0) {
|
||||||
apply();
|
apply();
|
||||||
console.log('[clouds3d] tiles: using dev proxy ' + path);
|
dbgLog('[clouds3d] tiles: using dev proxy ' + path);
|
||||||
}
|
}
|
||||||
}, function () {});
|
}, function () {});
|
||||||
}
|
}
|
||||||
@@ -2151,7 +2175,7 @@ function opProbeProxy() {
|
|||||||
}).then(function (r) {
|
}).then(function (r) {
|
||||||
if (r.status === 200) {
|
if (r.status === 200) {
|
||||||
CAR_ENDPOINTS = OP_PUBLIC.concat(OP_PROXY);
|
CAR_ENDPOINTS = OP_PUBLIC.concat(OP_PROXY);
|
||||||
console.log('[clouds3d] overpass: dev proxy available');
|
dbgLog('[clouds3d] overpass: dev proxy available');
|
||||||
}
|
}
|
||||||
}, function () {});
|
}, function () {});
|
||||||
}
|
}
|
||||||
@@ -2433,11 +2457,11 @@ function fetchMotorways() {
|
|||||||
var fresh = 0; // cells that needed a real fetch this cycle
|
var fresh = 0; // cells that needed a real fetch this cycle
|
||||||
var fpParts = []; // "id:savedAt" of every raw cell -> fp()
|
var fpParts = []; // "id:savedAt" of every raw cell -> fp()
|
||||||
var doneCells = 0, totalCells = CAR_QUERIES.length;
|
var doneCells = 0, totalCells = CAR_QUERIES.length;
|
||||||
console.log('[clouds3d] road cells: starting 0/' + totalCells);
|
dbgLog('[clouds3d] road cells: starting 0/' + totalCells);
|
||||||
function progCell(cached) {
|
function progCell(cached) {
|
||||||
doneCells++;
|
doneCells++;
|
||||||
if (doneCells % 10 === 0 || doneCells === totalCells) {
|
if (doneCells % 10 === 0 || doneCells === totalCells) {
|
||||||
console.log('[clouds3d] road cells: ' + doneCells + '/' + totalCells +
|
dbgLog('[clouds3d] road cells: ' + doneCells + '/' + totalCells +
|
||||||
(cached ? ' (cached)' : ' (fresh ' + fresh + ')'));
|
(cached ? ' (cached)' : ' (fresh ' + fresh + ')'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2535,14 +2559,14 @@ function fetchMotorways() {
|
|||||||
// be replayed forever and never grow.
|
// be replayed forever and never grow.
|
||||||
if (newest > rec.savedAt) return false;
|
if (newest > rec.savedAt) return false;
|
||||||
if (missing > Math.max(1, total * 0.1)) {
|
if (missing > Math.max(1, total * 0.1)) {
|
||||||
console.log('[clouds3d] car layer: ' + missing + '/' + total +
|
dbgLog('[clouds3d] car layer: ' + missing + '/' + total +
|
||||||
' cells missing - rebuilding instead of replaying the cache');
|
' cells missing - rebuilding instead of replaying the cache');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
carGeo = { motorway: { cnts: Int32Array.from(rec.mCnts), pts: b64ToF32(rec.mData) },
|
carGeo = { motorway: { cnts: Int32Array.from(rec.mCnts), pts: b64ToF32(rec.mData) },
|
||||||
secondary: { cnts: Int32Array.from(rec.bCnts), pts: b64ToF32(rec.bData) } };
|
secondary: { cnts: Int32Array.from(rec.bCnts), pts: b64ToF32(rec.bData) } };
|
||||||
kickCarBuild(false);
|
kickCarBuild(false);
|
||||||
console.log('[clouds3d] car layer: ' + carGeo.motorway.cnts.length +
|
dbgLog('[clouds3d] car layer: ' + carGeo.motorway.cnts.length +
|
||||||
' motorway + ' + carGeo.secondary.cnts.length + ' chains (from cache' +
|
' motorway + ' + carGeo.secondary.cnts.length + ' chains (from cache' +
|
||||||
(missing ? ', ' + missing + ' cells unavailable' : '') + ')');
|
(missing ? ', ' + missing + ' cells unavailable' : '') + ')');
|
||||||
return true;
|
return true;
|
||||||
@@ -2558,7 +2582,7 @@ function fetchMotorways() {
|
|||||||
carPersistFp = geoFp(fpParts);
|
carPersistFp = geoFp(fpParts);
|
||||||
carCycleFp = carPersistFp;
|
carCycleFp = carPersistFp;
|
||||||
kickCarBuild(true);
|
kickCarBuild(true);
|
||||||
console.log('[clouds3d] car layer: ' + carRaw.motorway.length +
|
dbgLog('[clouds3d] car layer: ' + carRaw.motorway.length +
|
||||||
' motorway + ' + carRaw.secondary.length +
|
' motorway + ' + carRaw.secondary.length +
|
||||||
' secondary polylines queued (' + fresh + ' cells re-fetched)');
|
' secondary polylines queued (' + fresh + ' cells re-fetched)');
|
||||||
});
|
});
|
||||||
@@ -2594,7 +2618,7 @@ function fetchSettlements() {
|
|||||||
function progCell(cached) {
|
function progCell(cached) {
|
||||||
doneCells++;
|
doneCells++;
|
||||||
if (doneCells % 5 === 0 || doneCells === totalCells) {
|
if (doneCells % 5 === 0 || doneCells === totalCells) {
|
||||||
console.log('[clouds3d] place cells: ' + doneCells + '/' + totalCells +
|
dbgLog('[clouds3d] place cells: ' + doneCells + '/' + totalCells +
|
||||||
(cached ? ' (cached)' : ' (fresh ' + fresh + ')'));
|
(cached ? ' (cached)' : ' (fresh ' + fresh + ')'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2623,7 +2647,7 @@ function fetchSettlements() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalCells = cells.length;
|
totalCells = cells.length;
|
||||||
console.log('[clouds3d] place cells: starting 0/' + totalCells);
|
dbgLog('[clouds3d] place cells: starting 0/' + totalCells);
|
||||||
var list = [];
|
var list = [];
|
||||||
var seenB = {}; // bucket -> index in list: dedupe node/way/rel of one place
|
var seenB = {}; // bucket -> index in list: dedupe node/way/rel of one place
|
||||||
function parse(data) {
|
function parse(data) {
|
||||||
@@ -2688,7 +2712,7 @@ function fetchSettlements() {
|
|||||||
var before = list.length;
|
var before = list.length;
|
||||||
parse(obj);
|
parse(obj);
|
||||||
if (list.length > before)
|
if (list.length > before)
|
||||||
console.log('[clouds3d] place cell ' + entry.id + ': +' +
|
dbgLog('[clouds3d] place cell ' + entry.id + ': +' +
|
||||||
(list.length - before) + ' places (total ' + list.length + ')');
|
(list.length - before) + ' places (total ' + list.length + ')');
|
||||||
if (!entry._cached) fresh++;
|
if (!entry._cached) fresh++;
|
||||||
// dot geometry is generated in the geo worker; the debounce
|
// dot geometry is generated in the geo worker; the debounce
|
||||||
@@ -2715,7 +2739,7 @@ function fetchSettlements() {
|
|||||||
cityGeo = b64ToF32(rec.data);
|
cityGeo = b64ToF32(rec.data);
|
||||||
kickCityBuild();
|
kickCityBuild();
|
||||||
if (carGeo) kickCarBuild(false);
|
if (carGeo) kickCarBuild(false);
|
||||||
console.log('[clouds3d] settlements: ' + rec.cnt + ' places (from cache)');
|
dbgLog('[clouds3d] settlements: ' + rec.cnt + ' places (from cache)');
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2731,7 +2755,7 @@ function fetchSettlements() {
|
|||||||
tileDbPut(db, { id: 'geo/placecache_v1', fp: geoFp(fpParts),
|
tileDbPut(db, { id: 'geo/placecache_v1', fp: geoFp(fpParts),
|
||||||
cnt: list.length, data: f32ToB64(packPlaces(list)), savedAt: Date.now() });
|
cnt: list.length, data: f32ToB64(packPlaces(list)), savedAt: Date.now() });
|
||||||
});
|
});
|
||||||
console.log('[clouds3d] settlements: ' + list.length + ' places queued for build' +
|
dbgLog('[clouds3d] settlements: ' + list.length + ' places queued for build' +
|
||||||
(terrainSample ? '' : ' (terrain pending)'));
|
(terrainSample ? '' : ' (terrain pending)'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -3511,7 +3535,7 @@ function geoWorkerGet(lane) {
|
|||||||
w.onmessage = function (e) {
|
w.onmessage = function (e) {
|
||||||
var res = e.data || {};
|
var res = e.data || {};
|
||||||
if (res.progress) {
|
if (res.progress) {
|
||||||
console.log('[clouds3d] ' + lane + ' worker ' + res.kind + ': ' +
|
dbgLog('[clouds3d] ' + lane + ' worker ' + res.kind + ': ' +
|
||||||
res.done + '/' + res.total);
|
res.done + '/' + res.total);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3698,13 +3722,13 @@ function runCarBuild(done) {
|
|||||||
if (res.joins) carJoinCount = res.joins;
|
if (res.joins) carJoinCount = res.joins;
|
||||||
if (res.maxChainKm) carMaxChainKm = res.maxChainKm;
|
if (res.maxChainKm) carMaxChainKm = res.maxChainKm;
|
||||||
applyCarBuild(gl, res);
|
applyCarBuild(gl, res);
|
||||||
console.log('[clouds3d] car build: ' + carCountM + ' + ' + carCountB +
|
dbgLog('[clouds3d] car build: ' + carCountM + ' + ' + carCountB +
|
||||||
' ribbon verts, ' + carGeo.motorway.cnts.length + '+' +
|
' ribbon verts, ' + carGeo.motorway.cnts.length + '+' +
|
||||||
carGeo.secondary.cnts.length + ' chains, ' +
|
carGeo.secondary.cnts.length + ' chains, ' +
|
||||||
carPtsCountM + ' motorway + ' + carPtsCountB + ' side lights, longest ~' +
|
carPtsCountM + ' motorway + ' + carPtsCountB + ' side lights, longest ~' +
|
||||||
Math.round(carMaxChainKm) + ' km, ' +
|
Math.round(carMaxChainKm) + ' km, ' +
|
||||||
Math.round(performance.now() - t0) + ' ms off-thread');
|
Math.round(performance.now() - t0) + ' ms off-thread');
|
||||||
console.log('[clouds3d] car slots: motorway ' + (carPtsCountM / 2) +
|
dbgLog('[clouds3d] car slots: motorway ' + (carPtsCountM / 2) +
|
||||||
' slots on ' + Math.round(res.mLen * KM_PER_TILE) + ' km (want ' +
|
' slots on ' + Math.round(res.mLen * KM_PER_TILE) + ' km (want ' +
|
||||||
Math.round(res.mLen / CAR_M_DASH) + ', dash ' +
|
Math.round(res.mLen / CAR_M_DASH) + ', dash ' +
|
||||||
Math.round(CAR_M_DASH * KM_PER_TILE * 1000) + ' m) | side ' + (carPtsCountB / 2) +
|
Math.round(CAR_M_DASH * KM_PER_TILE * 1000) + ' m) | side ' + (carPtsCountB / 2) +
|
||||||
@@ -3815,20 +3839,20 @@ function runCityBuild(done) {
|
|||||||
var gl = elevGl || (clouds3dState && clouds3dState.gl);
|
var gl = elevGl || (clouds3dState && clouds3dState.gl);
|
||||||
if (!gl || !terrainSample || !cityGeo) {
|
if (!gl || !terrainSample || !cityGeo) {
|
||||||
// silent skip made the layer look dead: say what is still missing
|
// silent skip made the layer look dead: say what is still missing
|
||||||
console.log('[clouds3d] city build skipped - ' +
|
dbgLog('[clouds3d] city build skipped - ' +
|
||||||
(!gl ? 'no gl context' : '') +
|
(!gl ? 'no gl context' : '') +
|
||||||
(!gl || terrainSample ? '' : ', no elevation sample yet') +
|
(!gl || terrainSample ? '' : ', no elevation sample yet') +
|
||||||
(cityGeo ? '' : ', no place nodes fetched yet'));
|
(cityGeo ? '' : ', no place nodes fetched yet'));
|
||||||
done(); return;
|
done(); return;
|
||||||
}
|
}
|
||||||
console.log('[clouds3d] city build: ' + (cityGeo.length / 4) + ' places queued');
|
dbgLog('[clouds3d] city build: ' + (cityGeo.length / 4) + ' places queued');
|
||||||
var t0 = performance.now();
|
var t0 = performance.now();
|
||||||
geoRun({ op: 'city', places: cityGeo, terrain: terrainMsg(), cfg: cityCfg() },
|
geoRun({ op: 'city', places: cityGeo, terrain: terrainMsg(), cfg: cityCfg() },
|
||||||
function (res) {
|
function (res) {
|
||||||
if (!res || res.error) { done(); return; }
|
if (!res || res.error) { done(); return; }
|
||||||
cityBuildMs = Math.round(performance.now() - t0);
|
cityBuildMs = Math.round(performance.now() - t0);
|
||||||
applyCityBuild(gl, res);
|
applyCityBuild(gl, res);
|
||||||
console.log('[clouds3d] settlements: ' + cityCount + ' lights from ' +
|
dbgLog('[clouds3d] settlements: ' + cityCount + ' lights from ' +
|
||||||
(cityGeo.length / 4) + ' places, ' + cityBuildMs + ' ms off-thread');
|
(cityGeo.length / 4) + ' places, ' + cityBuildMs + ' ms off-thread');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -4021,6 +4045,11 @@ 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_HALF_W = Math.max(0.002, fltNum(FLT_CFG.contrailWidthM, 310) / 2000 / KM_PER_TILE);
|
||||||
// world units: trails are gone by contrailFadeKm, ramping from 35% of it
|
// world units: trails are gone by contrailFadeKm, ramping from 35% of it
|
||||||
var CT_FADE_END = Math.max(1, fltNum(FLT_CFG.contrailFadeKm, 100) / KM_PER_TILE);
|
var CT_FADE_END = Math.max(1, fltNum(FLT_CFG.contrailFadeKm, 100) / KM_PER_TILE);
|
||||||
|
// Night readability. A physically scaled contrail on a starlit night is a
|
||||||
|
// faint grey thread that the pixel grid removes entirely, so the night share
|
||||||
|
// of its opacity and a gain on the moon/star light are configurable.
|
||||||
|
var CT_NIGHT = Math.max(0, Math.min(1, fltNum(FLT_CFG.contrailNight, 0.85)));
|
||||||
|
var CT_NIGHT_BOOST = 1.0 + CT_NIGHT * 2.2;
|
||||||
var CT_FADE_START = CT_FADE_END * 0.35;
|
var CT_FADE_START = CT_FADE_END * 0.35;
|
||||||
var CT_MAX = FLT_MAX;
|
var CT_MAX = FLT_MAX;
|
||||||
var CT_SEG = 8; // curve resolution per trail
|
var CT_SEG = 8; // curve resolution per trail
|
||||||
@@ -4218,7 +4247,7 @@ function fetchAirports() {
|
|||||||
});
|
});
|
||||||
if (!airports.length) return;
|
if (!airports.length) return;
|
||||||
buildFlights(airports);
|
buildFlights(airports);
|
||||||
console.log('[clouds3d] flights: ' + airports.length + ' airfields -> ' +
|
dbgLog('[clouds3d] flights: ' + airports.length + ' airfields -> ' +
|
||||||
fltData.length + ' aircraft (destinations rolled per take-off)');
|
fltData.length + ' aircraft (destinations rolled per take-off)');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -4388,6 +4417,7 @@ var CT_FRAG_SRC = [
|
|||||||
'uniform float uMoonInt;',
|
'uniform float uMoonInt;',
|
||||||
'uniform float uSkyAmb;',
|
'uniform float uSkyAmb;',
|
||||||
'uniform float uStarAmb;',
|
'uniform float uStarAmb;',
|
||||||
|
'uniform float uNightBoost;', // extra gain on the moon/star terms
|
||||||
'uniform vec3 uSunDir;',
|
'uniform vec3 uSunDir;',
|
||||||
'uniform vec3 uCamPos;',
|
'uniform vec3 uCamPos;',
|
||||||
'varying float vU;',
|
'varying float vU;',
|
||||||
@@ -4426,8 +4456,9 @@ var CT_FRAG_SRC = [
|
|||||||
// Same multipliers as the cloud deck, so the contrail/cloud
|
// Same multipliers as the cloud deck, so the contrail/cloud
|
||||||
// brightness ratio at night is the same as in daylight.
|
// brightness ratio at night is the same as in daylight.
|
||||||
'vec3 light = uSunColor * uSunInt * phase * 1.25',
|
'vec3 light = uSunColor * uSunInt * phase * 1.25',
|
||||||
' + uMoonColor * uMoonInt * 3.0',
|
' + (uMoonColor * uMoonInt * 3.0',
|
||||||
' + max(vec3(uSkyAmb), uStarAmb * vec3(0.75, 0.85, 1.0)) * 1.7;',
|
' + max(vec3(uSkyAmb), uStarAmb * vec3(0.75, 0.85, 1.0)) * 1.7)',
|
||||||
|
' * uNightBoost;',
|
||||||
'gl_FragColor = vec4(vec3(0.95, 0.96, 1.0) * light, a);',
|
'gl_FragColor = vec4(vec3(0.95, 0.96, 1.0) * light, a);',
|
||||||
'}'
|
'}'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -4616,7 +4647,7 @@ function buildSkyTexture(stars) {
|
|||||||
var b = 0.7 + 0.3 * Math.min(1, Math.pow(2.512, -mag * 0.7) * 1.3);
|
var b = 0.7 + 0.3 * Math.min(1, Math.pow(2.512, -mag * 0.7) * 1.3);
|
||||||
data[di++] = b;
|
data[di++] = b;
|
||||||
}
|
}
|
||||||
console.log('[clouds3d] stars: ' + stars.length + ' points, mean rgb ' +
|
dbgLog('[clouds3d] stars: ' + stars.length + ' points, mean rgb ' +
|
||||||
(mr / stars.length).toFixed(3) + '/' + (mg / stars.length).toFixed(3) + '/' +
|
(mr / stars.length).toFixed(3) + '/' + (mg / stars.length).toFixed(3) + '/' +
|
||||||
(mb / stars.length).toFixed(3));
|
(mb / stars.length).toFixed(3));
|
||||||
var prog = makeProgram(gl, SKYPTS_VERT, SKYPTS_FRAG);
|
var prog = makeProgram(gl, SKYPTS_VERT, SKYPTS_FRAG);
|
||||||
@@ -5513,6 +5544,7 @@ window.clouds3dRender = function(target) {
|
|||||||
// CT_HALF_W doubled with the trail width, so the factor
|
// CT_HALF_W doubled with the trail width, so the factor
|
||||||
// halves: the aircraft silhouette keeps its old size.
|
// halves: the aircraft silhouette keeps its old size.
|
||||||
var S0 = CT_HALF_W * 1.0;
|
var S0 = CT_HALF_W * 1.0;
|
||||||
|
var _meshDrawn = 0;
|
||||||
for (var dj = 0; dj < fltCount; dj++) {
|
for (var dj = 0; dj < fltCount; dj++) {
|
||||||
var briJ = fltBuf[dj * 6 + 3];
|
var briJ = fltBuf[dj * 6 + 3];
|
||||||
if (briJ <= 0) continue;
|
if (briJ <= 0) continue;
|
||||||
@@ -5535,6 +5567,7 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.uniform4f(planeU.orit, Math.cos(yaw), Math.sin(yaw), pitch, 0);
|
gl.uniform4f(planeU.orit, Math.cos(yaw), Math.sin(yaw), pitch, 0);
|
||||||
gl.uniform1f(planeU.s, scl);
|
gl.uniform1f(planeU.s, scl);
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, planeN);
|
gl.drawArrays(gl.TRIANGLES, 0, planeN);
|
||||||
|
_meshDrawn++;
|
||||||
}
|
}
|
||||||
gl.depthMask(true);
|
gl.depthMask(true);
|
||||||
}
|
}
|
||||||
@@ -5545,7 +5578,7 @@ window.clouds3dRender = function(target) {
|
|||||||
// and the stripe follows the curved leg: walked backwards from
|
// and the stripe follows the curved leg: walked backwards from
|
||||||
// the aircraft in fixed steps. Visible by day (sun-lit) AND by
|
// the aircraft in fixed steps. Visible by day (sun-lit) AND by
|
||||||
// night as dimmed moonlit streaks.
|
// night as dimmed moonlit streaks.
|
||||||
var tvis = Math.max(dayf, carNight * 0.42);
|
var tvis = Math.max(dayf, carNight * CT_NIGHT);
|
||||||
if (tvis > 0.02) {
|
if (tvis > 0.02) {
|
||||||
if (!ctBuf) ctBuf = new Float32Array(CT_MAX * CT_SEG * 6 * 7);
|
if (!ctBuf) ctBuf = new Float32Array(CT_MAX * CT_SEG * 6 * 7);
|
||||||
ctVerts = 0;
|
ctVerts = 0;
|
||||||
@@ -5574,7 +5607,7 @@ window.clouds3dRender = function(target) {
|
|||||||
if (dfc > 1) dfc = 1; else if (dfc <= 0.02) continue;
|
if (dfc > 1) dfc = 1; else if (dfc <= 0.02) continue;
|
||||||
// sun-lit by day, moonlit after dusk. dayf alone made the
|
// sun-lit by day, moonlit after dusk. dayf alone made the
|
||||||
// stripes vanish completely at night although the pass ran.
|
// stripes vanish completely at night although the pass ran.
|
||||||
var lit = Math.max(dayf, carNight * 0.34);
|
var lit = Math.max(dayf, carNight * CT_NIGHT);
|
||||||
var strg = gt * lit * dfc;
|
var strg = gt * lit * dfc;
|
||||||
// widen with distance: a fixed 310 m ribbon is sub-pixel at
|
// widen with distance: a fixed 310 m ribbon is sub-pixel at
|
||||||
// 150 km and blends away; grow it to hold ~2-4 screen px
|
// 150 km and blends away; grow it to hold ~2-4 screen px
|
||||||
@@ -5622,7 +5655,8 @@ window.clouds3dRender = function(target) {
|
|||||||
moonColor: gl.getUniformLocation(cpm, 'uMoonColor'),
|
moonColor: gl.getUniformLocation(cpm, 'uMoonColor'),
|
||||||
moonInt: gl.getUniformLocation(cpm, 'uMoonInt'),
|
moonInt: gl.getUniformLocation(cpm, 'uMoonInt'),
|
||||||
skyAmb: gl.getUniformLocation(cpm, 'uSkyAmb'),
|
skyAmb: gl.getUniformLocation(cpm, 'uSkyAmb'),
|
||||||
starAmb: gl.getUniformLocation(cpm, 'uStarAmb') };
|
starAmb: gl.getUniformLocation(cpm, 'uStarAmb'),
|
||||||
|
nightBoost: gl.getUniformLocation(cpm, 'uNightBoost') };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ctProg) {
|
if (ctProg) {
|
||||||
@@ -5637,6 +5671,7 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.uniform1f(ctU.moonInt, _moonI);
|
gl.uniform1f(ctU.moonInt, _moonI);
|
||||||
gl.uniform1f(ctU.skyAmb, _amb * ATMO_SKY_GAIN);
|
gl.uniform1f(ctU.skyAmb, _amb * ATMO_SKY_GAIN);
|
||||||
gl.uniform1f(ctU.starAmb, _star);
|
gl.uniform1f(ctU.starAmb, _star);
|
||||||
|
gl.uniform1f(ctU.nightBoost, 1.0 + carNight * (CT_NIGHT_BOOST - 1.0));
|
||||||
gl.enableVertexAttribArray(ctA.pos);
|
gl.enableVertexAttribArray(ctA.pos);
|
||||||
gl.vertexAttribPointer(ctA.pos, 3, gl.FLOAT, false, 28, 0);
|
gl.vertexAttribPointer(ctA.pos, 3, gl.FLOAT, false, 28, 0);
|
||||||
gl.enableVertexAttribArray(ctA.u);
|
gl.enableVertexAttribArray(ctA.u);
|
||||||
@@ -5655,6 +5690,21 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.depthMask(true);
|
gl.depthMask(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Diagnostic snapshot: how many aircraft are in range, how many
|
||||||
|
// meshes were actually drawn and how much contrail geometry made
|
||||||
|
// it past the distance gates.
|
||||||
|
window.__flightDbg = {
|
||||||
|
aircraft: fltCount,
|
||||||
|
meshDrawn: _meshDrawn,
|
||||||
|
ctVerts: ctVerts,
|
||||||
|
day: +dayf.toFixed(2),
|
||||||
|
night: +carNight.toFixed(2),
|
||||||
|
meshRangeKm: Math.round(16 * KM_PER_TILE)
|
||||||
|
};
|
||||||
|
if (DEBUG_LOG && now - _flightDbgT > 5000) {
|
||||||
|
_flightDbgT = now;
|
||||||
|
dbgLog('[clouds3d] flights:', JSON.stringify(window.__flightDbg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user