From 2fd394a8655c5db1bb53b313b658a652a14b16fe Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Thu, 8 Oct 2026 06:51:46 +0200 Subject: [PATCH] Right-side-up text: flip UNPACK flag + swap canvas axes UNPACK_FLIP_Y_WEBGL=true was correct for screen orientation but the shader's rotated-frame math expected it false. To get both right: - Re-enable UNPACK_FLIP_Y_WEBGL=true so canvas top maps to texture V=1 (screen top). Text now reads right-side up. - Invert the drawing axes: canvasY = (-lng + halfLong + 0.5)*TILE_SIZE so lng=+12 (corridor front, ahead) lands at small canvas Y, which after upload ends up at texture V=1, i.e. at screen top. Same inversion for perp. - Shader's gUv.y = rotated.x / uGroundSize.y + 0.5 is unchanged: for offset.z=+12 (north) it samples V=0.98 -> texture row 6271 -> canvas row 128 (the new top-of-corridor row) -> lng=+12. Correct. Effect: N labels read upright, image orientation matches world directions, and corridor scrolls in the visible flight direction. --- vendor/clouds3d.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 63f4a57..02e337d 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -328,8 +328,13 @@ function drawTileAt(lng, perp, dx, dz, centerTileX, centerTileY) { // (vertical) and perp along canvas X (horizontal). This way the // corridor's long axis (flight direction +Z) appears as a vertical // strip on canvas, matching the way depth maps on screen. - var canvasX = (perp + halfPerp + 0.5) * GROUND_TILE_SIZE; - var canvasY = (lng + halfLong + 0.5) * GROUND_TILE_SIZE; + // With UNPACK_FLIP_Y_WEBGL=true, canvas top -> texture V=1 (screen top). + // We want lng=+12 (corridor north/front, ahead of camera) at screen top. + // So canvasY is inverted: small Y = high lng (north). + // Same idea for perp: world east (dx > 0) should be on screen right, + // so canvasX is also inverted: large X = perp < 0 = dx > 0 = east. + var canvasX = (-perp + halfPerp + 0.5) * GROUND_TILE_SIZE; + var canvasY = (-lng + halfLong + 0.5) * GROUND_TILE_SIZE; if (canvasX < 0 || canvasX >= groundCanvas.width || canvasY < 0 || canvasY >= groundCanvas.height) { return Promise.resolve(false); } @@ -447,7 +452,7 @@ function initGroundSystem(glCtx) { groundCtx.fillRect(0, 0, groundCanvas.width, groundCanvas.height); groundTexture = groundGl.createTexture(); groundGl.bindTexture(groundGl.TEXTURE_2D, groundTexture); - groundGl.pixelStorei(groundGl.UNPACK_FLIP_Y_WEBGL, false); + groundGl.pixelStorei(groundGl.UNPACK_FLIP_Y_WEBGL, true); groundGl.texImage2D(groundGl.TEXTURE_2D, 0, groundGl.RGBA, groundGl.RGBA, groundGl.UNSIGNED_BYTE, groundCanvas); groundGl.texParameteri(groundGl.TEXTURE_2D, groundGl.TEXTURE_WRAP_S, groundGl.CLAMP_TO_EDGE); groundGl.texParameteri(groundGl.TEXTURE_2D, groundGl.TEXTURE_WRAP_T, groundGl.CLAMP_TO_EDGE);