diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 76a6f4f..e96835e 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -3736,6 +3736,8 @@ var CAR_VERT_SRC = [ 'uniform mat4 uProj;', 'uniform mat4 uView;', 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', 'uniform float uGrow;', // half width needed per unit distance (px floor)', 'uniform float uHalfW;', // ribbon half width baked into the geometry', 'varying float vLen;', @@ -3748,7 +3750,12 @@ var CAR_VERT_SRC = [ 'vLen = aMeta.x;', 'vSide = aMeta.y;', 'vDens = aDens;', - 'vec3 toCam = uCamPos - aPos;', + 'vec3 pos = aPos;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = pos.xz - uCamPos.xz;', + 'pos.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'vec3 toCam = uCamPos - pos;', 'vDist = length(toCam);', // Travel direction of a ribbon half is -tangent (see the flow sign // note in the fragment shader). The tangent is IDENTICAL on both @@ -3764,7 +3771,7 @@ var CAR_VERT_SRC = [ // A1 into the Attersee because a 3.6 px floor at 70 km is ~640 m. 'vHw = max(uHalfW, uGrow * vDist);', 'vec3 perp = vec3(-aMeta.w, 0.0, aMeta.z) * aMeta.y;', - 'vec3 wp = aPos + perp * (vHw - uHalfW);', + 'vec3 wp = pos + perp * (vHw - uHalfW);', 'gl_Position = uProj * uView * vec4(wp, 1.0);', '}' ].join('\n'); @@ -3813,6 +3820,8 @@ var CARP_VERT_SRC = [ 'uniform mat4 uProj;', 'uniform mat4 uView;', 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', 'uniform float uTime;', 'uniform float uSpeed;', // slots per second (frame flow)', 'uniform float uDash;', // vehicle spacing (world units)', @@ -3843,7 +3852,12 @@ var CARP_VERT_SRC = [ 'float span = min(uDash, aLen);', 'float off = fract(uTime * uSpeed + aMeta.w) * span + (aSeed - 0.5) * span * 0.12;', 'vec3 base = aPos - vec3(aMeta.x, 0.0, aMeta.y) * dir * off;', - 'vec3 toCam = uCamPos - base;', + 'vec3 pos = base;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = pos.xz - uCamPos.xz;', + 'pos.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'vec3 toCam = uCamPos - pos;', 'vDist = length(toCam);', 'vApproach = dot(vec3(aMeta.x, 0.0, aMeta.y), normalize(toCam));', // perp is the RIGHT of the digitisation tangent. This point travels', @@ -3858,7 +3872,7 @@ var CARP_VERT_SRC = [ 'vec3 perp = vec3(-aMeta.y, 0.0, aMeta.x) * (-dir * uTraffic);', 'float gap = max(uLaneW, uLanePx * uPxW * vDist);', 'gl_PointSize = clamp(uMinPx + uBoost / max(vDist, 0.35), uMinPx, uMaxPx);', - 'gl_Position = uProj * uView * vec4(base + perp * gap, 1.0);', + 'gl_Position = uProj * uView * vec4(pos + perp * gap, 1.0);', '}' ].join('\n'); @@ -4784,6 +4798,8 @@ function applyCarBuild(gl, res) { proj: gl.getUniformLocation(carProg, 'uProj'), view: gl.getUniformLocation(carProg, 'uView'), camPos: gl.getUniformLocation(carProg, 'uCamPos'), + earthRadius: gl.getUniformLocation(carProg, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(carProg, 'uCurvatureOn'), time: gl.getUniformLocation(carProg, 'uTime'), night: gl.getUniformLocation(carProg, 'uNight'), dens: gl.getUniformLocation(carProg, 'uDens'), @@ -4860,6 +4876,8 @@ function applyCityBuild(gl, res) { proj: gl.getUniformLocation(cityProg, 'uProj'), view: gl.getUniformLocation(cityProg, 'uView'), camPos: gl.getUniformLocation(cityProg, 'uCamPos'), + earthRadius: gl.getUniformLocation(cityProg, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(cityProg, 'uCurvatureOn'), scale: gl.getUniformLocation(cityProg, 'uScale'), night: gl.getUniformLocation(cityProg, 'uNight'), sunAlt: gl.getUniformLocation(cityProg, 'uSunAlt'), @@ -4880,17 +4898,24 @@ var CITY_VERT_SRC = [ 'uniform mat4 uProj;', 'uniform mat4 uView;', 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', 'uniform float uScale;', // px scale (viewport height / 600)', 'varying float vBri;', 'varying float vDist;', 'varying float vSeed;', 'void main() {', - 'vDist = length(uCamPos - aPos);', + 'vec3 pos = aPos;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = pos.xz - uCamPos.xz;', + 'pos.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'vDist = length(uCamPos - pos);', 'vBri = aBri;', // stable per-dot threshold seed: derived from the dot's own position 'vSeed = fract(sin(dot(aPos.xz, vec2(12.9898, 78.233))) * 43758.5453);', 'gl_PointSize = clamp(aSize * uScale * 750.0 / max(vDist, 0.1), 1.6, 18.0);', - 'gl_Position = uProj * uView * vec4(aPos, 1.0);', + 'gl_Position = uProj * uView * vec4(pos, 1.0);', '}' ].join('\n'); @@ -5227,6 +5252,8 @@ var FLT_VERT_SRC = [ 'uniform mat4 uProj;', 'uniform mat4 uView;', 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', 'uniform float uScale;', 'uniform float uMinPx;', // hard pixel floor for the light', 'uniform float uMaxPx;', // ceiling so a near jet is not a blob', @@ -5235,14 +5262,19 @@ var FLT_VERT_SRC = [ 'varying float vPhase;', 'varying float vDist;', 'void main() {', - 'vDist = length(uCamPos - aPos);', + 'vec3 pos = aPos;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = pos.xz - uCamPos.xz;', + 'pos.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'vDist = length(uCamPos - pos);', 'vBri = aBlink.x;', 'vPhase = aBlink.y;', // distance aware, but never below the pixel floor - the city dots // use the same clamp; aBlink.z carries the destination rank 'gl_PointSize = clamp(uMinPx + uBoost * uScale * aBlink.z / max(vDist, 0.35),', 'uMinPx, uMaxPx);', - 'gl_Position = uProj * uView * vec4(aPos, 1.0);', + 'gl_Position = uProj * uView * vec4(pos, 1.0);', '}' ].join('\n'); @@ -5292,6 +5324,9 @@ var PLANE_VERT_SRC = [ 'attribute float aCol;', // 0 body, 1 glass 'uniform mat4 uProj;', 'uniform mat4 uView;', + 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', '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 @@ -5306,7 +5341,12 @@ var PLANE_VERT_SRC = [ '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);', + 'vec3 origin = uOrigin;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = origin.xz - uCamPos.xz;', + 'origin.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'gl_Position = uProj * uView * vec4(origin + p, 1.0);', '}' ].join('\n'); @@ -5366,6 +5406,8 @@ var CT_VERT_SRC = [ 'uniform mat4 uProj;', 'uniform mat4 uView;', 'uniform vec3 uCamPos;', + 'uniform float uEarthRadius;', + 'uniform float uCurvatureOn;', 'varying float vU;', 'varying float vV;', 'varying float vS;', @@ -5373,8 +5415,13 @@ var CT_VERT_SRC = [ 'varying vec3 vWorld;', 'void main() {', 'vU = aU; vV = aV; vS = aS; vP = aP;', - 'vWorld = aPos;', - 'gl_Position = uProj * uView * vec4(aPos, 1.0);', + 'vec3 pos = aPos;', + 'if (uCurvatureOn > 0.5) {', + 'vec2 d = pos.xz - uCamPos.xz;', + 'pos.y -= dot(d, d) / (2.0 * max(1.0, uEarthRadius));', + '}', + 'vWorld = pos;', + 'gl_Position = uProj * uView * vec4(pos, 1.0);', '}' ].join('\n'); @@ -6370,6 +6417,8 @@ window.clouds3dRender = function(target) { gl.uniformMatrix4fv(carUniforms.proj, false, proj); gl.uniformMatrix4fv(carUniforms.view, false, view); gl.uniform3f(carUniforms.camPos, camX, camY, camZ); + gl.uniform1f(carUniforms.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(carUniforms.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform1f(carUniforms.time, now); gl.uniform1f(carUniforms.night, carNight); // dot radius the light needs per unit distance to still reach @@ -6414,6 +6463,8 @@ window.clouds3dRender = function(target) { proj: gl.getUniformLocation(cpp, 'uProj'), view: gl.getUniformLocation(cpp, 'uView'), camPos: gl.getUniformLocation(cpp, 'uCamPos'), + earthRadius: gl.getUniformLocation(cpp, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(cpp, 'uCurvatureOn'), time: gl.getUniformLocation(cpp, 'uTime'), night: gl.getUniformLocation(cpp, 'uNight'), speed: gl.getUniformLocation(cpp, 'uSpeed'), @@ -6441,6 +6492,8 @@ window.clouds3dRender = function(target) { gl.uniformMatrix4fv(carPtsUniforms.proj, false, proj); gl.uniformMatrix4fv(carPtsUniforms.view, false, view); gl.uniform3f(carPtsUniforms.camPos, camX, camY, camZ); + gl.uniform1f(carPtsUniforms.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(carPtsUniforms.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform1f(carPtsUniforms.time, now); gl.uniform1f(carPtsUniforms.night, carNight); gl.uniform1f(carPtsUniforms.sunAlt, window.__currentSunAlt || 0); @@ -6496,6 +6549,8 @@ window.clouds3dRender = function(target) { gl.uniformMatrix4fv(cityUniforms.proj, false, proj); gl.uniformMatrix4fv(cityUniforms.view, false, view); gl.uniform3f(cityUniforms.camPos, camX, camY, camZ); + gl.uniform1f(cityUniforms.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(cityUniforms.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform1f(cityUniforms.night, Math.min(1, carNight * 1.35)); gl.uniform1f(cityUniforms.sunAlt, window.__currentSunAlt || 0); gl.uniform1f(cityUniforms.lightHi, LIGHT_HI); @@ -6560,6 +6615,8 @@ window.clouds3dRender = function(target) { proj: gl.getUniformLocation(fltProg, 'uProj'), view: gl.getUniformLocation(fltProg, 'uView'), camPos: gl.getUniformLocation(fltProg, 'uCamPos'), + earthRadius: gl.getUniformLocation(fltProg, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(fltProg, 'uCurvatureOn'), scale: gl.getUniformLocation(fltProg, 'uScale'), time: gl.getUniformLocation(fltProg, 'uTime'), night: gl.getUniformLocation(fltProg, 'uNight'), @@ -6577,6 +6634,8 @@ window.clouds3dRender = function(target) { gl.uniformMatrix4fv(fltUniforms.proj, false, proj); gl.uniformMatrix4fv(fltUniforms.view, false, view); gl.uniform3f(fltUniforms.camPos, camX, camY, camZ); + gl.uniform1f(fltUniforms.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(fltUniforms.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform1f(fltUniforms.scale, gl.drawingBufferHeight / 600); gl.uniform1f(fltUniforms.time, now); gl.uniform1f(fltUniforms.night, Math.min(1, carNight * 1.2)); @@ -6621,6 +6680,9 @@ window.clouds3dRender = function(target) { planeU = { proj: gl.getUniformLocation(pp, 'uProj'), view: gl.getUniformLocation(pp, 'uView'), + camPos: gl.getUniformLocation(pp, 'uCamPos'), + earthRadius: gl.getUniformLocation(pp, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(pp, 'uCurvatureOn'), origin: gl.getUniformLocation(pp, 'uOrigin'), orit: gl.getUniformLocation(pp, 'uOrit'), s: gl.getUniformLocation(pp, 'uS'), @@ -6636,6 +6698,9 @@ window.clouds3dRender = function(target) { gl.useProgram(planeProg); gl.uniformMatrix4fv(planeU.proj, false, proj); gl.uniformMatrix4fv(planeU.view, false, view); + gl.uniform3f(planeU.camPos, camX, camY, camZ); + gl.uniform1f(planeU.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(planeU.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform3f(planeU.sun, sunv[0], sunv[1], sunv[2]); gl.uniform1f(planeU.light, dayf); var moonv = window.__currentMoonDir || [0, 1, 0]; @@ -6765,6 +6830,8 @@ window.clouds3dRender = function(target) { ctU = { proj: gl.getUniformLocation(cpm, 'uProj'), view: gl.getUniformLocation(cpm, 'uView'), camPos: gl.getUniformLocation(cpm, 'uCamPos'), + earthRadius: gl.getUniformLocation(cpm, 'uEarthRadius'), + curvatureOn: gl.getUniformLocation(cpm, 'uCurvatureOn'), sunDir: gl.getUniformLocation(cpm, 'uSunDir'), sunColor: gl.getUniformLocation(cpm, 'uSunColor'), sunInt: gl.getUniformLocation(cpm, 'uSunInt'), @@ -6780,6 +6847,8 @@ window.clouds3dRender = function(target) { gl.uniformMatrix4fv(ctU.proj, false, proj); gl.uniformMatrix4fv(ctU.view, false, view); gl.uniform3f(ctU.camPos, camX, camY, camZ); + gl.uniform1f(ctU.earthRadius, EARTH_RADIUS_UNITS); + gl.uniform1f(ctU.curvatureOn, CURVATURE_ON ? 1.0 : 0.0); gl.uniform3f(ctU.sunDir, sd[0], sd[1], sd[2]); gl.uniform3f(ctU.sunColor, _beam[0], _beam[1], _beam[2]); gl.uniform1f(ctU.sunInt, _beamL);