camera: bank the view when the flight path curves

This commit is contained in:
Richard Zink committed 2026-10-12 21:23:54 +02:00
1 parent 1fe214f403
commit e8ff7ee714
2 files changed
+53 -3

No files matched your search

+41 -2
View File
@@ -1428,6 +1428,13 @@ var CAM_CFG = (window.CONFIG && window.CONFIG.camera) || {};
var CAM_AIM_MOON = (CAM_CFG.aim === 'moon');
var CAM_AIM_EASE = (typeof CAM_CFG.aimEase === 'number') ? Math.max(0, Math.min(0.999, CAM_CFG.aimEase)) : 0.94;
var CAM_AIM_BOB = (typeof CAM_CFG.aimBob === 'number') ? Math.max(0, Math.min(1, CAM_CFG.aimBob)) : 0.25;
var CAM_ROLL_CFG = CAM_CFG.roll || {};
var CAM_ROLL_ON = CAM_ROLL_CFG.enabled !== false;
var CAM_ROLL_GAIN = (typeof CAM_ROLL_CFG.gain === 'number') ? Math.max(0, CAM_ROLL_CFG.gain) : 12;
var CAM_ROLL_MAX = (typeof CAM_ROLL_CFG.maxDeg === 'number') ? Math.max(0, Math.min(35, CAM_ROLL_CFG.maxDeg)) * Math.PI / 180 : 12 * Math.PI / 180;
var CAM_ROLL_EASE = (typeof CAM_ROLL_CFG.ease === 'number') ? Math.max(0, Math.min(0.999, CAM_ROLL_CFG.ease)) : 0.92;
var CAM_ROLL_SIGN = (typeof CAM_ROLL_CFG.sign === 'number') ? CAM_ROLL_CFG.sign : -1;
var lastCamBearing = null, lastCamTime = null, camRoll = 0;
var aimHdX = 0, aimHdZ = 1, aimPitch = 0.45, aimReady = false;
// CONFIG.clouds = { top: ..., bottom: ... } overrides the defaults; the
// deck must stay below the camera and keep a minimum thickness.
@@ -6243,7 +6250,29 @@ window.clouds3dRender = function(target) {
// View direction as compass bearing (world +X = east, +Z = north,
// matching the sun-vector convention): 0 = north, clockwise.
// The clock's compass needle reads this.
window.__flightBearing = Math.atan2(hdX, hdZ);
var flightBearing = Math.atan2(hdX, hdZ);
window.__flightBearing = flightBearing;
var viewRoll = 0;
if (CAM_ROLL_ON) {
if (lastCamBearing !== null && lastCamTime !== null) {
var _dt = Math.max(0.001, now - lastCamTime);
var _db = flightBearing - lastCamBearing;
while (_db > Math.PI) _db -= 2 * Math.PI;
while (_db < -Math.PI) _db += 2 * Math.PI;
var _turnRate = _db / _dt;
var _target = CAM_ROLL_SIGN * _turnRate * CAM_ROLL_GAIN;
if (_target > CAM_ROLL_MAX) _target = CAM_ROLL_MAX;
else if (_target < -CAM_ROLL_MAX) _target = -CAM_ROLL_MAX;
camRoll += (_target - camRoll) * (1 - CAM_ROLL_EASE);
}
lastCamBearing = flightBearing;
lastCamTime = now;
viewRoll = camRoll;
} else {
lastCamBearing = null;
lastCamTime = null;
camRoll = 0;
}
// Periodic check: when the camera approaches the corridor edge,
// re-centre the mosaic (whole-tile shift of the canvas content +
// fetch the newly exposed columns). At uSpeed=1 the camera moves
@@ -6309,10 +6338,20 @@ window.clouds3dRender = function(target) {
// Debug override: point at world origin so the mesh is always in view.
var fwdLen = Math.hypot(hdX, pitch, hdZ) || 1;
var fwdX = hdX / fwdLen, fwdY = -pitch / fwdLen, fwdZ = hdZ / fwdLen;
var upX = 0, upY = 1, upZ = 0;
if (Math.abs(viewRoll) > 1e-5) {
var _rc = Math.cos(viewRoll), _rs = Math.sin(viewRoll);
// Rodrigues rotation of the world-up vector around the forward axis.
upX = -fwdZ * _rs;
upY = _rc;
upZ = fwdX * _rs;
var _ul = Math.hypot(upX, upY, upZ) || 1;
upX /= _ul; upY /= _ul; upZ /= _ul;
}
var view = mat4LookAt(
[camX, camY, camZ],
[camX + fwdX * 5, camY + fwdY * 5, camZ + fwdZ * 5],
[0, 1, 0]
[upX, upY, upZ]
);
// Inverse of (proj * view) is uploaded to the cloud/sky shader so
// both can reconstruct the world ray from gl_FragCoord identically