1
0

feat: relief that ramps with the camera, and an instrument to cost a pose

`verticalExaggeration` is derived per board so the tallest blended peak
fills a fixed fraction of the board framed whole — 15.00 for California,
5.78 for the Bay Area, 3.41 for the Southland. Each is right, and while a
board could only be looked at from its own stand-off one number was all a
board could want. The merged California is the first that can be flown
from 1,551 km to 1.9 km, and 15x is what put the camera inside Twin Peaks
at the FiDi rung: 2.19 units of drawn hill against a 2.26-unit stand-off.

So a pack may now declare `nearVerticalExaggeration` — what it wants when
the camera is *in* it — and `unify.ts` sets it to the largest figure
either merged metro asked for, which is San Francisco's 5.78. Largest
rather than mean or smallest: both metros are known to read well at their
own number, and picking the smaller would flatten the Bay to suit a basin
four hundred kilometres away.

It is applied as one `scale.y` on a new `ground` group rather than by
rebuilding anything. Every height in that group came from the same
exaggeration, so multiplying restates all of them consistently — a lot
placed on a hillside is still on it, a bridge deck still clears the
water, a berth is still at the quay. That is the property the alternative
does not have: re-deriving placements against new ground is exactly the
blocker TODO.md names for the terrain quadtree, and this sidesteps it
rather than solving it. The quadtree will still have to solve it.

The sky is deliberately outside the group — clouds, precipitation,
migration, live aircraft, satellites, Starlink. Their altitudes are facts
about the atmosphere, not about the terrain, and an aeroplane that sinks
when the hills flatten draws the wrong thing. The visible consequence is
that traffic stands further off the ground as the ramp flattens it,
because it always was that far off in true metres.

The ramp is driven from stand-off, not altitude. Altitude is measured
against the ground and this moves the ground, so an altitude trigger is a
feedback loop; stand-off belongs to the controls alone. Log-interpolated,
because the rungs are spaced logarithmically — 1,551, 501, 256, 95, 45,
24, 8.3, 7.7 km — and a linear ramp spends its whole travel between the
two widest and none across the eight that matter.

Also: `scripts/cost-at.mjs`, which costs an arbitrary pose with
`performance-budget.mjs`'s own patched GL counters, so its numbers are
comparable rather than nearly comparable. The budget harness only ever
visits the poses a board is judged on, which is the right instrument for
"did this regress" and useless for "how much room is there at the bottom
of the descent". First run, merged board, at SF HQ:

    400 km  355,759 tris / 414 draws
    120 km  351,019 / 327
     45 km  332,969 / 265
      7.7km 330,426 / 339
      2.5km 329,392 / 334

The Bay Area board at that last pose draws 2,307,964. Ours is flat across
three orders of magnitude of stand-off, which is not a budget being spent
carefully — it is a board with no frustum culling doing any work, paying
for the whole state's heightfield while looking at one city block.
This commit is contained in:
2026-08-24 16:44:21 -07:00
parent 6e0281eed5
commit d6dc307d9a
6 changed files with 333 additions and 19 deletions
+5 -2
View File
@@ -75,8 +75,11 @@ test("the scene builds them, from the field the packs fill in", () => {
"nothing to express.",
);
assert.ok(
/scene\.add\(\s*airportGroup\s*\)/.test(scene) ||
/scene\.add\(createAirports\(/.test(scene),
// `ground`, not `scene`, since the relief ramp landed: a runway apron is a
// statement about the terrain and has to be restated with it, or an
// airfield floats off the ground the moment the exaggeration changes.
/(?:scene|ground)\.add\(\s*airportGroup\s*\)/.test(scene) ||
/(?:scene|ground)\.add\(createAirports\(/.test(scene),
"scene.ts builds the airports but never adds them to the scene",
);
});
+7 -1
View File
@@ -144,7 +144,13 @@ test("a layer that was not supplied contributes nothing at all", () => {
);
assert.match(
SCENE,
new RegExp(`if \\(${local}\\) \\{[\\s\\S]{0,200}?scene\\.add\\(${local}\\.group\\);`),
// `scene` or `ground`: the ground group is a child of the scene that
// carries everything whose height is a statement about the terrain, so
// the relief ramp can restate all of them with one scale. Which of the
// two a layer belongs to is a question about altitude — the port is on
// the ground, the rain is not — and this assertion is about the *guard*,
// not about which parent won that argument.
new RegExp(`if \\(${local}\\) \\{[\\s\\S]{0,200}?(?:scene|ground)\\.add\\(${local}\\.group\\);`),
`${local} is added to the scene outside its own null guard`,
);
}