fuzzyworld

tower / commit

fix(exterior): draw the seas behind the cap windows

28896dabbe2bf040670c6f47ba8a1378418db502 · Pavel Safronov · 2026-09-27 16:40:16 -0700 · parents 9d5a061c0239

Claude Code usage: this commit ≈0 out / ≈0 cached · cost unknown (no per-model trailer) · session 2 tally 744 in / 324.4K out / 79.7M cached · usage by commit and session

fix(exterior): draw the seas behind the cap windows

The see-through windows kept render order 0 while water drew at 1, so a
window went first, wrote its depth, and every sea behind the glass failed
the depth test: looking in at the sun cap showed the Sunward Sea's brown
bed. Windows now draw at 2.5, after water and falls and before the beam;
the exterior's rays share that order. Noted under D-066.

Usage: 744 in / 324.4K out / 79.7M cached (session tally)
rtk:   905.3K saved, 15.2% over 2657 cmds (global/lifetime)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
 decisions.md                |  7 +++++++
 src/views/exterior.ts       |  9 ++++++---
 src/views/interior-model.ts | 12 ++++++++++++
 3 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/decisions.md b/decisions.md
index 990372c..e5c7f11 100644
--- a/decisions.md
+++ b/decisions.md
@@ -2185,6 +2185,13 @@ readers get too, as §5.4 planned — keyed so a new build never reads an old wo
 compiles once, hands every shot the warm cache, caps each page at two workers, and reports
 any shot that compiled anyway. It takes about 100 s and averages about 3.4 cores.
 
+**A regression this caused, fixed 2026-09-27.** Giving water render order 1 put it after
+the cap windows, which kept the default 0. From outside, a window is transparent and in
+front of the seas: drawn first, it wrote its depth, and the water behind it failed the
+depth test — so looking in at the sun cap showed the Sunward Sea's bed, brown, instead of
+the sea. See-through windows now draw at 2.5, after the water and the falls and before the
+beam, and the exterior's rays share that order so they still sort against the glass.
+
 ---
 
 ## D-067 — Tower's weather is a published plan, and ships read it
diff --git a/src/views/exterior.ts b/src/views/exterior.ts
index acd5307..e76a9bd 100644
--- a/src/views/exterior.ts
+++ b/src/views/exterior.ts
@@ -21,7 +21,7 @@ import {
 } from '../canon/constants.js'
 import { type SurfacePoint, mapThetaOrigin, normalizeTheta } from '../canon/geometry.js'
 import { terrain } from '../terrain/service.js'
-import { InteriorModel, STAR_GLSL } from './interior-model.js'
+import { InteriorModel, STAR_GLSL, WINDOW_RENDER_ORDER } from './interior-model.js'
 
 const KM = 1000
 const R = RADIUS_M / KM
@@ -447,10 +447,13 @@ export class ExteriorView {
     const beam = new BufferGeometry()
     beam.setAttribute('position', new Float32BufferAttribute(verts, 3))
     beam.setAttribute('color', new Float32BufferAttribute(cols, 4))
-    this.scene.add(new LineSegments(beam, new LineBasicMaterial({
+    const rays = new LineSegments(beam, new LineBasicMaterial({
       vertexColors: true, transparent: true,
       blending: AdditiveBlending, depthWrite: false,
-    })))
+    }))
+    // The same order as the windows, so the rays still sort against the glass they end at.
+    rays.renderOrder = WINDOW_RENDER_ORDER
+    this.scene.add(rays)
   }
 
   /**
diff --git a/src/views/interior-model.ts b/src/views/interior-model.ts
index 630e44b..caefab9 100644
--- a/src/views/interior-model.ts
+++ b/src/views/interior-model.ts
@@ -1280,6 +1280,9 @@ export interface InteriorModelOptions {
   seeThrough: boolean
 }
 
+/** Where the see-through windows draw among the transparent things (see `buildCaps`). */
+export const WINDOW_RENDER_ORDER = 2.5
+
 export class InteriorModel {
   /** Every material here, so a caller can set a shared uniform in one loop. */
   readonly materials: ShaderMaterial[] = []
@@ -1583,6 +1586,15 @@ export class InteriorModel {
       side: BackSide, uniforms: { uTint: { value: new Color(0x9aa0aa) } },
     }))
     reveal.name = 'bore'
+    /**
+     * From outside, a window is drawn after the water (1) and the falls (2) behind it.
+     * Left at 0 it went first, wrote its depth, and every sea behind the glass failed the
+     * depth test — so looking in at the sun cap showed the Sunward Sea's bed, brown, where
+     * the sea should have been. It still goes before the beam (3), which is therefore
+     * hidden behind the glass as it always has been. The exterior's own transparent things
+     * share this order (exterior.ts), so they still sort against the glass by distance.
+     */
+    if (this.opts.seeThrough) sun.renderOrder = far.renderOrder = WINDOW_RENDER_ORDER
     return [sun, far, reveal]
   }