Smooth ground scroll: redraw corridor every frame
Replaced refetch-based updates with continuous per-frame redraw. The corridor now tracks the camera's continuous world position (floating point, not floored to a tile) so the tile content scrolls smoothly underneath the camera. No more 'jump by 1 tile' at every refetch boundary. Tradeoff: every frame draws ~65 images and does one texImage2D upload. Browser caches the Image elements, so the cost is mainly compositing + upload (~5MB at 6400x1280). Should still hit 30+ fps. This is closer to how real flight sims work: the world content scrolls continuously, not in tile-size jumps.
This commit is contained in:
1 parent
23327a1cba
commit
4f8fd4b62a
1 file changed
+52
-4
Vendored
+52
-4
@@ -307,6 +307,8 @@ var groundCenterTile = { x: GROUND_BASE_X, y: GROUND_BASE_Y };
|
|||||||
var groundLoadedTiles = {}; // "x_y" -> true
|
var groundLoadedTiles = {}; // "x_y" -> true
|
||||||
var groundLastCheck = 0;
|
var groundLastCheck = 0;
|
||||||
var groundUpdatePending = false;
|
var groundUpdatePending = false;
|
||||||
|
var groundCameraX = 0;
|
||||||
|
var groundCameraZ = 0;
|
||||||
|
|
||||||
function fetchTileImage(tileX, tileY) {
|
function fetchTileImage(tileX, tileY) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
@@ -416,6 +418,44 @@ function rebuildGroundMosaic(centerTileX, centerTileY) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continuously redraw the corridor based on the live camera
|
||||||
|
// position. Without this the corridor would shift in tile-size jumps
|
||||||
|
// at every refetch; with it the corridor scrolls smoothly as the
|
||||||
|
// camera flies. The browser caches the loaded Image elements so the
|
||||||
|
// only per-frame cost is compositing + a texImage2D upload (~5MB).
|
||||||
|
function refreshGroundMosaic() {
|
||||||
|
if (!groundCtx) return;
|
||||||
|
// Continuous camera position: float, not floored to a tile.
|
||||||
|
var fdx = (window.__flightDir && window.__flightDir.dx) || 0;
|
||||||
|
var fdz = (window.__flightDir && window.__flightDir.dz) || 1;
|
||||||
|
var camX = (fdx !== 0) ? groundCameraX * fdx : 0;
|
||||||
|
var camZ = (fdz !== 0) ? groundCameraZ * fdz : 0;
|
||||||
|
var centerTileX = GROUND_BASE_X + Math.floor(camX);
|
||||||
|
var centerTileZ = GROUND_BASE_Y + Math.floor(camZ);
|
||||||
|
groundCenterTile = { x: centerTileX, z: centerTileZ };
|
||||||
|
var halfLong = Math.floor(GROUND_CORRIDOR_LONG / 2);
|
||||||
|
var halfPerp = Math.floor(GROUND_CORRIDOR_PERP / 2);
|
||||||
|
// Clear and redraw every frame.
|
||||||
|
groundCtx.fillStyle = '#7c9aa1';
|
||||||
|
groundCtx.fillRect(0, 0, groundCanvas.width, groundCanvas.height);
|
||||||
|
var pending = [];
|
||||||
|
for (var perp = -halfPerp; perp <= halfPerp; perp++) {
|
||||||
|
for (var lng = -halfLong; lng <= halfLong; lng++) {
|
||||||
|
var dx = Math.round(lng * GROUND_TRAVEL_COS - perp * GROUND_TRAVEL_SIN);
|
||||||
|
var dz = Math.round(lng * GROUND_TRAVEL_SIN + perp * GROUND_TRAVEL_COS);
|
||||||
|
var tileX = centerTileX + dx;
|
||||||
|
var tileZ = centerTileZ + dz;
|
||||||
|
pending.push(drawTileAt(lng, perp, dx, dz, tileX, tileZ, centerTileX, centerTileZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Promise.all(pending).then(function() {
|
||||||
|
if (groundTexture && groundGl) {
|
||||||
|
groundGl.bindTexture(groundGl.TEXTURE_2D, groundTexture);
|
||||||
|
groundGl.texImage2D(groundGl.TEXTURE_2D, 0, groundGl.RGBA, groundGl.RGBA, groundGl.UNSIGNED_BYTE, groundCanvas);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Called periodically from the render loop with the camera world
|
// Called periodically from the render loop with the camera world
|
||||||
// position. Re-centres the mosaic if we're approaching its edge.
|
// position. Re-centres the mosaic if we're approaching its edge.
|
||||||
function maybeRefetchGround(cameraWorldX, cameraWorldZ, gl) {
|
function maybeRefetchGround(cameraWorldX, cameraWorldZ, gl) {
|
||||||
@@ -586,10 +626,18 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.uniform2f(s.uFlightDir, flightDir.dx, flightDir.dz);
|
gl.uniform2f(s.uFlightDir, flightDir.dx, flightDir.dz);
|
||||||
|
|
||||||
var drift = now * speed * 0.12;
|
var drift = now * speed * 0.12;
|
||||||
if (performance.now() - groundLastCheck > 1500) {
|
// Track continuous camera world position (X and Z independently,
|
||||||
groundLastCheck = performance.now();
|
// since flight direction is independent from fetch direction).
|
||||||
maybeRefetchGround(drift, drift, gl);
|
var fdx2 = (window.__flightDir && window.__flightDir.dx) || 0;
|
||||||
}
|
var fdz2 = (window.__flightDir && window.__flightDir.dz) || 1;
|
||||||
|
groundCameraX = (fdx2 !== 0) ? drift * fdx2 : 0;
|
||||||
|
groundCameraZ = (fdz2 !== 0) ? drift * fdz2 : 0;
|
||||||
|
// Continuous scroll: redraw the corridor every frame so the camera
|
||||||
|
// glides smoothly through the tile content rather than jumping at
|
||||||
|
// integer tile boundaries. Browser caches the loaded Image
|
||||||
|
// elements so the only per-frame cost is compositing + a small
|
||||||
|
// texImage2D upload.
|
||||||
|
refreshGroundMosaic();
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user