# Medieval town atom kit

Twenty frozen placement atoms for late-medieval northern European houses and the town square. Models use metres, +Y up, +Z facade outward, and X across the facade. Names, dimensions, bounding boxes, clear openings and placement exceptions are in `kit.json`. The source reference is `sheet.jpg`.

## Files

- `kit.glb`: all twenty named nodes at their authored origins, with identity transforms.
- `kit.json`: placement contract, budgets, dimensions and per-node material names.
- `materials.json` and `materials/`: external PBR shelf, shared maps preserved byte-for-byte. Bind by exact material name. GLB contains geometry and material factors, without embedded images.
- `kit.blend`: editable source geometry. `build_kit.py`, `geometry.py`, `details.py`, `prepare_materials.py`: reproducible builders.
- `kit_sheet.png`: labelled atom contact sheet. `test_house.png`: rich house corner, cottage, pillory and wheel well. `test_house.glb`: the same assembly for loader testing.
- `viewer/`, `browser_check.py`, `runtime_check.json`: actual Three.js loader, captures and measured draw counts.
- `validation.json`, `audit_glb.json`: geometry/budget checks. `renders/`, `details_*.png`, `rear_*.png`: textured and clay evidence.

## Placement

Facade atoms stay upright. The exterior host wall plane is Z=0. Door origins are bottom centre of the clear opening, with plank leaves recessed behind the wall and stone trim in front. Cut the declared opening out of the host wall; do not cover door leaves, barred openings, unglazed windows or bay interiors with a solid wall. `window_shuttered` has the same 0.7 × 1.0 m clear opening as `window_leaded`; open shutters project approximately 0.355 m. These are physical open leaves, not glass planes.

`dormer_gabled` and `dormer_hipped` remain vertical, with their sill centre on the roof. Rotate around Y for roof orientation, **not around X to follow roof pitch**. The 55° host plane in atom space is `y = -tan(55°) * z`: positive Z points downslope. The cheeks and roofs extend behind the plane and bury into it. Front glazing stays above the slope. No host-roof cutout is needed for the delivered opaque glazing. Wall dormers instead anchor at the eaves, with their front at Z=0.

`thatch_panel` is in slope space: X along the eaves, Y thickness away from rafters, Z downslope. Place its upper-edge origin at 1 m grid intervals, then rotate the whole grid to the roof pitch (50–55°). Actual panels overlap by 0.01 m in X and 0.04 m in Z; nominal grid units remain 1 × 1 m. Do not scale them apart. `thatch_ridge` repeats along X at 1 m intervals; its skirts descend below the ridge to cover panel tops. The test cottage has exactly three columns and two rows per side, plus three ridge rolls. Reed normal detail is derived from the same periodic analytic height field as the albedo, not an AO or geometry bake. Maps are 1024² with a 1 m surface repeat and 0.4 m cut-end repeat; PNG row derivatives are inverted for OpenGL +Y normal maps.

Chimneys anchor bottom centre, with their base 0.55 m below the roof ridge. `louvre_ridge` and ridge rolls have ridge axis X. Signs anchor at the wall fixing point, hang below Y=0 and project into +Z. The oriel anchors at the storey floor on its back plane; corbels intentionally extend 0.43 m below that origin and at least 35 mm in front of the wall. Door surrounds project 175 mm, avoiding facade z-fighting.

Nominal envelopes guide placement; consult actual bounding boxes for culling. Small bargeboard/bevel overhangs and below-origin supports are retained, rather than distorting the frozen anchors to force an axis-aligned nominal box.

## Materials and loader

Use existing shelf names; `Thatch_reed`, `Thatch_reed_cut` and `Leather_brown` are original additions. The cut material supplies hollow stalk ends on eaves and verges. UV0 already contains the physical repeat scale: set texture repeat to 1, wrap both axes, load base colour as sRGB and normal/ORM as linear. For glTF external maps use `flipY=false`. ORM channels are ambient occlusion, roughness, metallic. Copy UV0 to the AO UV channel when required by the renderer. `Door_paint` and `Sign_paint` remain recolourable.

The source geometry is deliberately authored Y-up in Blender and exported with `export_yup=False`. Blender imports the delivered GLB into its usual Z-up workspace for rendering. The render script handles that conversion; never apply it a second time in the game.

## Rebuild and checks

```sh
python3 prepare_materials.py
blender -b --factory-startup --python-exit-code 1 --python build_kit.py
blender -b --factory-startup --python-exit-code 1 --python render_kit.py
blender -b --factory-startup --python-exit-code 1 --python bind_source_materials.py
python3 make_sheet.py
python3 validate_kit.py
python3 browser_check.py
```

`validate_kit.py` uses the installed Forge `audit_glb.py`. The strict generic audit flags identical primitives reused by the two different dormer atoms. This is an explicitly checked cross-node reuse, not duplicated surfaces inside one atom. No zero-area triangles or non-finite attributes are accepted. Browser metrics are measured main-pass counts; software Chromium timings are not representative of a game GPU. Roof-cover/eaves-system contract sections are excluded per this task's brief.

## Visual review

The reed revision adds weathered directional albedo and registered normals, scalloped eaves with hollow stem ends, and bent hazel ridge spars. The board-free boot uses dark `Leather_brown` with its lighter sole edge. Frozen atom frames and budgets remain unchanged. See `critic_final.txt` for the current visual review. The delivered louvre has three physical slats per long side with open gaps; its feet embed at the ridge.

Current technical checks pass: 298 panel triangles, 246 ridge triangles, 25,072 kit triangles. Measured kit draws rose from 61 to 63; assembly draws from 86 to 92. Visual status remains REVISE: continuous strand striping and the regular eave silhouette still fall short of natural shaggy reed. See `revision_metrics.json` and `critic_final.txt`.
