# TOWN: how a game builds a settlement (8 Oct 2026)

For the game builder. Plain steps, no theory. Interfaces are frozen in `SETTLEMENT.md`; this is the recipe.
Copy the whole `omg/kit/building/` folder into the game (it imports nothing from outside itself) plus the kits you have
in `browse/kits/<kit>/` (`kit.glb`, `kit.json`, `materials.json`, and `style.json` for `medieval`).

## 0. The one call (OhMyGames builds: code in src/lib/town, models in public/town)
```js
import { createTown } from '@/lib/town/index.mjs';
const town = await createTown(THREE, { renderer, GLTFLoader, KTX2Loader, style: 'medieval', tier: 'town', site: 'river', seed: 7 });
scene.add(town.group);
// town.spawn {x, y, z, yaw}: the main square. town.colliders: boxes {x, z, w, d, yaw, y0, y1}. town.heightAt(x, z): the ground.
```
Defaults: kits from `/town/kits/`, the KTX2 decoder from `/town/basis/` (pass `base` / `basisPath` otherwise). Extra options go
to buildSettlement (`detail: 'full' | 'lite' | 'far' | 'auto-far'`, `near: { x, z, r }`; default auto-far round the square). Light the scene with an environment map (RoomEnvironment
through PMREMGenerator, intensity about 0.5) plus a sun: the shelf materials look flat and dark under a hemisphere light alone.
The steps below are what createTown does, for a game that needs more control.

## 1. Load the kits (once)
```js
import { loadStyleKit } from './building/render.mjs';
const names = ['medieval', 'medieval-trades', 'medieval-church', 'medieval-cathedral', 'medieval-walls'];
const kits = {};
for (const n of names) { try { kits[n] = await loadStyleKit(THREE, { base: `./kits/${n}/`, renderer, GLTFLoader, KTX2Loader, DRACOLoader, basisPath }); } catch {} }
const style = await (await fetch('./kits/medieval/style.json')).json();
const landmarks = await import('./building/landmarks.mjs').catch(() => null);   // church() and cathedral()
```
A kit that is not there is fine: its atoms become grey boxes and the finding list says which.

## 2. Settle (instant: 10 to 60 ms)
```js
import { settle, heightOf } from './building/settlement.mjs';
const plan = settle({ seed: 7, tier: 'town', site: 'river', style: 'medieval' });          // the game has no terrain: we make one
const plan2 = settle({ seed: 7, tier: 'village', site: 'hill', heightAt: (x, z) => myTerrain(x, z) });   // the game has terrain: use it
```
tier = hamlet | village | town | city. site = plain | river | crossroads | hill | spur | coast. Same seed, same plan.
`plan.rect = {x, z, R}` is the middle and the build radius. `plan.findings` says in plain words what did not fit.
With your own `heightAt` there is no generated ground (`plan.terrain` is null); `hill` and `spur` then look for the
highest point in your terrain, and a river or the coast still come as lines in `plan.water` that you can ignore.

## 3. Build the meshes (0.2 to 2 s, once per settlement)
```js
import { STYLE_MODULES } from './building/styles/registry.mjs';
import { buildSettlement } from './building/settlement-render.mjs';
const mod = STYLE_MODULES.medieval;
const s = buildSettlement(THREE, { kits, styles: { medieval: style }, plan, landmarks,
  makeSpec: (st, type, seed, ov) => mod.makeSpec(st, type, seed, ov), layout: (spec, info) => mod.layoutStyle(spec, info) });
scene.add(s.group);
```
You get `s.group`, `s.colliders`, `s.heightAt(x, z)`, `s.spawn`, `s.stats`, `s.findings`.
Call it again for another seed; remove the old group first (`scene.remove(old)`).

## 4. Walk in it
- **Ground**: the player's feet are at `s.heightAt(x, z)`. It is the same function the houses were placed with.
- **Walls and houses**: `s.colliders` is a list of boxes `{ x, z, w, d, yaw, y0, y1 }` (centre, size, turn, bottom and
  top height). Test a step like this: move the point into the box frame
  (`lx = dx*cos(yaw) - dz*sin(yaw)`, `lz = dx*sin(yaw) + dz*cos(yaw)`) and refuse the step when
  `|lx| < w/2 + radius` and `|lz| < d/2 + radius` and the ground is between `y0` and `y1`.
  One box per building, wall piece, tower, large prop. A gate is two boxes with the passage left open.
- **Spawn**: `s.spawn = { x, z, y, yaw, eye }` is the middle of the main square. Put the camera at `y + eye`.
  Face east (+X) and you look at the church or cathedral: churches always face east, west front to the square.
- Streets are flat to walk on (they follow the ground). Stair lanes on hills are real stairs (`stair_flight` atoms),
  so sample the ground with `heightAt`, do not use a fixed floor.

## 5. Budgets (measured on the GPU box, 1600 x 900, one frame, medieval kit as it is today)
Round 3 (8 Oct 2026, 3x denser, default detail 'auto-far' round the main square: full within 30 m, lite to 60 m, far beyond):
| tier | buildings | draw calls | triangles | build time |
|---|---|---|---|---|
| hamlet (full) | 7 to 13 | 69 to 79 | 0.8 to 1.6 M | under 0.1 s |
| village | 56 to 91 | 245 to 266 | 1.3 to 2.4 M | 0.1 to 0.2 s |
| town | 207 to 362 | 286 to 319 | 2.2 to 3.3 M | 0.2 to 0.4 s |
| city | 318 to 768 | 214 to 289 | 1.7 to 3.6 M | 0.3 to 0.7 s |
(GPU box, seed 5, `tools/gpu/settlement_probe.py`.) **A game passes its player position as `near: { x, z, r: 30 }`** (to
createTown or buildSettlement) so the full ring is where the player stands; without it the ring sits on the main square.
Round 2 numbers (less dense, lite default) for comparison: village 22-36 buildings 2-3.8 M, town 100-140 2.9-4.5 M,
city 210-320 5-9 M.
- **Draw calls stay near 75 for every tier** (one instanced draw per atom part and material, plus ground, roads, squares,
  fields, water). The cost is triangles: about 100 000 per house in `full`, almost all roof tiles and rubble displacement.
- **`detail` option** (`buildSettlement(THREE, { ..., detail, near })`): `'full'` is today's house; `'lite'` makes each roof
  slope ONE textured slab (same tile material, world-scale UVs, 7 cm thick at the eaves and verges), the ridge one box,
  walls flat quads with their holes (same material and normal map, no displacement grid), pegs and rafter tails dropped, and
  every kit atom above 300 triangles (windows, bressummers, chimney) drawn as a vertex-clustered copy (about a third of the triangles).
  Doors, timber frame, trades atoms, signs and verge boards are as in full. Default: full for hamlet and village, lite for town
  and city. `near: { x, z, r }`: plots within r metres keep `full` even in lite (a game passes its spawn square, r 40).
  Same in `layout(spec, { ..., detail: 'lite' })` (Breton and medieval; the layout then carries `detail: 'lite'`, which
  render.mjs reads per building). From 25 m the two look the same (side-by-side shots in the W-LOD report); up close lite walls lose rubble relief.
- **`'far'`** (aerial views, the far side of town, 60 m and beyond): lite plus walls WITHOUT holes (same material, normal map kept),
  every window and door ONE flat quad 2 cm proud of the wall (new panel kind `'opening'`, dark glass 0x2a3036, doors the paint tint darkened,
  one merged mesh), timber members kept, bressummers one beam box, chimneys one closed stone box with a dark flue quad, Breton dormers a box plus a
  two slope slab roof. Signs, lanterns, shutters, gutters, downpipes, pegs, joists, quoins dropped. Trades atoms (forge shed and stack, oven,
  hall posts) stay atoms. The layout bbox is the full twin's (a far house stands exactly where its full house would). Same in
  `layout(spec, { detail: 'far' })` (medieval and Breton; `layout.detail === 'far'`). A far house is 0.6 to 1.5 k panel triangles (medieval worst of 12:
  house 1330, tavern 1490, smithy 670, shop 664, bakery 620, market hall 110 plus its atoms; trades atoms extra) against 20 to 60 k lite;
  a whole far town (109 buildings) draws 0.67 M triangles in 175 calls against 3.7 M / 241 for lite, a far city (285) 1.07 M against 9.3 M.
  `detail: 'auto-far'` with `near { x, z, r }` (r default 60): full within r, lite within 2r, far beyond (without `near`: the tier default);
  `detail: 'far'` with `near` keeps the plots within r full. `stats.farBuildings`. Pages: `settlement.html?detail=far|auto-far&near=x,z,r`, `buildings.html?detail=far` (key F).
- Lite cost per building is about 20 to 35 k triangles (house 34 k, tavern 59 k, smithy 22 k, shop 24 k, bakery 21 k, market hall 1 k, counted
  before the atom clustering, which takes roughly a quarter off); full is 100 to 220 k. Pages: `settlement.html?detail=full|lite`, `buildings.html?detail=lite` (key L toggles).
- Terrain mesh: 4 m grid, about 40 000 triangles for a city. Roads, squares, fields: one mesh each.
- Colliders: 13 (hamlet) to about 700 (city). A plain loop over them per step is fast enough; add a grid when you add NPCs.
- The new kits (trades, church, cathedral, walls) add instanced draws, not new batches per building.

## 6. What the planner promises (tests check these on 50 seeds per tier and site)
Nothing overlaps (plots, roads, squares, water). Every plot touches its road. Smithy at the edge on a main road, tavern on
the square or at the entrance, bakery near the square, market hall inside the market square (town, city), barns at the
edge. Church on the summit on hill and spur, east of the square elsewhere, facing east. Towns and cities: walls, gates
where main roads cross, towers every 40 to 60 m, party walls and gable fronts (flat ground). Villages: detached houses.
On slopes the street front is at street level and the downhill side shows a stone base (`plinth`, up to one storey);
more than that is terraced with a retaining wall. Always read `plan.findings` and `s.findings`: they list what the
planner could not do (for example "no smithy", "no free ground for the castle", "grey box instead of the church kit").

## 7. Not in yet
Castle (reserved empty plot only), tanners and dyers, harbour quays and boats, breton style tuning of plot widths,
interiors. Bridges are a plain plank box until the kit has a bridge atom.
