# Northern European late-medieval atom kit

29 original procedural masters for Alsace / Normandy / Rhineland buildings. The supplied `sheet.png` guided timber, casement, tile, door and masonry detailing. The adjacent Breton kit was a read-only implementation precedent.

- `kit.glb`: geometry, named placeholder materials and articulated child nodes. No embedded textures.
- `kit.json`: exact node names, actual triangle counts/budgets, bounds, clear openings, origins and placement contracts.
- `materials.json` and `materials/`: 14 exact-name shared materials, registered BaseColor / Normal / ORM maps; 16-bit Height on the two rubble and three plaster materials.
- `kit_sheet.png`, `material_sheet.png`, `details_*.png`, `clay_check.png`, `reverse_check.png`: renders of the delivered GLB with the external shelf bound.
- `viewer/`: Three.js preview with atom selection, moving parts, clay and rear views. Run `python3 -m http.server 18789`, then open `http://localhost:18789/viewer/`.
- `build_kit.py`, `geometry.py`, `shelf.py`, `render_kit.py`, `sources/`: reproducible source. Blender 5.2: `blender -b --factory-startup --python-exit-code 1 --python build_kit.py`. Add `-- --reuse-shelf` to preserve the existing shelf, or `-- --geometry-only` to skip rendering. Run `python3 make_sheet.py` afterward.

## Assembly contract

Coordinates are metres, **Y up, +Z exterior, +X along the facade**. Master roots are identity transforms. Opening origins are bottom centre at the exterior wall plane. Bounding boxes include surrounds, hardware and sills: use the `opening` fields for wall cuts, not full bounds. Wall backs lie at Z=-0.20 for timber windows and Z=-0.50 for stone windows. Straight frame members and infill are generated by the house assembler. Keep plaster faces at Z=-0.025; braces occupy the timber face. Align generated jamb studs with the outer jamb edges and avoid a duplicate surround.

Clone an entire named root, preserving its child hierarchy. Rotate the two casements around their child-local Y axes, left negative/right positive; the same convention applies to the double door. The plank door opens around local Y. Rotate `window_shop_counter` +90 degrees around local X for the outward horizontal counter. Sign board pivot is local Z. The small fixed dormer casement is part of the dormer mesh.

`bressummer_1m` repeats every 1 m along X; the vine position and tangent match across boundaries. `joist_end` projects 0.45 m along +Z. The knee bracket drops below its origin. Tiles use X across the roof, local Y thickness and +Z downslope. Rotate X by the roof pitch; repeat courses every 0.10 m along the roof slope. Tile strips span exactly 1.32 m. Stagger alternate courses by 0.0825 m and trim the verge boundary, using individual tiles for the edge fill. Built-in lap tilt separates overlapping plain-tile courses. Ridge caps repeat at 0.255 m along +X, with a 45 mm lap and a rising end profile; their nominal bedding is for a 55-degree roof, usable within the stated 50–60-degree range with bedding adjustment. No gutters are included.

The dormer is authored for a 55-degree main roof. Its main-roof cut polygon and equation are in `kit.json`; remove the main roof under that polygon. Its rear cheeks and tiles terminate on the roof plane. Do not insert the dormer through an uncut roof. Chimney origin is its bottom centre, with pots reaching Y=2.5 m.

## Material binding

Replace materials by exact name using `materials.json`. BaseColor is sRGB; Normal and ORM are linear. ORM channels are R=AO, G=roughness, B=metallic. Use repeat wrapping and `flipY=false` for glTF in Three.js. UV0 already encodes physical repeat; do not apply a second texture repeat. Set material roughness and metalness factors to 1 so the ORM controls both. Timber longitudinal grain maps to U. Generated beams should orient U along each member and retain metric scale.

Height maps are optional inputs for generated stone walls/plaster, not automatic mesh displacement on atoms. Keep opening boundaries fixed when displacing generated walls. `Door_paint` and `Sign_paint` are deliberately light neutral tint targets. Glass is an opaque smoky surrogate; its physical lead lattice is geometry, so it does not rely on transparency sorting.

Textures derive from Poly Haven CC0 scans: [castle_wall_varriation](https://polyhaven.com/a/castle_wall_varriation), [rough_wood](https://polyhaven.com/a/rough_wood), [rough_plaster_03](https://polyhaven.com/a/rough_plaster_03), [worn_rock_natural_01](https://polyhaven.com/a/worn_rock_natural_01), and [metal_plate_02](https://polyhaven.com/a/metal_plate_02). Source hashes, periodic seam checks and derivation are recorded in `texture_evidence.json` and `shelf.py`.

`assembly_check.py` renders a small facade fixture with generated straight members and infill, a 0.5 m stone wall, repeated jetty joinery and tile courses. `assembly_check.png` and `assembly_clay.png` show that fixture; it is not an extra master in the GLB.

## Validation

`audit.json` is the strict binary GLB audit. `validation.json` checks required names, budgets, identity master roots, material files, bounds and articulation contracts. `runtime_check.json` records actual Chromium/Three.js loading and opening tests. Browser timings use SwiftShader software rendering and are not a hardware performance claim. `critic_01.log` and `critic_02.log` retain independent visual review evidence.
