camera: bank the view when the flight path curves
This commit is contained in:
1 parent
1fe214f403
commit
e8ff7ee714
2 files changed
+53
-3
No files matched your search
@@ -480,7 +480,18 @@ window.CONFIG = {
|
|||||||
aim: 'off', // 'off' | 'moon'
|
aim: 'off', // 'off' | 'moon'
|
||||||
// aim: 'moon' // look at the moon while flying the path
|
// aim: 'moon' // look at the moon while flying the path
|
||||||
aimEase: 0.94, // 0 = instant turn, 0.99 = very slow
|
aimEase: 0.94, // 0 = instant turn, 0.99 = very slow
|
||||||
aimBob: 0.25 // share of the altitude bob kept while aiming
|
aimBob: 0.25, // share of the altitude bob kept while aiming
|
||||||
|
// Banking when the flight path curves. The camera keeps flying the
|
||||||
|
// same path; roll only rotates the view around the forward axis.
|
||||||
|
roll: {
|
||||||
|
enabled: true,
|
||||||
|
gain: 12, // turn-rate to bank-angle gain
|
||||||
|
maxDeg: 12, // maximum bank angle
|
||||||
|
ease: 0.92, // 0 = instant bank, 0.99 = very slow
|
||||||
|
sign: -1 // flip to +1 if the bank direction feels inverted
|
||||||
|
// roll: { enabled: false }
|
||||||
|
// roll: { gain: 20, maxDeg: 18 }
|
||||||
|
}
|
||||||
// camera: { aim: 'moon' } // look at the moon
|
// camera: { aim: 'moon' } // look at the moon
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Vendored
+41
-2
@@ -1428,6 +1428,13 @@ var CAM_CFG = (window.CONFIG && window.CONFIG.camera) || {};
|
|||||||
var CAM_AIM_MOON = (CAM_CFG.aim === 'moon');
|
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_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_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;
|
var aimHdX = 0, aimHdZ = 1, aimPitch = 0.45, aimReady = false;
|
||||||
// CONFIG.clouds = { top: ..., bottom: ... } overrides the defaults; the
|
// CONFIG.clouds = { top: ..., bottom: ... } overrides the defaults; the
|
||||||
// deck must stay below the camera and keep a minimum thickness.
|
// 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,
|
// View direction as compass bearing (world +X = east, +Z = north,
|
||||||
// matching the sun-vector convention): 0 = north, clockwise.
|
// matching the sun-vector convention): 0 = north, clockwise.
|
||||||
// The clock's compass needle reads this.
|
// 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,
|
// Periodic check: when the camera approaches the corridor edge,
|
||||||
// re-centre the mosaic (whole-tile shift of the canvas content +
|
// re-centre the mosaic (whole-tile shift of the canvas content +
|
||||||
// fetch the newly exposed columns). At uSpeed=1 the camera moves
|
// 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.
|
// Debug override: point at world origin so the mesh is always in view.
|
||||||
var fwdLen = Math.hypot(hdX, pitch, hdZ) || 1;
|
var fwdLen = Math.hypot(hdX, pitch, hdZ) || 1;
|
||||||
var fwdX = hdX / fwdLen, fwdY = -pitch / fwdLen, fwdZ = hdZ / fwdLen;
|
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(
|
var view = mat4LookAt(
|
||||||
[camX, camY, camZ],
|
[camX, camY, camZ],
|
||||||
[camX + fwdX * 5, camY + fwdY * 5, camZ + fwdZ * 5],
|
[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
|
// Inverse of (proj * view) is uploaded to the cloud/sky shader so
|
||||||
// both can reconstruct the world ray from gl_FragCoord identically
|
// both can reconstruct the world ray from gl_FragCoord identically
|
||||||
|
|||||||
Reference in new issue
Block a user