From 6f19b3490038871311268bfcb1d78388697270f7 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 13 Oct 2026 08:42:37 +0200 Subject: [PATCH] flights: draw two narrow engine contrails and raise beacon --- config.js | 7 +++- vendor/clouds3d.js | 100 +++++++++++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/config.js b/config.js index fb484e7..9cad75f 100644 --- a/config.js +++ b/config.js @@ -397,9 +397,14 @@ window.CONFIG = { contrailChance: 1.00, // 0 = no contrails, 1 = every eligible jet contrailStartKm: 4, // start after this distance from the airport contrailLifeS: 300, // seconds before a trail disappears - contrailWidthM: 1400, // full trail width + contrailWidthM: 1400, // aircraft/trail scale width + contrailEngineWidthM: 700, // width of each engine contrail + contrailEngineOffset: { x: 0.36, y: -0.055, z: -0.14 }, // engine exhaust in model units + markingLightOffset: { x: 0.0, y: 0.235, z: 0.0 }, // top-centre fuselage beacon contrailFadeKm: 300, // fully faded at this distance (ramp starts at 35 %) // contrailChance: 0.35, contrailLifeS: 120 // short sparse trails + // contrailEngineWidthM: 520, contrailEngineOffset: { x: 0.36, y: -0.055, z: -0.18 } + // markingLightOffset: { x: 0.0, y: 0.26, z: 0.05 } // 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 diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 88355ff..54d5a87 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -5702,6 +5702,9 @@ 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_ENGINE_HALF_W = Math.max(0.001, fltNum(FLT_CFG.contrailEngineWidthM, 700) / 2000 / KM_PER_TILE); +var CT_ENGINE_OFFSET = FLT_CFG.contrailEngineOffset || { x: 0.36, y: -0.055, z: -0.14 }; +var FLT_MARK_OFFSET = FLT_CFG.markingLightOffset || { x: 0.0, y: 0.235, z: 0.0 }; // 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); // Night readability. A physically scaled contrail on a starlit night is a @@ -5724,7 +5727,7 @@ var FLT_BLINK_GAP = Math.max(0.02, fltNum(FLT_CFG.blinkGapS, 0.12)); var FLT_BLINK_W = Math.max(0.01, fltNum(FLT_CFG.blinkFlashS, 0.07)); 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 fltBuf = null, fltLightBuf = null, fltVbo = null, fltCount = 0; var fltState = []; // per frame render poses var fltProg = null, fltAttribs = null, fltUniforms = null; var fltFetching = false; @@ -5820,6 +5823,7 @@ function buildFlights(airports) { } fltCount = fltData.length; fltBuf = new Float32Array(fltCount * 6); // pos3 + bri + phase + size + fltLightBuf = new Float32Array(fltCount * 6); // beacon positions, offset from model origin } // advance every aircraft with the frame clock; the destination is rolled @@ -8014,6 +8018,19 @@ window.clouds3dRender = function(target) { fltBuf[k + 3] = f.big; fltBuf[k + 4] = f.phase; fltBuf[k + 5] = f.big; // destination rank also drives light size + if (fltLightBuf) { + var markScale = CT_HALF_W; + var mx = (FLT_MARK_OFFSET.x || 0) * markScale; + var my = (FLT_MARK_OFFSET.y || 0) * markScale; + var mz = (FLT_MARK_OFFSET.z || 0) * markScale; + var mcy = fl.tz, msy = fl.tx; + fltLightBuf[k] = fl.x + mx * mcy + mz * msy; + fltLightBuf[k + 1] = yF + my; + fltLightBuf[k + 2] = fl.z - mx * msy + mz * mcy; + fltLightBuf[k + 3] = f.big; + fltLightBuf[k + 4] = f.phase; + fltLightBuf[k + 5] = f.big; + } 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; @@ -8022,7 +8039,7 @@ window.clouds3dRender = function(target) { if (carNight > 0.06) { if (!fltVbo) fltVbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, fltVbo); - gl.bufferData(gl.ARRAY_BUFFER, fltBuf, gl.STREAM_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, fltLightBuf || fltBuf, gl.STREAM_DRAW); if (!fltProg) { var fp = makeProgram(gl, FLT_VERT_SRC, FLT_FRAG_SRC); if (!fp) { FLT_ON = false; } @@ -8174,14 +8191,14 @@ window.clouds3dRender = function(target) { gl.depthMask(true); } } - // -- contrails: simple robust ribbon pass -- + // -- contrails: two narrow engine trails -- var tvis = Math.max(dayf, carNight * CT_NIGHT); if (tvis > 0.01) { - if (!ct2Buf) ct2Buf = new Float32Array(FLT_MAX * CT2_SEG * 6 * 6); + if (!ct2Buf) ct2Buf = new Float32Array(FLT_MAX * 2 * CT2_SEG * 6 * 6); ct2Verts = 0; var _cs2 = { x: 0, z: 0, tx: 1, tz: 0 }; + var _eo = { x: 0, y: 0, z: 0 }; for (var cj = 0; cj < fltCount; cj++) { - if (ct2Verts + CT2_SEG * 6 > ct2Buf.length / 6) break; var stc = fltState[cj]; if (!stc || stc.gnd) continue; if (fltRand(cj * 3.7 + 0.77) > CT_CHANCE) continue; @@ -8191,33 +8208,54 @@ window.clouds3dRender = function(target) { var tl = Math.min(Math.max(1.0, CT_T * fltData[cj].v), stc.s); if (tl < 0.2) continue; var lit = Math.max(dayf, carNight * CT_NIGHT); - var strg = Math.max(0.30, gt * lit); - var hy = stc.y; - var prev = null; - var cur = { x: 0, z: 0, px: 0, pz: 0, u: 0 }; - for (var sk = 0; sk <= CT2_SEG; sk++) { - var dd = stc.s - tl + tl * (sk / CT2_SEG); - if (dd < 0) dd = 0; - fltLegAt(stc.leg, dd, _cs2); - var dx = _cs2.x - camX, dz = _cs2.z - camZ, dy = hy - camY; - var dist = Math.sqrt(dx * dx + dy * dy + dz * dz); - var hw = CT_HALF_W + dist * 0.004; - var tx = _cs2.tx, tz = _cs2.tz; - var sl = Math.hypot(tx, tz) || 1; - tx /= sl; tz /= sl; - cur.x = _cs2.x; cur.z = _cs2.z; - cur.px = tz * hw; cur.pz = -tx * hw; - cur.u = sk / CT2_SEG; - if (prev) { - _ct2Vert(prev.x + prev.px, hy, prev.z + prev.pz, prev.u, 1, strg); - _ct2Vert(prev.x - prev.px, hy, prev.z - prev.pz, prev.u, -1, strg); - _ct2Vert(cur.x - cur.px, hy, cur.z - cur.pz, cur.u, -1, strg); - _ct2Vert(prev.x + prev.px, hy, prev.z + prev.pz, prev.u, 1, strg); - _ct2Vert(cur.x - cur.px, hy, cur.z - cur.pz, cur.u, -1, strg); - _ct2Vert(cur.x + cur.px, hy, cur.z + cur.pz, cur.u, 1, strg); + var baseStrg = Math.max(0.30, gt * lit); + var cln = Math.min(stc.leg.len * 0.4, FLT_CLIMB_U); + var pitch = 0; + if (stc.alt > 0.02) { + if (stc.s < cln) pitch = 0.16; + else if (stc.s > stc.leg.len - cln) pitch = -0.16; + } + var cp2 = Math.sqrt(Math.max(0.0, 1.0 - pitch * pitch)); + for (var side = -1; side <= 1; side += 2) { + if (ct2Verts + CT2_SEG * 6 > ct2Buf.length / 6) break; + var strg = baseStrg * (0.88 + 0.12 * fltRand(cj * 5.1 + side * 2.3)); + var prev = null; + var cur = { x: 0, z: 0, y: 0, px: 0, pz: 0, u: 0 }; + for (var sk = 0; sk <= CT2_SEG; sk++) { + var dd = stc.s - tl + tl * (sk / CT2_SEG); + if (dd < 0) dd = 0; + fltLegAt(stc.leg, dd, _cs2); + var tx = _cs2.tx, tz = _cs2.tz; + var sl = Math.hypot(tx, tz) || 1; + tx /= sl; tz /= sl; + var ex = (side * (CT_ENGINE_OFFSET.x || 0.36)) * CT_HALF_W; + var ey = (CT_ENGINE_OFFSET.y || -0.055) * CT_HALF_W; + var ez = (CT_ENGINE_OFFSET.z || -0.14) * CT_HALF_W; + var ey2 = ey * cp2 + ez * pitch; + var ez2 = -ey * pitch + ez * cp2; + _eo.x = ex * tz + ez2 * tx; + _eo.y = ey2; + _eo.z = -ex * tx + ez2 * tz; + var hx = _cs2.x + _eo.x; + var hy = stc.y + _eo.y; + var hz = _cs2.z + _eo.z; + var dx = hx - camX, dz = hz - camZ, dy = hy - camY; + var dist = Math.sqrt(dx * dx + dy * dy + dz * dz); + var hw = CT_ENGINE_HALF_W + dist * 0.002; + cur.x = hx; cur.z = hz; cur.y = hy; + cur.px = tz * hw; cur.pz = -tx * hw; + cur.u = sk / CT2_SEG; + if (prev) { + _ct2Vert(prev.x + prev.px, prev.y, prev.z + prev.pz, prev.u, 1, strg); + _ct2Vert(prev.x - prev.px, prev.y, prev.z - prev.pz, prev.u, -1, strg); + _ct2Vert(cur.x - cur.px, cur.y, cur.z - cur.pz, cur.u, -1, strg); + _ct2Vert(prev.x + prev.px, prev.y, prev.z + prev.pz, prev.u, 1, strg); + _ct2Vert(cur.x - cur.px, cur.y, cur.z - cur.pz, cur.u, -1, strg); + _ct2Vert(cur.x + cur.px, cur.y, cur.z + cur.pz, cur.u, 1, strg); + } + prev = cur; + cur = { x: 0, z: 0, y: 0, px: 0, pz: 0, u: 0 }; } - prev = cur; - cur = { x: 0, z: 0, px: 0, pz: 0, u: 0 }; } } if (ct2Verts > 0) {