# Dungeon surface kits

The current square dungeon renderer is built from the art already packaged with
HEXatlas. The earlier generated flagstone files remain here only as local design
experiments; they are not active dungeon textures.

Style names: Painted (stored id `fantasy`; the style was called Fantasy before Oct 2026) and Low Poly (stored id `fabled-lands-3d`; the style was called Fabled Lands 3D before Oct 2026). Folders such as `fabled/`, `fabled-lands-3d/`, `fantasy/` and `fantasy-model-*` keep the stored ids.

| Theme | Active source | Use |
| --- | --- | --- |
| Painted | Props: `fantasy-model-pixels/` (76 models × 8 facings, pixelized from the Low Poly mesh bakes; see below). Floors: `crypt-surfaces/fantasy/`. | Pixel-art props and floors, drawn with smoothing off. `assets/hex-packs/fantasy/locations/decor/*` remains only as the preset fallback for generated props. |
| Old-School | `assets/hex-packs/old-school/*` | Existing inked symbols; the floor renderer stays parchment-and-ink for readability. |
| Classic | `assets/hex-packs/classic/*` | Existing Classic colors and symbols with a high-contrast floor renderer. |
| Low Poly | `fabled/` contains renders of KayKit Dungeon Remastered 1.1 (CC0) source models and palette variants of those dirt renders. | 23 floor materials and 158 base/variant looks; the original 346 PNG sprites include six floor families, eleven furnishings with variants/eight facings, and older wall/door caps. |

The actual room geometry, doors, walls and furnishings are records in the map,
not raster pixels. That means users can repaint floors, edit walls, move props,
and export the resulting battlemap normally.

Painted furnishings are pixel sprites (next section). Classic and Old-School furnishings, floors, door glyphs and wall bands are
original vector SVGs in `vector/{classic,old-school}/` (built by
`scripts/build-vector-dungeon-kits.mjs`, matching the settlement stamp sets:
ink-on-paper for Old-School, flat shapes with soft borders for Classic; the
floors include every Low Poly look, the 66 named terrain looks such as
`forest-oak-a` too); the inline symbols in `dungeon-kit.js` remain only as the last fallback. Existing source art's
license/provenance restrictions remain in
[`ASSET_RIGHTS_REVIEW.md`](../../ASSET_RIGHTS_REVIEW.md). The earlier generated
flagstone experiments are not part of the active distributable kit. Active
Low Poly terrain floors derive from the CC0 source described below.

## Low Poly model bake

`scripts/bake-dungeon-kit.py` reads the original OBJ geometry and UV coordinates,
samples the pack's `dungeon_texture.png`, and renders orthographic sprites with
fixed lighting. No AI-generated images or world-map hex tiles are used. The
source folder found locally is `KayKit_DungeonRemastered_1.1_FREE` (the purchased
edition was not independently identified). `fabled/LICENSE.txt` is copied from
that pack; `fabled/manifest.json` records each source model's hash and facing.

Run with Pillow and numpy already installed:

```
python3 scripts/bake-dungeon-kit.py /path/to/KayKit_DungeonRemastered_1.1_FREE
```

Core floors and furnishings load small PNG sprites as needed, without loading the
original KayKit meshes. Grid coordinates, editable wall paths, door states and object records
remain the same. Uploaded floor textures and customized object icons take
precedence. Furniture uses eight rendered facings at 45° intervals. The older
wall/door cap sprites remain on disk but are no longer used for live solid walls.

Floor variants use the existing fixed/random brush controls. Furnishing variants
are selectable when placing or editing an object, including random placement.
Theme switches retain variant and facing metadata; themes without matching art
use their base symbol until the map returns to Low Poly.

### Low Poly furniture collections

`domestic/`, `homestead/`, `commerce/`, `garrison/`, `stairs/` and `ladders/` keep
their Low Poly sprites in `fabled-lands-3d/`, rendered with the KayKit sets' camera
and light so a room reads as one set of 3D models: orthographic oblique, tilt .55
(heights rise up the screen, the floor is not foreshortened), light
`[-0.35, 0.88, -0.32]` from the upper left with self-shadow, contact shading and a
soft ground shadow, rendered at 2x and halved. Every base, rustic and stocked
variant has eight 45° facings with the file names the vector styles use.

Each PNG is 256 px wide. Its bottom 256 px square is the object's physical canvas
on the floor (`canvasFeet` in the manifest), so the footprint stays on its grid
cells; rows above it hold the object's height (`heightRowsPx`). The runtime draws
that square on the footprint and lets the rest stand above it
(`HexAtlasDungeonKit.raisedSprite`, used by `getLandmarkIconPlacement`, flattened
floors in `dungeon-building-layers.js` and settlement stamps). Recessed models
(stairs and ladders going down) are masked to their opening.

```
blender --background -t 4 --python scripts/export-lowpoly-furniture-meshes-blender.py -- --collection domestic --out /tmp/meshes
python3 scripts/bake-lowpoly-furniture-oblique.py --meshes /tmp/meshes --collection domestic
```

The export runs the collection's own Blender builder and captures each model's
evaluated triangles and material colours; the bake (numpy and Pillow) writes the
PNGs and the manifest's `camera`, `projection`, `models` and `images`.

### Terrain ground palette

`fabled/terrain/` adds 134 self-contained SVG images. Ground variants recolor only
the soil material of four existing dirt floor PNGs; the original rocks and plants
keep their colors. Coast, deep water, Water, Lava and Void use native flat surfaces
without dirt, rocks or weeds. Bare terrains use bare ground; explicitly named
forest variants retain vegetation, including Arctic Forest.
The 14 built-in world terrain types plus Water, Lava and Void each receive a
base and three surface variants, plus all 66 named Low Poly world variants under
their existing IDs. Together with the original six floor families, this gives
23 materials and 158 looks. World hex silhouettes are not used as dungeon floors.
Classic and Old-School retain their flat presentation; Painted keeps its existing
painted renderer. Theme switches preserve the floor's variant choice.

Rebuild after changing the built-in terrain/Low Poly variant catalog:

```
node scripts/build-dungeon-terrain-floors.mjs
```

`fabled/terrain/manifest.json` records each ground PNG/mask source, SHA-256 hashes
and palette color, and distinguishes native flat surfaces. SVGs embed their PNG
data and have no external image dependencies. The normal image cache handles
loading. User-uploaded floors keep precedence; the generator does not replace
custom tile definitions.

The four checked-in grayscale masks in `fabled/terrain-source-masks/` use the
original mesh's two ground triangles. Plants and rocks remain black in the mask;
soil is white, with the original projection, visibility and antialiasing. Its
manifest records model/texture hashes and the original soil UV palette color.
Rebuild masks only when changing the source model renders (Pillow/numpy required):

```
python3 scripts/build-dungeon-floor-masks.py /path/to/KayKit_DungeonRemastered_1.1_FREE
```

Wall construction now uses original procedural meshes: dungeon/castle masonry,
cottage planks, dark cottage planks, and pointed palisade logs. Individual blocks,
planks, rails, junction posts and door leaves are real geometry with vertex
colors, not sampled floor textures. The runtime generates meshes from editable
wall paths and projects them with WebGL, with a shaded-triangle Canvas fallback.
Custom-colored or special walls retain vector rendering. The live preview
gallery uses the same wall renderer.

Palisade stakes retain their sharp cut tips. Their connectors are separate,
wider flat-topped timber posts with low base collars. Endpoints and junctions
share this construction; diagonal posts remain inside the doorway clearance.

`wall-models/` includes 16 editable OBJ files and paired MTL files, covering
straight walls, corners, T-junctions and crossings. These are HEXatlas-authored
models, separate from the KayKit pack; provenance is recorded in
`wall-models/SOURCE.txt`. Rebuild them with:

```
node scripts/export-dungeon-wall-models.mjs
```

This is a fixed 2.5D presentation, not an orbitable 3D camera. Painted's full
scene projection and a denser, room-aware furniture planner remain separate
follow-up work; the current floor grid is square in every theme.

## Painted pixel props

Painted (stored id `fantasy`) props are 64px pixel art, per the theme contract in
`docs/DUNGEON_KIT_INTEGRATION.md`. `scripts/build-dungeon-model-pixels.py`
converts each of the eight Low Poly mesh bakes of every catalog model (base,
variants and withdrawn forge/fire looks) into a crisp-edged SVG of pixel runs.
It shares one palette across a model's facings and never rotates a flat image.
Inputs are the KayKit CC0 bakes and the original workshop, stone and crypt bakes
(see `ASSET_RIGHTS_REVIEW.md`). `fantasy-model-pixels/manifest.json` records each
source and output hash; `validation.json` and `contacts/` hold the review evidence.

```
python3 scripts/build-dungeon-model-pixels.py
node scripts/build-dungeon-painted-sprite-index.mjs
```

Run the index script after any sprite change. It writes
`fantasy-model-pixels/sprite-index.json` from the files on disk and the matching
generated block in `dungeon-kit.js`. `objectSource('fantasy', …)` uses that block
to pick the first shipped tier per model and facing: the pixel sprite, then the
painted-wash bake in `fantasy-model-painted/`, then the raw Low Poly bake, then
an inline symbol. It never probes the network or names a missing file.
`fantasy-model-painted/` is an unadopted painterly experiment (chest, coffin and
stone doorway only, from `scripts/build-dungeon-model-painted.py`). The older
code-drawn `fantasy-pixels/` set is no longer referenced at runtime.
`scripts/test-dungeon-kit.mjs` fails if the index is stale or if any catalog
look falls back off its own pixel sprite.

## Hearths and workshop furnishings

`workshop/` adds thirteen models with 104 facings: cottage, great-hall and
empty fireplaces, all without mantels; open, hooded and cold forges; an anvil with a tools
variant; separate small fire, low fire and embers; and a hammer and tongs.
All are authored geometry with the existing KayKit palette/style as reference;
no KayKit mesh geometry is embedded. Editable OBJ/MTL files, a palette texture, source hashes,
and provenance accompany the rendered sprites. See `workshop/PROVENANCE.md`.

The refined models use arched hearth openings, beveled stone courses, faceted
logs, volumetric fire, coal pieces, accordion bellows and tapered anvil horns
joined continuously to the flat striking face.
`scripts/render-dungeon-workshop.py` bakes directional shadows, contact shading
and soft ground shadows with the original fixed camera. Only explicitly marked
fire palette colors receive emissive brightness. Palette dimensions expand with
the shade count, avoiding clipped colors. Runtime still loads cached PNGs.

Regenerate with:

```
python3 scripts/build-dungeon-workshop.py /path/to/KayKit_DungeonRemastered_1.1_FREE
```

The normal Object picker exposes Fireplace, Anvil,
Smithing hammer and Smithing tongs, their variants,
and eight rotation angles. Classic and Old-School use flat authored symbols;
Painted uses the same modest lift as other native furnishings. Variant metadata
is retained when changing themes. New houses, taverns and inns include a hearth;
new smithies include an anvil. Existing layouts are not regenerated.

Forge and Fire / embers are withdrawn from the picker, previews and new
interior generation pending further art work. Their exported files and source
definitions remain available to existing saved maps, including theme changes.
Hearths and forges contain no baked-in flames. Existing fire effect objects can
share exact coordinates with ordinary objects; they use stable ID storage keys,
render above furnishings, and can be moved/deleted independently. Full-map and
fallback cloud save/load preserve them without replacing a co-located hearth.
