1180 lines
74 KiB
Markdown
Executable File
1180 lines
74 KiB
Markdown
Executable File
# The Last Hearth — woodland travelers and botanical pixel catalogue
|
||
|
||
## Authoritative fauna-23 / catalogue-100 art contract (2026-11-03)
|
||
|
||
This section supersedes earlier catalogue counts and package totals, not the
|
||
character-layer or legacy OBJ contracts. Original MIT procedural drawings and
|
||
meshes only: no downloaded assets, fonts, libraries or engine launch. Runtime
|
||
wildlife integration remains the Lua owner's responsibility; these generators
|
||
do not register monsters, change spawning, collision boxes or gameplay.
|
||
|
||
### Exact animated fauna handoff
|
||
|
||
[The fauna generator](../scripts/fauna-art.mjs) exports frozen `FAUNA_NAMES`,
|
||
`FAUNA_ANIMATIONS`, `FAUNA_ART_MANIFEST`, `FAUNA_MATERIALS` and pure methods
|
||
`faunaB3D(id)`, `faunaTexture(id)`, `faunaBounds(id)`, `faunaPortraitSheet(frame=9)`.
|
||
Unknown IDs and frames fail closed. Every result is deterministic and fresh.
|
||
Exactly these **23** IDs, in manifest/portrait row-major order:
|
||
|
||
hare, deer, fox, rabbit, red_squirrel, bank_vole, wood_mouse, hedgehog, badger,
|
||
roe_deer, stoat, weasel, otter, robin, blackbird, blue_tit, barn_owl, mallard,
|
||
common_frog, smooth_newt, brown_trout, bumblebee, earthworm.
|
||
|
||
Each has **hearth_fauna_<id>.b3d** and
|
||
**hearth_fauna_<id>.png**; the latter is an opaque **64×64** nearest-neighbor
|
||
semantic atlas. These 23 PNGs are deliberately **not** worldArt tiles. Old
|
||
hearth_hare.obj, hearth_fox.obj, hearth_deer.obj and their textures remain byte
|
||
compatible and generated; runtime can migrate to the new names independently.
|
||
|
||
Every model has one root, one textured mesh, outward-wound flat-normal solids,
|
||
half-texel inset UVs, normalized rigid bone weights, no scale animation and
|
||
**100 real skeletal frames at 24 FPS**. B3D key indices 1–100 map to Luanti
|
||
frames 0–99. `visual_size={x=1,y=1}`, **10 model units/node**, feet/lowest bind
|
||
surface at **Y=0**, forward +Z. `faunaBounds(id).min/max` are authoritative
|
||
**bind-mesh node bounds**, not guessed collision or conservative animated bounds.
|
||
They are also serialized per animal; do not infer scale from portrait cells.
|
||
|
||
Bind bounds in nodes below are rounded to five decimals for review; the exported
|
||
values and generated manifest retain full precision. All minimum Y values are 0.
|
||
|
||
| ID | Minimum X, Y, Z | Maximum X, Y, Z |
|
||
| --- | --- | --- |
|
||
| hare | -0.13750, 0, -0.34150 | 0.13750, 0.81400, 0.37004 |
|
||
| deer | -0.31625, 0, -0.68300 | 0.31625, 1.59470, 0.65081 |
|
||
| fox | -0.15000, 0, -0.86240 | 0.15000, 0.68560, 0.50280 |
|
||
| rabbit | -0.15400, 0, -0.25954 | 0.15400, 0.55090, 0.31371 |
|
||
| red_squirrel | -0.07500, 0, -0.46744 | 0.07500, 0.59240, 0.24266 |
|
||
| bank_vole | -0.07000, 0, -0.17039 | 0.07000, 0.19600, 0.17507 |
|
||
| wood_mouse | -0.06000, 0, -0.31045 | 0.06000, 0.23860, 0.16250 |
|
||
| hedgehog | -0.11000, 0, -0.21173 | 0.11000, 0.25000, 0.23755 |
|
||
| badger | -0.19000, 0, -0.45078 | 0.19000, 0.49240, 0.47589 |
|
||
| roe_deer | -0.22375, 0, -0.49176 | 0.22375, 1.22850, 0.48443 |
|
||
| stoat | -0.06000, 0, -0.45146 | 0.06000, 0.24620, 0.26954 |
|
||
| weasel | -0.04250, 0, -0.26846 | 0.04250, 0.18080, 0.19111 |
|
||
| otter | -0.12500, 0, -0.70711 | 0.12500, 0.38840, 0.39778 |
|
||
| robin | -0.16460, 0, -0.21895 | 0.16460, 0.37210, 0.17388 |
|
||
| blackbird | -0.18440, 0, -0.30653 | 0.18440, 0.41750, 0.20140 |
|
||
| blue_tit | -0.12500, 0, -0.17516 | 0.12500, 0.31670, 0.13705 |
|
||
| barn_owl | -0.28340, 0, -0.27369 | 0.28340, 0.64340, 0.27500 |
|
||
| mallard | -0.27350, 0, -0.43790 | 0.27350, 0.47040, 0.25550 |
|
||
| common_frog | -0.09900, 0, -0.15026 | 0.09900, 0.14750, 0.22272 |
|
||
| smooth_newt | -0.04000, 0, -0.34231 | 0.04000, 0.10140, 0.16295 |
|
||
| brown_trout | -0.10450, 0, -0.35520 | 0.10450, 0.30450, 0.24000 |
|
||
| bumblebee | -0.14400, 0, -0.06500 | 0.14400, 0.12250, 0.10640 |
|
||
| earthworm | -0.02000, 0, -0.19127 | 0.02000, 0.04000, 0.19127 |
|
||
|
||
| Clip | Inclusive engine frames | Authored action |
|
||
| --- | --- | --- |
|
||
| idle | 0–19 | breathing, looking, ear/tail attention; segmented worm motion |
|
||
| locomotion | 20–39 | linked upper/lower limb strides, tail follow-through; worm joint wave |
|
||
| feed | 40–59 | head/neck dip and bird peck; small head/segment sampling motion |
|
||
| flight_swim | 60–79 | mirrored wing flaps with secondary flight feathers; aquatic tail beats/fins |
|
||
| rest | 80–99 | settled head/limbs, low-amplitude breathing and tail/segment movement |
|
||
|
||
All five ranges loop with matching endpoint poses. No engine-side fake motion
|
||
is needed. The shared `flight_swim` range on non-flying terrestrial mammals
|
||
is an **alert looking reserve**, not invented flight/swimming. The rest range
|
||
is a settled standing/perched posture, **not a speculative lying-down or curled
|
||
skeletal deformation**. Frog/newt limbs paddle; fish have a hinged caudal fan,
|
||
paired pectorals and dorsal fin; bees have six legs and thoracic wings; the
|
||
earthworm has twelve serially parented body joints. No bones are driven merely
|
||
because another species has similarly named appendages.
|
||
|
||
Hare ears and hind feet, compact rabbit, squirrel's raised brush, vole's short
|
||
tail versus mouse's long tail, hedgehog spine blocks, broad badger, tall antlered
|
||
deer versus short roe antlers, slender stoat/weasel, low otter with cream muzzle,
|
||
robin breast, blackbird bill, blue tit cap, owl facial discs, duck neck/webbed
|
||
feet/speculum and amphibian low profiles are authored species landmarks, not a
|
||
single body recoloured. The restrained coat/cream/earth/mineral palette matches
|
||
the Minecraft/Vintage Story-like block world without copying either game's art.
|
||
|
||
The sixteen **16×16** material patches occupy four row-major columns:
|
||
|
||
| Row | 0 | 1 | 2 | 3 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 0 | coat | belly | face | ear |
|
||
| 1 | foot | tail | wing | eye |
|
||
| 2 | nose | stripe | feather | beak |
|
||
| 3 | fin | spot | horn | membrane |
|
||
|
||
Badger face stripes, cream otter underside, stoat black tail tip, fox cream tip,
|
||
bee bands, trout spots, wing bars and feather veins are semantic markings, not
|
||
whole-model tints. All atlas texels have alpha 255 (solid low-poly appendages).
|
||
|
||
`generateAssets()` writes **fauna-art.json** at package root: format 1,
|
||
`animations`, `animals` (ID, exact filenames, dimensions, units, visual size,
|
||
bind bounds, posture, clips, MIT provenance), and `portraitSheet` metadata.
|
||
Every output uses the existing guarded staging writer and package hash inventory.
|
||
The portrait path inside staging is
|
||
**mods/hearth/textures/hearth_fauna_contact_sheet.png**: **512×768**, four
|
||
columns of 128px cells, 23 animals plus one paper cell, frame 9. It is an actual
|
||
CPU triangle rasterization of skinned geometry with atlas sampling and flat
|
||
lighting, **not a texture swatch, font illustration or engine screenshot**.
|
||
`faunaPortraitSheet(65)` previews the same geometry's flight/swim pose. Each cell
|
||
fits its own posed bounds, so cells are an identity review, not relative scale.
|
||
|
||
### Catalogue 100 and three additional cube-tree material families
|
||
|
||
The JavaScript art catalogue now matches all **100 exact IDs and Latin names**
|
||
in the read-only Lua BOTANY catalogue. Every taxon has active and dormant 32px
|
||
sprites: **200** provenance records in **botany-art.json**, each including
|
||
`botanicalLatin`, group, morphology, state, filename, dimensions and MIT author.
|
||
Group counts: trees 13, shrubs 14, grasses 10, herbs 22, ferns_mosses 11,
|
||
fungi 10, aquatic 10, lichens 10. The twenty additions are:
|
||
|
||
- crab_apple — Malus sylvestris; wild_cherry — Prunus avium;
|
||
field_maple — Acer campestre.
|
||
- honeysuckle — Lonicera periclymenum; ivy — Hedera helix;
|
||
gorse — Ulex europaeus; broom — Cytisus scoparius.
|
||
- dandelion — Taraxacum officinale; daisy — Bellis perennis;
|
||
red_clover — Trifolium pratense; birdsfoot_trefoil — Lotus corniculatus;
|
||
ribwort_plantain — Plantago lanceolata; selfheal — Prunella vulgaris;
|
||
knapweed — Centaurea nigra; oxeye_daisy — Leucanthemum vulgare;
|
||
field_scabious — Knautia arvensis; teasel — Dipsacus fullonum;
|
||
cow_parsley — Anthriscus sylvestris; garlic_mustard — Alliaria petiolata.
|
||
- holly_fern — Polystichum setiferum (catalogue contract ID; soft shield-fern).
|
||
|
||
Original 80 drawings and approved leaf PNG bytes are retained. New silhouettes
|
||
have distinct flowerheads, twining honey tubes, climbing ivy rootlets, gorse
|
||
spines, broom switches, ligulate dandelion rays, trifoliate clovers, plantain's
|
||
anther ring, teasel cones, compound parsley umbels, brassica siliques and forked
|
||
fern pinnae. Dormant specimens lose flowers and fruit, keeping species-specific
|
||
dry structure. Existing runtime pixel-wind plant waving is unchanged.
|
||
|
||
All thirteen cube-tree IDs: oak, birch, pine, willow, alder, beech, aspen, rowan,
|
||
hazel, spruce, crab_apple, wild_cherry, field_maple. Each has log_top, log_side,
|
||
leaves and planks. Deciduous species also have leaves_spring, leaves_autumn and
|
||
leaves_winter. Cherry has horizontal lenticels; crab apple fine fissures; maple
|
||
interrupted cork diamonds. **46** compatibility bark/leaf atlases cover all
|
||
species/seasons, including underscore IDs parsed by the anchored suffix contract
|
||
`/_atlas(?:_(spring|autumn|winter))?$/`, never positional underscore splitting.
|
||
No new unused irregular-tree OBJ meshes are generated; the 29 legacy flora OBJ
|
||
exports and the 23 old species-stage meshes remain frozen.
|
||
|
||
The world contact sheet now has **421 tiles**, **512×864**, **16 columns**,
|
||
sorted filenames as the row-major legend, height **ceil(count/16)*32**. The
|
||
bounded **1–512** tile guard remains intact. Its 61 additions are 40 botanical
|
||
sprites plus 21 cube-tree materials/seasonal leaves. Fauna never consumes those
|
||
cells. The two-swatch minimal fixture closes at **675 PNGs and 63 models**:
|
||
**37 OBJ + 26 B3D** (3 players and 23 fauna), with exact manifest-derived
|
||
inventories and package-integrity verification, not loose minimum-count checks.
|
||
|
||
Validation is Node-only: independent B3D chunk parsing, winding, normals, UVs,
|
||
weights, every keyed pose, forward-kinematic displacement for all five clips,
|
||
loop seams, species landmarks, encoded texture pixels, botanical catalogue
|
||
equality, unique silhouettes, approved old leaf hashes, package bytes and output
|
||
link refusal. No engine render, performance, live animation or spawn claim.
|
||
|
||
Final focused run: **60 passed, 0 failed, 0 cancelled, 0 skipped**, Node
|
||
**24.22.0**, **160.80 seconds**, exit 0 and empty stderr. Suites: fauna art,
|
||
world art, flora art, existing assets and character art. Both actual skinned
|
||
mesh sheets (idle frame 9 and flight/swim frame 65) and the 421-tile world sheet
|
||
were visually inspected outside the source tree. No manual binary files added.
|
||
CodeGuard boundaries: no credentials or certificates introduced; SHA256 is used
|
||
only for deterministic integrity checks; positive ID/frame validation, bounded
|
||
allocations and existing symlink/junction-safe staging writes are retained.
|
||
Optional audio codec decode/regeneration was not configured; existing Ogg
|
||
framing/CRC/hash checks passed. Directory-junction refusal passed; the existing
|
||
file-symlink subcase lacked Windows symlink privilege.
|
||
|
||
## Authoritative character art contract (2026-11-03)
|
||
|
||
**Revision note (2026-11-04):** the 2026-11-03 option counts and rig/mesh-size
|
||
figures in this section are superseded by the expanded character catalogue below.
|
||
|
||
### Expanded woodland traveler update (2026-11-04)
|
||
|
||
The current public body allowlist is `compact`, `lean`, `balanced`, `broad`,
|
||
`sturdy`; all remain cosmetic, equal-height silhouettes using the same rig and
|
||
collision. Added original face linework (`lined`), hair styles (`curly`,
|
||
`topknot`, `undercut`), facial hair (`goatee`, `braided_beard`), garments
|
||
(`hooded_cloak`, `travel_coat`, `wool_mantle`), accents (`satchel`, `tool_belt`,
|
||
`pauldrons`, `hood`), and a `birthmark`. The current generated manifest is 258
|
||
distinct validated filenames (including transparent `none` variants), and the
|
||
expanded player B3D contains 384 vertices and 192 triangles. The model adds an
|
||
original draped shoulder mantle, belt, back satchel, and projecting nose/ears;
|
||
no player mechanics or tool attachment names are changed. Current Lua and art
|
||
builders must use the exact texture order implemented by `C.preview()` and
|
||
`characterLayers()`; hood accents intentionally overlay hair. See
|
||
[character-creator.md](character-creator.md) for the current option list.
|
||
|
||
This section supersedes earlier **player** appearance and package-total statements
|
||
below; botanical art itself is unchanged. Character work owns only
|
||
[scripts/media.mjs](../scripts/media.mjs),
|
||
[scripts/assets.mjs](../scripts/assets.mjs),
|
||
[scripts/character-art.mjs](../scripts/character-art.mjs),
|
||
[tests/character-art.test.mjs](../tests/character-art.test.mjs),
|
||
[tests/assets.test.mjs](../tests/assets.test.mjs) and this document.
|
||
No Lua, gameplay, collision, camera, engine, dependency or source-media edits.
|
||
All drawings are original procedural MIT work, with no downloaded art, network,
|
||
image service, third-party model, global tint or extra character-layer entities.
|
||
|
||
### Woodland traveler grammar and unchanged UV contract
|
||
|
||
Quiet mineral cloth, undyed linen, worn walnut leather and natural hair replace
|
||
the old flat blockperson skin. Small collars, rolled cuffs, stitched hems,
|
||
plackets, leather panels, coat openings, boot laces/soles and sparse linework give
|
||
the traveler a grounded, practical silhouette. Faces remain readable at eight
|
||
pixels: two iris landmarks, restrained brows, nose and mouth, no oversized white
|
||
cartoon eyes. Skin choices are independent of facial features and clothes.
|
||
|
||
Every runtime layer is **64×64 straight RGBA**, nearest-neighbor, with **0/255
|
||
alpha** and zero RGB behind transparent pixels. The atlas is still eight columns
|
||
of eight-pixel tiles, with the original half-texel UV inset. Tile coordinates:
|
||
|
||
| Row | Column 0 | Column 1 | Column 2 |
|
||
| --- | --- | --- | --- |
|
||
| 0 | face | hair (head back/top) | side (both head sides) |
|
||
| 1 | tunic (torso front/ends/sides) | back | sleeve |
|
||
| 2 | trousers | boot | skin (hands/head underside) |
|
||
| 3 | metal | wood | wrap |
|
||
|
||
The six base skins are opaque over **all 4096 pixels**, including a linen
|
||
underlayer and fallback trousers/boots. Skin tone changes exactly the four
|
||
exposed head/hand tiles, **256 pixels**, not the clothing or tool swatches.
|
||
Face overlays are skin-neutral lines, not light-colored face masks. Hair paints
|
||
the scalp, sides and hairline; long hair and braids also cover upper back and
|
||
shoulder pixels. Garments and accents paint only torso/back/sleeve tiles;
|
||
trousers and boots paint only their own tiles. No overlay touches tool tiles or
|
||
unused atlas space. The former metal/wood/wrap pixels are **byte-exact** in all
|
||
base skins and in the default **hearth_person.png**, so existing held-tool OBJ
|
||
materials keep their appearance. Existing tool geometry and icon art are retained.
|
||
|
||
### Exact IDs and generated layer manifest
|
||
|
||
`CHARACTER_OPTIONS` exports these frozen allowlists, without aliases:
|
||
|
||
| Group | IDs |
|
||
| --- | --- |
|
||
| bodies | compact, balanced, broad |
|
||
| skin | porcelain, sand, honey, olive, umber, ebony |
|
||
| faces | calm, angular, soft, weathered |
|
||
| eyes | hazel, brown, green, blue, grey |
|
||
| hairStyles | cropped, sidepart, wavy, braids, long, shaved, none |
|
||
| hairColors | ash, flax, chestnut, auburn, black, silver |
|
||
| facialHair | none, stubble, moustache, short_beard, full_beard |
|
||
| garments | tunic, workshirt, jerkin, coat |
|
||
| clothColors | sage, teal, slate, mauve, ochre, rust, linen, charcoal |
|
||
| trousers | earth, slate, moss, charcoal |
|
||
| boots | walnut, black, tan |
|
||
| accents | none, scarf, apron, sash, vest |
|
||
| details | none, freckles, scar, freckles_scar |
|
||
|
||
The **170** entries in `CHARACTER_ART_MANIFEST` use these exact filename
|
||
templates. Each row includes every Cartesian product, even transparent `none`:
|
||
|
||
| Filename template | Count |
|
||
| --- | ---: |
|
||
| hearth_char_skin_<skin>.png | 6 |
|
||
| hearth_char_face_<face>.png | 4 |
|
||
| hearth_char_eyes_<eyes>.png | 5 |
|
||
| hearth_char_hair_<style>_<hairColor>.png | 42 |
|
||
| hearth_char_beard_<style>_<hairColor>.png | 30 |
|
||
| hearth_char_garment_<style>_<cloth>.png | 32 |
|
||
| hearth_char_trousers_<trousers>.png | 4 |
|
||
| hearth_char_boots_<boots>.png | 3 |
|
||
| hearth_char_accent_<style>_<accentColor>.png | 40 |
|
||
| hearth_char_detail_<detail>.png | 4 |
|
||
|
||
Accent colors use the same eight `clothColors`; beard colors use the same six
|
||
`hairColors`. Exactly **21** images are intentionally empty: six hair-none, six
|
||
beard-none, eight accent-none and one detail-none. All **149 nonempty layers**
|
||
are distinct; equivalent empty-none color variants are deliberately identical.
|
||
Each manifest record includes `filename`, `kind`, `id`, `color` (null if absent),
|
||
`width`, `height`, `empty`, `license`, `author`.
|
||
|
||
`generateAssets()` writes the layers into the staged texture directory, and a
|
||
root-level **character-art.json** provenance document with `format:1`, `options`,
|
||
`meshes` and `images`. All outputs use the existing guarded `writeAsset`,
|
||
`relativeName` and `safePath`; no source-worktree assets are generated.
|
||
The document and every layer/mesh are included in the existing SHA256 package
|
||
inventory. It adds **171 PNGs** (170 layers + portrait sheet) and **two B3Ds**
|
||
to previous totals: **576 fixed PNGs**, plus source-dependent compatibility
|
||
swatches, **37 OBJ + three B3D**. The two-swatch fixture totals **578 PNGs**.
|
||
|
||
### Pure methods and Lua composition handoff
|
||
|
||
[The renderer module](../scripts/character-art.mjs) exports:
|
||
|
||
- `characterLayer(filename)` → fresh `{width:64,height:64,data:Uint8Array}`;
|
||
unknown filenames fail closed. `characterArt()` → deterministic filename Map
|
||
of all 170 fresh images.
|
||
- `characterLayers(options={})` → ten exact filenames in compositing order;
|
||
`characterTexture(options={})` → those names joined by `^`, ready for one
|
||
Luanti mesh texture. Only allowlisted fields/values are accepted; no path,
|
||
prototype key, inherited options object or arbitrary file input.
|
||
- `compositeCharacter(options={})` → pure fresh opaque 64×64 RGBA atlas using
|
||
the same crisp source-over order. No I/O or global recoloring.
|
||
- `characterMesh(body='balanced')` → exact mesh filename.
|
||
- `portraitSheet()` → opaque **512×64** RGBA contact sheet, eight assembled
|
||
64px portrait cells. `CHARACTER_PORTRAITS` gives the exact left-to-right
|
||
selections. Portraits sample actual composited face, tunic, sleeve, skin,
|
||
trousers and boot tiles; they are paper dolls, **not atlas swatches or 3D
|
||
engine-render screenshots**. Staging writes **hearth_character_contact_sheet.png**.
|
||
|
||
Selection fields and defaults (`DEFAULT_CHARACTER`): `body:balanced`,
|
||
`skin:sand`, `face:calm`, `eyes:hazel`, `hair:sidepart`, `hairColor:chestnut`,
|
||
`beard:none`, `garment:tunic`, `cloth:teal`, `trousers:earth`, `boots:walnut`,
|
||
`accent:scarf`, `accentColor:sage`, `detail:none`. The old **hearth_person.png**
|
||
is now this complete default composite, not a removed compatibility material.
|
||
|
||
**Required left-to-right texture order:** skin → face → eyes → garment →
|
||
trousers → boots → accent → hair → beard → detail. In filename form:
|
||
hearth_char_skin_<skin>.png ^ hearth_char_face_<face>.png ^
|
||
hearth_char_eyes_<eyes>.png ^ hearth_char_garment_<garment>_<cloth>.png ^
|
||
hearth_char_trousers_<trousers>.png ^ hearth_char_boots_<boots>.png ^
|
||
hearth_char_accent_<accent>_<accentColor>.png ^
|
||
hearth_char_hair_<hair>_<hairColor>.png ^
|
||
hearth_char_beard_<beard>_<hairColor>.png ^ hearth_char_detail_<detail>.png.
|
||
Remove the displayed spaces around carets when composing. Clothing **must precede
|
||
hair**, or long locks disappear under the upper-back/shoulder garment pixels.
|
||
Runtime appearance UI, persistence and attachment application belong to the Lua
|
||
owner; this work only supplies assets and the exact builder contract.
|
||
|
||
### Mesh profiles, rig and attachment invariants
|
||
|
||
`playerB3D(profile='balanced')` accepts exactly `compact`, `balanced`, `broad`.
|
||
Mesh filenames: **hearth_person.b3d** (balanced), **hearth_person_compact.b3d**,
|
||
**hearth_person_broad.b3d**. Each retains one textured mesh, **240 vertices,
|
||
120 outward-wound triangles**, rigid weight 1 per vertex, original UV assignments
|
||
and **17.5 model-unit height** (feet 0, head top 17.5 = 1.75 nodes). No collision,
|
||
camera/eye-height, reach, global scale or root-motion change.
|
||
|
||
Balanced head changes from X ±1.75 / Z ±1.5 to **X ±1.6 / Z ±1.4**;
|
||
Y remains 13.5–17.5. Torso shoulder half-width 2.2 → **2.15**, half-depth
|
||
1.25 → **1.18**; bottom waist is 86% width/depth. Arms half-width .85 → **.78**,
|
||
depth .9 → **.84**, tapering to 90% width/95% depth at the wrist. Legs half-width
|
||
.95 → **.91**, depth 1 → **.95**, tapering to 90% width/94% depth at the ankle.
|
||
Normals are recomputed from actual tapered face planes. Hands, boots and all
|
||
vertical extents stay unchanged. These are subtle tapered ten-part solids, not
|
||
additional joints or extra geometry.
|
||
|
||
Compact/broad multiply torso, upper-arm and leg widths by **.92 / 1.08**.
|
||
Broad uses a slightly fuller 90% waist width; all waist depths stay 86%.
|
||
Head, hands, boots, depths, height and rig pivots remain identical across profiles.
|
||
No profile moves an attachment hand to accommodate a wider body.
|
||
|
||
All nine bones, hierarchy and bind positions remain exactly unchanged: `Root`,
|
||
`Body`, `Head`, `Arm_Right`, `Hand_Right`, `Arm_Left`, `Hand_Left`, `Leg_Right`,
|
||
`Leg_Left`. Arm pivots X ±3.1/Y13; hands local [0,-4.8,0] under their arms.
|
||
All bones retain the exact 100 keyed poses, B3D frames 1–100 / engine 0–99.
|
||
Clips unchanged: idle 0–19@12, walk 20–39@24, run 40–59@32, strike 60–69@28,
|
||
guard 70–79@12, swim 80–99@20. Held grip stays at `Hand_Right`, local origin,
|
||
rotation {x=90,y=0,z=0}; the shared tool atlas is retained.
|
||
|
||
### Validation and security boundary
|
||
|
||
CodeGuard was read and applied: no credentials, secrets, certificates or crypto
|
||
protocols introduced; existing SHA256 stays integrity-only. Positive enum/key
|
||
validation, bounded allocations, pure offline drawing and existing link/junction
|
||
write refusal are retained. No shell/eval, new dependencies or downloads.
|
||
Tests independently parse generated PNG CRC/data and all three B3D chunk trees,
|
||
check every exact option, semantic mask, all 170 encoded layers, 149 nonempty
|
||
distinct images, meaningful skin differences, 21 intentional empty layers,
|
||
paper-doll samples, deterministic regeneration/package hashes, rig/keys/weights,
|
||
head/waist geometry and byte-exact held-tool swatches.
|
||
No engine launch, client rendering, FPS, collision-course or live-world claim.
|
||
|
||
Final focused character + asset run: **24 passed, 0 failed, 0 cancelled,
|
||
0 skipped**, exit 0, empty stderr, **65.60 seconds**, Windows Node **24.22.0**.
|
||
All five JavaScript syntax checks passed. All six owned files were read back
|
||
from disk for trailing whitespace and completion; the eight-portrait sheet was
|
||
visually reviewed. Optional full audio codec regeneration was not configured;
|
||
existing audio framing/CRC/hash checks passed. Directory-junction refusal passed;
|
||
the file-symlink subcase could not run without Windows symlink privilege.
|
||
Git diff checking was unavailable from this subprocess environment, so no Git
|
||
diff-clean claim is made. Runtime Lua appearance integration is not tested here.
|
||
|
||
## Authoritative catalogue-80 art contract (2026-11-02)
|
||
|
||
This section supersedes all folded historical counts and mesh integration advice.
|
||
Only [scripts/world-art.mjs](../scripts/world-art.mjs),
|
||
[scripts/flora-art.mjs](../scripts/flora-art.mjs),
|
||
[scripts/assets.mjs](../scripts/assets.mjs),
|
||
[tests/world-art.test.mjs](../tests/world-art.test.mjs),
|
||
[tests/flora-art.test.mjs](../tests/flora-art.test.mjs) and this document are owned.
|
||
The actual [botany catalogue](../mods/hearth/botany.lua),
|
||
[biomes](../mods/hearth/biomes.lua) and
|
||
[diversity](../mods/hearth/diversity.lua) were read, never edited. No engine tests
|
||
or client launch are part of this art work; runtime cube/season closure belongs
|
||
to the content and seasons owners.
|
||
|
||
### Original botanical grammar
|
||
|
||
All art is original procedural drawing under the [MIT license](../LICENSE.txt).
|
||
No copyrighted third-party images, downloads, fonts, random noise, dependencies
|
||
or generated-image service. Every specimen is drawn at **32×32**, straight RGBA,
|
||
nearest-neighbour with **crisp 0/255 alpha**, zero hidden RGB and one-pixel empty
|
||
borders. Small natural sage, bark, buff and mineral palettes dominate; flowers,
|
||
fruit and reproductive details are restrained accents. These are stylized field
|
||
journal specimens, **not identification, toxicity or eating advice**. No plate,
|
||
meal, bite, food badge or edibility permission is depicted, including toxic taxa.
|
||
|
||
- **Trees**: oak rounded lobes and stalked acorns; birch serrate diamonds and
|
||
catkins; pine long paired needle sprays; willow pendant narrow blades; alder
|
||
round notched leaves and conelets; beech oval parallel-veined leaves; aspen
|
||
scalloped round leaves and petioles; rowan pinnate leaflets and clustered pomes;
|
||
hazel heart-shaped leaves and husked nuts; spruce short radial needles and long
|
||
pendant cones. All ten get inventory/fieldguide specimens independently of cubes.
|
||
- **Shrubs**: low branching twig clusters, arching thorny bramble/rose canes,
|
||
upright raspberry, opposite compound elder, rigid blackthorn, lobed hawthorn,
|
||
spiny evergreen holly, juniper needle whorls, three-lobed guelder rose. Fruit
|
||
attachment, clusters and leaves differ, not just hue.
|
||
- **Grasses and graminoids**: dense tussocks below diagnostic panicles/spikes;
|
||
sedge has angular triangular culm faces, reedmace cylindrical brown heads,
|
||
cock's-foot one-sided clumps, timothy a tight long spike, vernal-grass short
|
||
golden ovals, Yorkshire-fog soft pink panicles, hair-grass airy rays, reed tall
|
||
jointed stems and broad blades, rush lateral inflorescences on smooth culms.
|
||
- **Herbs**: trifoliate clover and stolon, serrate opposite nettle, heather scale
|
||
sprays, one-sided bluebell bells, yarrow flat corymb and divided leaves,
|
||
foxglove tubular bells and rosette, meadowsweet cream froth, garlic star umbel,
|
||
cowslip nodding yellow tubes, anemone single flower over divided-leaf whorl.
|
||
- **Ferns/mosses**: triangular three-part bracken, fine lady-fern, robust male-fern,
|
||
entire hart's-tongue straps, polypody pinnae/sori versus low moss cushions:
|
||
sphagnum capitula, haircap shoots/capsules, plait-moss feathers, swept fork-moss
|
||
leaves, broad-leaved star-moss shoots. They do not share a generic bush glyph.
|
||
- **Fungi**: warted fly-agaric, scabrous birch-bolete stem, chestnut bay cushion,
|
||
folded chanterelle funnel, lilac blewit, shaggy cylindrical inkcap, banded turkey
|
||
tail shelves, lateral oyster fans, tufted ringed honey caps, kidney polypore.
|
||
No invented puffball is substituted: none occurs among these ten catalogue IDs.
|
||
- **Aquatic**: duckweed small paired fronds/roots; **round or oval notched lily
|
||
pads**, yellow cup versus many white tepals (not lotus horns); crowfoot divided
|
||
foliage, mint globe, forget-me-not curled cyme, brooklime raceme, starwort
|
||
terminal rosette, rootless hornwort forked whorls, long-petioled pondweed ovals.
|
||
- **Lichens**: fruticose reindeer forks, flattened oakmoss straps, hanging beard
|
||
filaments, map-lichen crust islands, broad dog-lichen lobes/rhizines, lungwort
|
||
reticulation, orange sunburst discs, grey hammered shields, distinct slender
|
||
mealy and stout pebbled pixie cups. Crust, foliose and branched forms differ.
|
||
|
||
Inactive images are **per ID**, not one shared dormant swatch: blooms/fruit are
|
||
omitted, leaves reduced or twigs exposed, fungi collapse to stems/shelves, growth
|
||
settles into a smaller dry silhouette. Persistent evergreen/moss/lichen images
|
||
also have an explicit inactive fallback, but **do not imply those species must
|
||
become dormant in Winter**; the seasonal registrar decides when each is used.
|
||
|
||
### Exact image and provenance manifests
|
||
|
||
`BOTANY_CATALOGUE` lists **80 unique exact IDs and 80 unique Latin names**,
|
||
transcribed from the actual Lua `add()` records, in catalogue order. Eight groups
|
||
of ten: trees, shrubs, grasses, herbs, ferns_mosses, fungi, aquatic, lichens.
|
||
Each record includes `id`, `latin`, `group`, diagnostic `morphology`.
|
||
`BOTANY_ART_NAMES` contains those exact 80 IDs; `BOTANY_SPRITE_NAMES` expands
|
||
them to **160** stems. Each has prefix `hearth_botany_` and PNG extension:
|
||
the ID alone for active art, the ID plus `_dormant` for inactive art.
|
||
|
||
`BOTANY_ART_MANIFEST` annotates **each of the 160 PNG outputs** with exact
|
||
`filename`, `id`, **`botanicalLatin`**, `group`, `morphology`, `state`, dimensions,
|
||
license and authorship. Generation writes this deterministically as a root-level
|
||
JSON provenance document named **botany-art.json** inside the private output tree.
|
||
It is included in the hash-verified package. No Lua evaluation, source-loop
|
||
guessing, network or platform-specific botanical database is needed. Tests parse
|
||
the actual plain Lua catalogue independently and compare every ID/Latin pair.
|
||
|
||
`worldArt()` now returns **360 distinct images**: **160 world/ecology tiles,
|
||
34 item icons, three UI tiles, three animal atlases, 160 catalogue specimens**.
|
||
This adds **192** images to the previous 168: twenty new tree faces, twelve new
|
||
deciduous seasonal leaf faces, and 160 catalogue specimens.
|
||
|
||
Generated package outputs: **405 fixed PNGs**, plus source-dependent compatibility
|
||
swatches. The minimal fixture has two swatches, thus **407 PNGs**. The fixed set
|
||
is 360 sprites + **38 opaque 64×32 atlases** (four generic, 34 species) + contact
|
||
sheet + four tool icons + player skin + crosshair alias. There remain **29 legacy
|
||
flora OBJ files**, **37 OBJ total plus one animated player B3D**. No new tree
|
||
meshes were authored; player, tools, wildlife and fifteen Ogg files are unchanged.
|
||
|
||
### Exact ten-tree cube material and season contract
|
||
|
||
All stems in this table take prefix `hearth_` and PNG extension. Each row
|
||
explicitly gives log end, log side, leaves and crafted boards in that order.
|
||
|
||
| Species / botanical identity | Log top | Log side | Leaves | Planks | Additional leaf seasons |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| oak / Quercus robur | oak_log_top | oak_log_side | oak_leaves | oak_planks | spring, autumn, winter |
|
||
| birch / Betula pendula | birch_log_top | birch_log_side | birch_leaves | birch_planks | spring, autumn, winter |
|
||
| pine / Pinus sylvestris | pine_log_top | pine_log_side | pine_leaves | pine_planks | none; evergreen |
|
||
| willow / Salix alba | willow_log_top | willow_log_side | willow_leaves | willow_planks | spring, autumn, winter |
|
||
| alder / Alnus glutinosa | alder_log_top | alder_log_side | alder_leaves | alder_planks | spring, autumn, winter |
|
||
| beech / Fagus sylvatica | beech_log_top | beech_log_side | beech_leaves | beech_planks | spring, autumn, winter |
|
||
| aspen / Populus tremula | aspen_log_top | aspen_log_side | aspen_leaves | aspen_planks | spring, autumn, winter |
|
||
| rowan / Sorbus aucuparia | rowan_log_top | rowan_log_side | rowan_leaves | rowan_planks | spring, autumn, winter |
|
||
| hazel / Corylus avellana | hazel_log_top | hazel_log_side | hazel_leaves | hazel_planks | spring, autumn, winter |
|
||
| spruce / Picea abies | spruce_log_top | spruce_log_side | spruce_leaves | spruce_planks | none; evergreen |
|
||
|
||
Season images append `_spring`, `_autumn`, `_winter` to the **leaf stem**.
|
||
Beech/aspen/rowan/hazel add twelve seasonal faces; spruce is evergreen like pine.
|
||
Forty base faces plus 24 seasonal leaf faces are explicit outputs. New bark
|
||
conveys smooth grey beech, diamond-scarred pale aspen, lenticellate rowan/hazel
|
||
and scaly spruce; endgrain is eccentric rings and boards show restrained joints.
|
||
All older generic/species leaf drawing code and encoded PNG bytes are preserved;
|
||
tests pin the six approved base leaf PNG hashes, plus the original generic RGBA
|
||
SHA256 `6ca3e11b7e8e98f46220db92a4dbe729515835c3ae39c76ac3003fa5494f3f4c`.
|
||
|
||
**New roots/stages use cubes**, no irregular canopy OBJ. Log face order is top,
|
||
top, side, side, side, side; existing `facedir` controls axis orientation. Each
|
||
seasonal woody-stage copy must retain that cube tile list and must not switch to
|
||
an atlas/mesh. Canopy cubes use the species/season leaf faces; tree seedlings,
|
||
inventory and guide can use catalogue sprites. Runtime geometry, migration and
|
||
seasonal swaps are other owners' work, not implemented by this art generator.
|
||
|
||
`LEGACY_MESH_SPECIES` freezes the original five species. Existing generic and
|
||
species OBJ exports are **deprecated compatibility assets only**, retained until
|
||
the native-closure owner excludes unused files. Do not infer use from packaging
|
||
or add five new sculptural models. `SPECIES_ATLAS_NAMES` has all ten species:
|
||
eight deciduous × four seasons + two evergreen × one = 34. Generic four + 34 = 38.
|
||
New atlases are compatibility/review bytes, **not cube stage tiles**. Each keeps
|
||
own bark byte-exact at left and own seasonal foliage at right with opaque
|
||
underpaint. Evergreen non-summer atlas requests fail closed.
|
||
|
||
### Bounded sorted review layout
|
||
|
||
`contactSheet()` accepts **1–512** validated RGBA 32px tiles: **16 columns**,
|
||
512px wide, height `ceil(count/16)*32`, maximum 1024px. Current **360-image
|
||
512×736** sheet has **23 rows**, eight unused final cells. Native pixels are not
|
||
resampled. Full filenames sort in codepoint order regardless of Map insertion
|
||
order; zero-based sorted index `i` maps to column `i%16`, row `floor(i/16)`.
|
||
Cells are composited on the journal checker, not opaque placeholder squares.
|
||
|
||
This formula and the complete named/Latin manifest are the authoritative legend,
|
||
not an obsolete manually maintained 10-column table. A separate botanical review
|
||
can select the 160 `hearth_botany_` images: the same sorted 16-column layout makes
|
||
**512×320** (ten rows), placing active and dormant specimens next to each other.
|
||
|
||
### Safety and validation boundary
|
||
|
||
Every PNG, retained OBJ, review sheet and provenance output passes the existing
|
||
`writeAsset` / `relativeName` / `safePath` path guards in private staging. Links
|
||
and junctions are refused; tile count, dimensions and decode allocations are
|
||
bounded. Source scanning remains mod-Lua-only. No secrets, cryptographic protocol
|
||
or external certificates are introduced; SHA256 is package integrity, not trust.
|
||
|
||
Focused Node suites check the actual 80 ID/Latin pairs, 160 unique images,
|
||
80 unique active and 80 unique inactive alpha masks, palette/occupancy/padding,
|
||
diagnostic botanical landmarks, preserved leaf PNGs, all cube faces and atlases,
|
||
determinism, independent PNG CRC/decode, exact package bytes and output link
|
||
refusal. Existing legacy topology/UV budgets remain checked without claiming the
|
||
models are used. **No engine rendering, mapgen, seasonal runtime closure or
|
||
performance certification is claimed.**
|
||
|
||
Final focused run: **39 passed, 0 failed, 0 cancelled, 0 skipped**, exit 0,
|
||
empty stderr, **80.84 seconds**, Windows Node **24.22.0**. All three asset suites
|
||
were included; optional external audio codec regeneration was not configured.
|
||
The **512×736** complete sheet and **512×320** botanical sheet (also enlarged
|
||
nearest-neighbour 2× for inspection) were generated under the system temporary
|
||
review directory. Windows directory-junction refusal and exact fixture-package
|
||
bytes passed. Existing leaf PNG fingerprints passed without changing their art.
|
||
|
||
<details>
|
||
<summary>Historical art milestones — superseded counts and tree-mesh advice</summary>
|
||
|
||
# The Last Hearth — moonlit botanical field journal
|
||
|
||
## Current botanical extension — authoritative contract (2026-11-02)
|
||
|
||
This section supersedes the folded historical snapshot below. Only
|
||
[world-art.mjs](../scripts/world-art.mjs),
|
||
[flora-art.mjs](../scripts/flora-art.mjs),
|
||
[assets.mjs](../scripts/assets.mjs),
|
||
[world-art.test.mjs](../tests/world-art.test.mjs),
|
||
[flora-art.test.mjs](../tests/flora-art.test.mjs) and this document are owned.
|
||
The exact runtime contracts were read from
|
||
[biomes.lua](../mods/hearth/biomes.lua),
|
||
[diversity.lua](../mods/hearth/diversity.lua),
|
||
[world-generation.md](world-generation.md) and [ecosystems.md](ecosystems.md).
|
||
No gameplay, engine, package metadata or live-world edits are included.
|
||
|
||
### Original realistic botanical pixel grammar
|
||
|
||
All drawings and sculptures are original procedural source under the project's
|
||
[MIT license](../LICENSE.txt). No downloaded assets, image/model libraries,
|
||
network calls or random/hash noise. Mocha journal shadows support muted natural
|
||
mineral, loam, sage and timber colours; flowers and metal highlights remain small.
|
||
These are stylized natural materials, not photographic scans or plant-ID advice.
|
||
|
||
- **Base stone is unjointed parent rock**: irregular connected mineral microfacets,
|
||
not masonry, mortar outlines or a brick tessellation. Granite has feldspar and
|
||
quartz grains; basalt tight dark vesicles; limestone pale fossil/lens marks;
|
||
slate oblique cleavage; sandstone interrupted bedding and compact grains.
|
||
- **Soil is granular aggregate**: small discontinuous compacted lenses, crumbs and
|
||
organic marks, never full-width cartoon stripes. Forest floor has fragmented
|
||
litter, podzol needle litter, alluvium fine sediment/rounded grains, peat dark
|
||
organic fibres, alpine turf low mineral-interspersed growth.
|
||
- **Grass top is dense, non-symbolic tuft mosaic** across the whole surface, not
|
||
isolated outlined blades. Grass side has a variable organic cap and fine roots
|
||
into the same mineral soil body. Sand uses low-contrast ripples; gravel has
|
||
rounded clasts, scree angular fragments, snow sparse compressed wind marks.
|
||
- **Bark has wandering interrupted fissures** and branch scars, not repeated
|
||
straight planks. Endgrain rings are eccentric, warped and split. Birch is pale
|
||
with dark horizontal lenticels; pine scaly warm bark; willow fine dark furrows;
|
||
alder compact gray-brown bark. Planks remain visibly crafted with subdued joints
|
||
and flowing grain. Only foundations/weirs and named rock bricks use masonry.
|
||
- The approved generic **leaf texture is byte-for-byte unchanged**. Its RGBA
|
||
SHA256 is `6ca3e11b7e8e98f46220db92a4dbe729515835c3ae39c76ac3003fa5494f3f4c`.
|
||
New species leaves have distinct shapes: lobed oak, small airy birch, pine
|
||
needle sprays, narrow hanging willow and rounded alder. Spring adds sparse buds;
|
||
Autumn copper/ochre/wine; Winter open woody forks with no green leaf fill.
|
||
- Copper seams show oxidized green beside dull copper; quartz crystalline milky
|
||
facets; coal broken dark seams. Resource and tool icons have their own padded
|
||
silhouettes. The copper charge retains ore with the existing binding/tag grammar.
|
||
|
||
Every drawing is native 32×32 straight-alpha RGBA, nearest-neighbor, with an
|
||
explicit restrained palette. Natural rock/substrate and ore-host faces are opaque;
|
||
leaves/understory/items use crisp 0/255 alpha and zero hidden RGB. Item borders
|
||
remain empty. Existing window alpha-95 glass, water seams and legacy interfaces
|
||
are retained. Sand is horizontally seamless; water is seamless on both axes.
|
||
Different materials use different geometry rather than one recoloured tessellation.
|
||
|
||
### Exact output inventory
|
||
|
||
`worldArt()` returns **168 distinct 32×32 images**: **128 world/ecology tiles,
|
||
34 item icons, three UI tiles and three animal atlases**. The increase from 95 is
|
||
73 sprites: 41 biome faces, 12 deciduous seasonal leaf faces, 14 understory sprites
|
||
and six resource/tool icons. `WORLD_NAMES`, `BIOME_WORLD_NAMES`, `SPECIES_NAMES`,
|
||
`ROCK_NAMES`, `SPECIES_SEASONAL_NAMES`, `UNDERSTORY_NAMES`,
|
||
`DORMANT_UNDERSTORY_NAMES`, `PLANT_NAMES`, `ITEM_NAMES` are explicit bounded manifests.
|
||
|
||
All stems below take `hearth_` plus the PNG extension:
|
||
|
||
- For each of granite, basalt, limestone, slate, sandstone: the rock stem and
|
||
its `_bricks` stem (10); forest_floor, podzol, alluvium, peat, alpine_turf,
|
||
gravel, scree, snow (8); quartz_vein, copper_ore_vein, coal_ore_vein (3).
|
||
- For each oak, birch, pine, willow, alder: `_log_top`, `_log_side`, `_leaves`,
|
||
`_planks` (20). These **41 faces plus item_quartz, item_copper_ore, item_coal**
|
||
implement the exact 44-PNG biome contract, not aliases or compatibility swatches.
|
||
- Deciduous oak/birch/willow/alder `_leaves_spring`, `_leaves_autumn`,
|
||
`_leaves_winter` (12). Pine has no seasonal leaf/atlas/mesh duplicates.
|
||
- clover, nettle, heather, bluebell, sedge, bulrush, moss, lichen; the first six
|
||
also have `_dormant` (14). Dormant sprites lose flowers/green and reduce growth;
|
||
bluebell reuses the dormant sprite outside Spring. Moss/lichen remain persistent.
|
||
- Additional `item_copper_ingot`, `item_copper_charge`, `item_copper_pick` (3).
|
||
Quartz crystal, copper ore, coal, ingot, bound charge and pick are six distinct
|
||
icons, including when generated from a minimal source with no asset references.
|
||
|
||
`generateAssets()` additionally writes **21 opaque 64×32 tree atlases** (four
|
||
generic plus 17 species), the **320×544 contact sheet**, four existing tool icons,
|
||
the 64×64 player skin and engine crosshair alias. Thus there are **196 named PNG
|
||
outputs plus the separately source-dependent compatibility swatches**. No output
|
||
discovery depends on expanding Lua loops. Existing sound/player/tool generation
|
||
and fifteen Ogg files remain unchanged.
|
||
|
||
`FLORA_NAMES` contains **29 OBJ meshes**: six retained generic flora models plus
|
||
23 species models. Together with three animals, four held tools and the fallback
|
||
player this is **37 OBJ files**, plus the existing animated player B3D.
|
||
|
||
### Species sculpture, scale and seasonal contract
|
||
|
||
All flora uses 10 model units/node, node-centre origin, ground exactly Y=-5
|
||
model units (-0.5 nodes), `visual_scale=1`. Mature height is at most five nodes
|
||
and horizontal footprint at most ±1.5; young forms fit 2.5 high and ±0.75.
|
||
Seedlings are below one node high and ±0.3. Collision/placement/growth policy is
|
||
the ecosystem owner's responsibility, not inferred from the visual canopy.
|
||
|
||
Oak has broad spreading forks and an asymmetric multi-lobed crown. Birch has a
|
||
slim elevated bole and separated small airy lobes. Pine has tapered branch whorls
|
||
and a narrow leader, with daylight between tiers. Willow has long descending
|
||
terminal wood and six pendant leaf curtains under a spreading crown. Alder has
|
||
multiple upright leaders and a compact rounded riparian crown. These are closed
|
||
volumes, not crossed planes, leaf billboards or oversized cubes.
|
||
|
||
For every species generate `_seedling`, `_young`, `_mature` OBJ stems. Deciduous
|
||
species also generate `_young_winter`, `_mature_winter`; all original woody parts
|
||
are retained exactly and all foliage omitted. Seedlings retain their ordinary
|
||
mesh in Winter as runtime specifies. Pine stays evergreen. Generic deadwood stays
|
||
shared; species provenance is metadata, not extra deadwood art.
|
||
|
||
| Species | Seedling vertices / triangles | Young and mature vertices / triangles | Each winter young/mature |
|
||
| --- | --- | --- | --- |
|
||
| Oak | 84 / 152 | 332 / 596 | 176 / 308 |
|
||
| Birch | 84 / 152 | 300 / 540 | 144 / 252 |
|
||
| Pine | 110 / 200 | 546 / 988 | none |
|
||
| Willow | 84 / 152 | 558 / 1004 | 272 / 476 |
|
||
| Alder | 84 / 152 | 274 / 492 | 144 / 252 |
|
||
|
||
`SPECIES_MESH_NAMES` and `SPECIES_ATLAS_NAMES` enumerate exact outputs. Each
|
||
species uses its own `_atlas` PNG; deciduous trees additionally use
|
||
`_atlas_spring`, `_atlas_autumn`, `_atlas_winter`. `treeAtlas(art, season, species)`
|
||
preserves the original two-argument API. Unsupported species/seasons fail closed;
|
||
seasonal pine requests are rejected. Left half is own-species bark, byte-identical
|
||
through seasons; right half own-season foliage over opaque semantic underpaint.
|
||
Half-texel inset UVs and explicit outward normals are retained. Winter faces sample
|
||
only bark; canopy holes are gaps between lobes, never torn transparent faces.
|
||
|
||
### Sorted contact-sheet legend
|
||
|
||
`contactSheet()` sorts by complete filename using codepoint order, independent of
|
||
input Map insertion order. It accepts **1–320 validated 32px RGBA tiles**, always
|
||
10 columns, height `ceil(count/10)*32` (maximum 1024px). The current 168-tile sheet
|
||
is **320×544** with two unused checker cells. It contains every sprite, not the
|
||
separate 64×32 tree atlases. Native pixels are not resampled. Rows and columns
|
||
below are one-based; stems retain `item_` and omit only `hearth_` / PNG extension.
|
||
|
||
| Row | Columns 1 → 10 |
|
||
| --- | --- |
|
||
| 1 | alder_leaves · alder_leaves_autumn · alder_leaves_spring · alder_leaves_winter · alder_log_side · alder_log_top · alder_planks · alluvium · alpine_turf · autumn_mushrooms |
|
||
| 2 | autumn_mushrooms_dormant · basalt · basalt_bricks · bedroll · berry_bush · berry_bush_bare · birch_leaves · birch_leaves_autumn · birch_leaves_spring · birch_leaves_winter |
|
||
| 3 | birch_log_side · birch_log_top · birch_planks · bluebell · bluebell_dormant · bulrush · bulrush_dormant · clay_bank · clover · clover_dormant |
|
||
| 4 | coal_ore_vein · copper_ore_vein · crop_1 · crop_2 · crop_3 · crop_4 · crosshair · deadwood · deer · door |
|
||
| 5 | drying_rack · dung · fern · fern_dormant · fire · fire_lit · fish_trap · forest_floor · foundation · fox |
|
||
| 6 | frame · fungi · grain_patch · granite · granite_bricks · grass_side · grass_top · gravel · hare · heather |
|
||
| 7 | heather_dormant · hotbar · iron_ore_vein · item_autumn_mushrooms · item_berries · item_charcoal · item_charcoal_charge · item_clay · item_clay_jar · item_clay_jar_charge |
|
||
| 8 | item_cloak · item_coal · item_cooked_fish · item_cooked_food · item_copper_charge · item_copper_ingot · item_copper_ore · item_copper_pick · item_dried_fish · item_dried_food |
|
||
| 9 | item_dried_food_charge · item_firestarter · item_fish_bait · item_fish_drying_charge · item_grain · item_grain_seed · item_iron_axe · item_iron_ingot · item_iron_ore · item_quartz |
|
||
| 10 | item_raw_fish · item_raw_food · item_smelt_charge · item_spring_greens · item_vessel · item_water_clean · item_water_raw · leaves · leaves_autumn · leaves_spring |
|
||
| 11 | leaves_winter · lichen · limestone · limestone_bricks · litter · log_side · log_top · loose_stone · mature_tree · meadow_grass |
|
||
| 12 | meadow_grass_dormant · moss · nettle · nettle_dormant · oak_leaves · oak_leaves_autumn · oak_leaves_spring · oak_leaves_winter · oak_log_side · oak_log_top |
|
||
| 13 | oak_planks · pantry · peat · pine_leaves · pine_log_side · pine_log_top · pine_planks · planks · podzol · quartz_vein |
|
||
| 14 | reeds · roof · sand · sandstone · sandstone_bricks · sapling · scree · sedge · sedge_dormant · selected |
|
||
| 15 | slate · slate_bricks · snow · soil · spring_greens · spring_greens_dormant · stairs · sticks · stone · storage |
|
||
| 16 | thatch · torch · tree_seedling · vent · wall · water · weir_stone · wildflowers · wildflowers_dormant · willow_leaves |
|
||
| 17 | willow_leaves_autumn · willow_leaves_spring · willow_leaves_winter · willow_log_side · willow_log_top · willow_planks · window · young_tree · (two unused) |
|
||
|
||
### Packaging, security and verification boundary
|
||
|
||
Every named PNG/OBJ still passes through `writeAsset`, `relativeName`, `safePath`
|
||
and the existing guarded staging directories. Input/output links and junctions
|
||
remain refused; sheet and PNG memory are bounded. Minimal-source generation and
|
||
integrity-verified packaging check every new atlas and every mesh byte. No new
|
||
dependencies, secrets or certificates; SHA256 is integrity, PNG CRC32 a checksum,
|
||
neither an authenticity guarantee. This applies CodeGuard secret avoidance and
|
||
bounded safe-file handling without introducing a network/security subsystem.
|
||
|
||
The focused suites now cover exact independent manifests, actual content icon
|
||
coverage, approved leaf hash, rock versus brick structure, distributed turf,
|
||
aggregate soil/root transitions, multi-radius growth rings, unique palettes/masks,
|
||
understory dormancy, species dimensions/architecture, all 29 flora meshes' finite
|
||
indexed outward nondegenerate triangles, flat normals, closed oriented manifold
|
||
parts, positive volume, UV insets, every seasonal woody-part preservation, exact
|
||
atlas source pixels, complete package bytes, independent PNG decode and sorted
|
||
sheet cells. Larger species have explicit 640-vertex/1200-triangle ceilings;
|
||
legacy budgets and all topology assertions remain. The bounds are real serialized
|
||
geometry, not assumed from profile labels.
|
||
|
||
Node-only validation and contact-sheet inspection do **not** certify engine
|
||
rendering, growth, seasonal swaps, mapgen, collision, performance or live-world
|
||
acceptance. No engine was launched. Optional codec regeneration requires its
|
||
existing configured tool; Ogg framing/CRC/hash checks remain part of the unchanged
|
||
media suite.
|
||
|
||
Latest extension verification: **35 passed, 0 failed, 0 cancelled, 0 skipped**,
|
||
exit 0, empty stderr, **43.87 seconds** with the absolute Node **24.22.0**
|
||
executable. All five JavaScript/test modules passed syntax checks. The generated
|
||
**168-image 320×544** sheet was inspected separately. Optional codec
|
||
decode/regeneration was not configured; Ogg framing/CRC/hash checks passed.
|
||
Windows file-symlink privilege was unavailable; directory-junction refusal passed.
|
||
|
||
<details>
|
||
<summary>Historical pre-extension art snapshot (superseded counts, geometry and sheet order)</summary>
|
||
|
||
The following records the earlier 95-sprite/six-mesh milestone only. In particular
|
||
its old masonry base stone, broad earth bands, 160-tile limit, contact-sheet order,
|
||
unchanged generic bark statement and latest-run counts are no longer current.
|
||
Use the authoritative extension contract above for generation and integration.
|
||
|
||
## Direction and authorship
|
||
|
||
An original, quiet 32px pixel-art system: a naturalist's field journal left open
|
||
beside a warm hearth. Warm bark, layered earth, weathered stone and sage foliage
|
||
form the world; moonlit Mocha shadows hold it together. Mauve, teal and pale gold
|
||
are small points of interest, **not neon terrain fills**.
|
||
|
||
The design was checked against eom.dev's local
|
||
[Catppuccin stylesheet](../../www/htdocs/static/catppuccin.css) and
|
||
[home stylesheet](../../www/htdocs/static/home.css): Mocha base/mantle,
|
||
restrained raised surfaces, mauve accents and pale text. These are color/design
|
||
references, not borrowed illustrations. All pixel silhouettes, material motifs,
|
||
meshes and UV layouts here are newly authored in
|
||
[world-art.mjs](../scripts/world-art.mjs), under the project's
|
||
[MIT license](../LICENSE.txt). No downloaded art, animal model, font, image
|
||
library, runtime dependency, network fetch, random seed or generative-image
|
||
output is involved.
|
||
|
||
### Palette hierarchy
|
||
|
||
| Role | Palette anchors | Use |
|
||
| --- | --- | --- |
|
||
| Journal shadows | `#181825`, `#1e1e2e`, `#313244`, `#45475a` | Outlines, deep joints, interface |
|
||
| Warm ground | `#443a38`, `#665044`, `#876957`, `#ad7e68` | Earth strata, clay banks, plaster |
|
||
| Bark and cut timber | `#433b36`, `#685346`, `#92765a`, `#ad916b`, `#c6ad83` | Fissures, growth rings, sawn boards |
|
||
| Botanical greens | `#384c43`, `#556e55`, `#7d9570`, `#a4b28b` | Sage leaves with dark undersides |
|
||
| Spring growth | `#526b50`, `#8fa67c`, `#bac59a`, `#c19caa` | Fresh sage blades, sparse dusty-pink buds |
|
||
| Autumn canopy | `#64454c`, `#a26c49`, `#b59458`, `#845365` | Muted wine shadows, copper and ochre clusters; no green |
|
||
| Thatch and grain | `#766848`, `#a39061`, `#c6b581` | Overlapping straw and ripe seed heads |
|
||
| Water and iron | `#334952`, `#425e66`, `#638187`, `#909f9f` | Quiet ripples and small cool metal edges |
|
||
| Reserved accents | `#cba6f7`, `#94e2d5`, `#f9e2af`, `#fab387` | Flowers, selection, small sparks; never biome fills |
|
||
|
||
The complete immutable `PALETTE` export is authoritative. Each tile uses a small
|
||
subset. Plain dark pixels are intentional visual rest, not missing detail.
|
||
|
||
### Pixel grammar
|
||
|
||
- Exactly 32×32 logical pixels; nearest-neighbor filtering, no antialiasing.
|
||
- Connected 2–8px color clusters instead of independent hash/static noise.
|
||
- Material-specific geometry: irregular stone courses, broad soil layers, long
|
||
bark fissures, concentric end-grain, peg-and-joint planks, timber braces,
|
||
plaster cracks, tied overlapping thatch, inset storage and pantry jars.
|
||
- Light comes from upper left; edges catch warm parchment or cool moonlight.
|
||
- Plants have stems, paired leaves, distinct negative spaces and intentional
|
||
asymmetric silhouettes. Fruit is a mauve cluster, not a glowing block.
|
||
- Food and tools read by silhouette first. Charges retain the input ingredient
|
||
and add a diagonal binding and pale-gold paper tag; do not confuse a queued
|
||
process with a finished resource. Clean water has a crisp sparkle; raw water
|
||
has muted suspended marks as well as a different color.
|
||
- Water is static, fully opaque source art with sparse horizontal wavelets. Its
|
||
opposite edges match on both axes. Apply the game's desired liquid alpha in
|
||
node configuration. Sand/clay strata match left/right edges. Not every
|
||
decorative building tile promises identical edge pixels on all four sides.
|
||
|
||
## Generation and contracts
|
||
|
||
[generateAssets](../scripts/assets.mjs) always bundles the complete named atlas,
|
||
three animal meshes, and the contact sheet. It does **not** discover named assets
|
||
by parsing real gameplay declarations or opening named source binaries. Minimal
|
||
fixture trees containing only mod Lua still package correctly. Existing bounded
|
||
Lua color scanning remains for compatibility swatches. Those now use subdued
|
||
earth-tinted clusters, not noisy neon squares; semantic names below are the
|
||
intended integration target.
|
||
|
||
Every output still passes through `writeAsset`, `relativeName` and `safePath`.
|
||
Source symlinks/junctions, linked output ancestors and unsafe relative paths
|
||
remain refused. PNG dimensions/pixels and review-sheet tile count are bounded.
|
||
No credentials, certificates, insecure cryptography or new downloads were
|
||
introduced; PNG CRC32 is a file checksum, not a security primitive. This follows
|
||
the reviewed CodeGuard secret avoidance and bounded/safe-file-handling rules.
|
||
|
||
### Public pure exports
|
||
|
||
- `worldArt()` returns a fresh ordered `Map<filename, {width, height, data}>`.
|
||
`data` is straight-alpha RGBA `Uint8Array`; all tiles are 32×32.
|
||
- `WORLD_NAMES`, `PLANT_NAMES`, `SEASONAL_PLANT_NAMES`, `ITEM_NAMES`, `ANIMAL_NAMES`, `PALETTE` and `SIZE`
|
||
expose the fixed manifest and palette without accessing the filesystem.
|
||
- `animalOBJ(species)` returns deterministic original OBJ text; unknown species
|
||
are rejected. `animalBounds(species)` returns node-unit `min`, `max` and the
|
||
expected `visualSize`.
|
||
- `contactSheet()` returns a 320px-wide RGBA review atlas at **full native 32px cell
|
||
resolution**, not downsampled thumbnails. Height is `ceil(tileCount / 10) × 32`
|
||
(currently 320px). It accepts 1–160 validated tiles, including every seasonal
|
||
sprite and both picked-food icons. The 64×32 tree atlases remain separate;
|
||
their seasonal leaf drawings are reviewed in the corresponding sprite cells.
|
||
- `artContactSheetPNG()` in the asset generator exports its encoded PNG bytes.
|
||
`png(width, pixelCallback, height = width)` now supports bounded rectangular
|
||
output while preserving square callers.
|
||
|
||
### Output locations
|
||
|
||
Under any generated destination:
|
||
|
||
- `mods/hearth/textures/hearth_<world-name>.png`
|
||
- `mods/hearth/textures/hearth_item_<item-name>.png`
|
||
- `mods/hearth/textures/hearth_{hare,fox,deer}.png`
|
||
- `mods/hearth/models/hearth_{hare,fox,deer}.obj`
|
||
- `mods/hearth/textures/hearth_{hotbar,selected,crosshair}.png`
|
||
- `mods/hearth/textures/hearth_art_contact_sheet.png`
|
||
|
||
No binary assets need to be checked into the source tree. Normal packaging calls
|
||
the generator and includes these outputs in its existing integrity manifest.
|
||
|
||
## Complete named-art manifest / contact-sheet legend
|
||
|
||
The review sheet contains **95 distinct images**, plus the generated sheet
|
||
itself. There are ten columns, ten rows; rows/columns below are **one-based**.
|
||
Unused cells in the last row are checker paper. World identifiers use
|
||
`hearth_` + identifier + `.png`; item identifiers use `hearth_item_` + identifier
|
||
followed by `.png`. Interface and animal identifiers use `hearth_` + identifier
|
||
followed by `.png`.
|
||
|
||
| Row | Columns 1 → 10 |
|
||
| --- | --- |
|
||
| 1 — world | stone · soil · grass_top · grass_side · sand · log_top · log_side · leaves · planks · wall |
|
||
| 2 — construction | roof · thatch · foundation · frame · stairs · door · window · vent · storage · pantry |
|
||
| 3 — plants / clay | reeds · berry_bush · berry_bush_bare · sapling · grain_patch · crop_1 · crop_2 · crop_3 · crop_4 · clay_bank |
|
||
| 4 — resources / equipment | iron_ore_vein · water · fire · fire_lit · torch · sticks · loose_stone · bedroll · drying_rack · fish_trap |
|
||
| 5 — ecology | weir_stone · meadow_grass · wildflowers · fern · litter · fungi · deadwood · tree_seedling · young_tree · mature_tree |
|
||
| 6 — seasonal world | dung · leaves_spring · leaves_autumn · leaves_winter · meadow_grass_dormant · fern_dormant · wildflowers_dormant · spring_greens · spring_greens_dormant · autumn_mushrooms |
|
||
| 7 — patch, then items | **world:** autumn_mushrooms_dormant · **items:** berries · grain · grain_seed · raw_food · cooked_food · fish_bait · raw_fish · cooked_fish · fish_drying_charge |
|
||
| 8 — items | dried_fish · iron_ore · iron_ingot · charcoal · charcoal_charge · smelt_charge · clay · clay_jar · clay_jar_charge · dried_food |
|
||
| 9 — items, then UI | **items:** dried_food_charge · cloak · vessel · water_raw · water_clean · firestarter · iron_axe · spring_greens · autumn_mushrooms · **UI:** hotbar |
|
||
| 10 — UI / animal UV atlases | selected · crosshair · hare · fox · deer · (five unused cells) |
|
||
|
||
Counts: **61 world/ecology textures, 28 craftitem icons, 3 interface textures,
|
||
and 3 animal UV atlases: 95 total**. Existing four tool icons, the 64px player skin,
|
||
color-compatibility textures, original player/tool models and audio remain
|
||
separate; they are not silently included in this sheet's numbering.
|
||
|
||
### Alpha and main-agent node integration
|
||
|
||
- `leaves`, `reeds`, `berry_bush`, `berry_bush_bare`, `sapling`, `grain_patch`,
|
||
all four `crop_*`, `meadow_grass`, `wildflowers`, `fern`, `litter`, `fungi`,
|
||
`deadwood`, `tree_seedling`, `young_tree`, `mature_tree`, `dung`: crisp 0/255
|
||
alpha, zero RGB beneath transparent pixels. Use clip-compatible leaf/glasslike
|
||
or appropriate plantlike drawtypes; ground litter/deadwood may suit a low mesh
|
||
or nodebox better. Mature-tree art is a silhouette, **not** a new tree-building
|
||
algorithm. Ecology controls growth, placement and drops independently.
|
||
- All craftitems: crisp 0/255 alpha and empty outer borders. Set
|
||
`inventory_image = "hearth_item_<item>.png"` individually rather than tinting
|
||
one color square. Processing charges have their own distinct icons.
|
||
- `window`: empty outside, opaque frame/mullions, alpha-95 glass. Use blend
|
||
alpha and a suitable glass/nodebox drawtype, not opaque rendering.
|
||
- `fire`, `fire_lit`, `torch`, `sticks`, `loose_stone`, `bedroll`, `drying_rack`,
|
||
`fish_trap`: transparent silhouettes. Select clip-compatible rendering or use
|
||
them as inventory art for physical nodeboxes; an opaque cube will look wrong.
|
||
Fire is one static drawing, not an animation strip. Emission/light and flame
|
||
particles remain gameplay decisions.
|
||
- Remaining world materials and animal atlases: fully opaque. `water` source
|
||
art is opaque deliberately; liquid transparency must be configured by main.
|
||
- Six cube face ordering for logs: use `log_top` on the two ends and `log_side`
|
||
on four sides; grass uses `grass_top`, `soil` bottom and `grass_side` sides.
|
||
Do not continue multiplying these authored textures by saturated old tints.
|
||
- `hearth_hotbar.png` is a **single slot tile**, not a prebuilt eight-slot strip.
|
||
Main must repeat it into its chosen hotbar layout or use individual slot HUD
|
||
images; directly stretching it across the whole bar would stretch the border.
|
||
`hearth_selected.png` is a transparent gold/mauve slot outline.
|
||
`hearth_crosshair.png` has sparse parchment marks and a Mocha contrast outline.
|
||
Source pixels remain 32px; HUD scale/offset is main's responsibility.
|
||
|
||
## Original wildlife meshes — integration handoff
|
||
|
||
No external model or flat animal billboard. Hare has deep haunches, grounded
|
||
hind feet, long upright ears and a small cream tail. Fox has dark slim legs,
|
||
pointed ears, cream bib/muzzle and a long tapered, pale-tipped brush. Deer has
|
||
long separated legs, dark hooves, raised neck, broad ears and branched antlers.
|
||
These are static low-poly models, **not animated B3D rigs**; movement/idle logic
|
||
must not request player animation frames on them.
|
||
|
||
Select `visual="mesh"`, the species OBJ and the matching PNG texture, with
|
||
`visual_size={x=1,y=1}`. Mesh coordinates use **10 model units per node**, just
|
||
like the existing player contract. Feet are Y=0, origin at ground center,
|
||
forward is **+Z**. Do **not** apply another 10× scale. Each model is one texture,
|
||
with no external MTL/image references. Eight semantic 8px atlas patches use
|
||
half-texel inset UVs; stable indexed, outward-wound triangles avoid sampling
|
||
neighboring material patches. Use nearest-neighbor texture filtering.
|
||
|
||
| Model / matching texture stem | Vertex / triangle count | Min bounds (nodes) | Max bounds (nodes) |
|
||
| --- | --- | --- | --- |
|
||
| `hearth_hare` | 112 / 168 | (-0.185, 0, -0.370) | (0.185, 0.848159, 0.429500) |
|
||
| `hearth_fox` | 128 / 192 | (-0.155, 0, -0.820375) | (0.155, 0.786649, 0.592000) |
|
||
| `hearth_deer` | 192 / 288 | (-0.280, 0, -0.492526) | (0.280, 1.510000, 0.675000) |
|
||
|
||
`animalBounds()` is authoritative at full precision. These are **visual bounds**,
|
||
including ears/tails/antlers, not prescribed gameplay collision boxes. Main
|
||
should choose comfortable body collision/selection boxes, ground-origin spawn
|
||
offsets and facing/yaw conversion. Review the import orientation in-engine before
|
||
claiming a verified visual integration. This work does not edit wildlife Lua.
|
||
|
||
### Preserved player/media contract
|
||
|
||
The original 64×64 player skin, B3D bytes, skeleton, UVs and six animation clips
|
||
are unchanged. Existing held tools, compatibility OBJ and fifteen Ogg assets
|
||
are unchanged. The new animals do not reuse or disturb the player's atlas.
|
||
|
||
## Verification
|
||
|
||
Focused Node-only checks live in [world-art.test.mjs](../tests/world-art.test.mjs),
|
||
[flora-art.test.mjs](../tests/flora-art.test.mjs)
|
||
and the unchanged [assets.test.mjs](../tests/assets.test.mjs). No engine tests,
|
||
live worlds, native client launch or full application test gates are required or
|
||
claimed by this art task.
|
||
|
||
The tests cover exact named manifests; all 95 distinct byte-deterministic tiles;
|
||
restricted palettes; color-independent material geometry and contiguous clusters;
|
||
plant/item occupancy, negative space, alpha and inventory padding; monotonic
|
||
crop growth; berries versus bare foliage; water seams and sand/clay strata seams;
|
||
independent PNG signature/chunk/CRC/RGBA decoding; rectangular atlas dimensions
|
||
and exact unscaled cells; bounded PNG/contact-sheet input rejection; animal
|
||
topology, finite coordinates, nondegenerate outward triangles, UV patch bounds,
|
||
counts, species proportions and node-unit bounds; minimal fixture bundling; and
|
||
linked-output refusal. Existing tests additionally verify deterministic real-source
|
||
regeneration, fixture package integrity, source traversal limits, player B3D and
|
||
audio contracts.
|
||
|
||
Validation limitations remain explicit: optional codec decode/regeneration is not
|
||
run without its configured tool; Ogg CRC/framing/hash checks still run. File
|
||
symlink creation may be unavailable on Windows; directory-junction refusal is
|
||
always checked. Geometry tests and the pixel contact sheet are not a claim that
|
||
animal models have been rendered or that Lua references have been migrated.
|
||
|
||
Historical pre-season baselines were 19 tests for world/media art and 24 with
|
||
volumetric flora. The seasonal verification section below supersedes those
|
||
counts. No Lua or other source files are owned by this art task.
|
||
|
||
## Volumetric ecology trees — refinement
|
||
|
||
The newer ecology trees supersede the billboard recommendation above for
|
||
`tree_seedling`, `young_tree`, `mature_tree` and `deadwood` only. Original pure
|
||
generation lives in [flora-art.mjs](../scripts/flora-art.mjs), under the same MIT
|
||
license. No downloaded vegetation, tree-block construction, external MTL or new
|
||
dependency is used. Meadow grass, wildflowers, ferns and other ground sprites
|
||
keep their existing plantlike presentation.
|
||
|
||
`floraOBJ(name)` generates six deterministic OBJ models; `floraBounds(name)`
|
||
returns their actual serialized vertex bounds in node units. `FLORA_NAMES` is
|
||
the fixed manifest (four original models plus two bare winter trees). Each leafy
|
||
standing tree has a tapered eight-sided stem,
|
||
separate visible boughs and intersecting closed broadleaf lobes: two for the
|
||
seedling, four for the young tree, five for the mature tree. Three uneven rings
|
||
and offset tips per lobe produce asymmetric faceted silhouettes from every
|
||
direction, rather than a giant cube or crossed planes. Deadwood has a low fallen
|
||
trunk, two broken side branches and a fork. Every face is triangulated with an
|
||
explicit outward flat normal. Nearest-neighbor filtering preserves the pixel
|
||
marks on the three-dimensional surfaces.
|
||
|
||
### Texture and packaging contract
|
||
|
||
`treeAtlas(art = worldArt(), season = 'summer')` returns fresh **64×32 straight-alpha RGBA** data. The left 32px
|
||
patch is the original `log_side` bark drawing; the right patch is the original
|
||
`leaves` drawing over opaque `leafDark` underpainting. All alpha bytes are 255
|
||
deliberately: spaces between volumetric lobes, not punched-out polygon surfaces,
|
||
define the canopy. Each triangle stays within its bark/leaf patch with half-texel
|
||
UV insets. One texture and no `usemtl`/`mtllib` loading are required.
|
||
|
||
[generateAssets](../scripts/assets.mjs) always writes these through the existing
|
||
guarded `writeAsset`, including when the source has only minimal mod Lua:
|
||
|
||
- `mods/hearth/models/hearth_tree_seedling.obj`
|
||
- `mods/hearth/models/hearth_young_tree.obj`
|
||
- `mods/hearth/models/hearth_mature_tree.obj`
|
||
- `mods/hearth/models/hearth_deadwood.obj`
|
||
- `mods/hearth/models/hearth_young_tree_winter.obj`
|
||
- `mods/hearth/models/hearth_mature_tree_winter.obj`
|
||
- `mods/hearth/textures/hearth_tree_atlas.png`
|
||
- `mods/hearth/textures/hearth_tree_atlas_spring.png`
|
||
- `mods/hearth/textures/hearth_tree_atlas_autumn.png`
|
||
- `mods/hearth/textures/hearth_tree_atlas_winter.png`
|
||
|
||
Normal packaging includes all ten in its integrity manifest. They are generated
|
||
outputs, not checked-in binaries; the review sheet grows dynamically with sprites.
|
||
|
||
### Scale, ground and exact visual bounds
|
||
|
||
Coordinates use **10 model units per node**, node-center origin, with ground
|
||
exactly **Y = -5 model units = -0.5 nodes**. Use `drawtype="mesh"`,
|
||
`visual_scale=1`, the corresponding OBJ and `tiles={"hearth_tree_atlas.png"}`.
|
||
No additional scale or vertical offset is needed.
|
||
|
||
| Model stem | Vertices / triangles | Minimum (nodes) | Maximum (nodes) | Height (nodes) |
|
||
| --- | --- | --- | --- | --- |
|
||
| `hearth_tree_seedling` | 84 / 152 | (-0.25, -0.5, -0.175) | (0.241, 0.2, 0.1375) | 0.7 |
|
||
| `hearth_young_tree` | 168 / 304 | (-0.61, -0.5, -0.56) | (0.626, 1.5, 0.45) | 2.0 |
|
||
| `hearth_mature_tree` | 210 / 380 | (-1.04, -0.5, -1.04) | (1.064, 3.0, 1.064) | 3.5 |
|
||
| `hearth_deadwood` | 64 / 112 | (-0.4044987, -0.5, -0.8019998) | (0.4034683, -0.0422183, 0.8016249) | 0.4577817 |
|
||
|
||
These are visual bounds, not canopy-sized obstacles. The ecology registration
|
||
loop alone selects mesh rendering and disallows replacement for all four.
|
||
Young and mature trees are walkable with narrow trunk collision/selection boxes:
|
||
(-0.12, -0.5, -0.12) to (0.12, 0.6, 0.12) for young trees and to
|
||
(0.12, 1.2, 0.12) for mature trees. Deadwood has a low central-trunk box;
|
||
seedlings remain nonwalkable. The semantic-art registration helper preserves
|
||
explicit `def.tiles`; the loop supplies its mesh atlas directly. No
|
||
`override_item` API or mock is required.
|
||
|
||
Growth clearance, succession logic and canopy obstruction policy remain outside
|
||
this art change; no changes were made to the succession module.
|
||
|
||
## Seasonal flora — exact integration contract
|
||
|
||
The authoritative runtime declarations are
|
||
[seasons.lua](../mods/hearth/seasons.lua) and
|
||
[content.lua](../mods/hearth/content.lua), documented in
|
||
[ecosystems.md](ecosystems.md). All following files are generated into the
|
||
destination's texture/model directories, never written into source by packaging.
|
||
`TREE_SEASONS` exports `summer`, `spring`, `autumn`, `winter`; unsupported values
|
||
fail closed. Summer's atlas pixels and all four original meshes are unchanged.
|
||
|
||
### Trees and legacy leaf nodes
|
||
|
||
| Season | Mesh-tree texture | Legacy leaf-node texture | Young / mature mesh |
|
||
| --- | --- | --- | --- |
|
||
| Spring | `hearth_tree_atlas_spring.png` | `hearth_leaves_spring.png` | Existing leafy meshes |
|
||
| Summer | `hearth_tree_atlas.png` | `hearth_leaves.png` | Existing leafy meshes |
|
||
| Autumn | `hearth_tree_atlas_autumn.png` | `hearth_leaves_autumn.png` | Existing leafy meshes |
|
||
| Winter | `hearth_tree_atlas_winter.png` | `hearth_leaves_winter.png` | `hearth_young_tree_winter.obj`, `hearth_mature_tree_winter.obj` |
|
||
|
||
Every atlas is **64×32, fully opaque RGBA**: unchanged bark left, seasonal
|
||
botanical clusters right. Spring uses fresh sage with sparse dusty buds; Summer
|
||
retains deep sage; Autumn uses copper, ochre and muted wine with **no green**.
|
||
Winter's right patch contains warm woody twigs over bark-dark underpaint, not
|
||
green or snow-painted foliage. Legacy 32×32 leaf textures retain crisp 0/255
|
||
alpha; Winter is sparse interlacing twig forks with transparent negative space.
|
||
|
||
Both winter meshes contain **only trunk, boughs and lateral woody forks**. Every
|
||
original trunk/bough vertex, UV, normal and triangle is retained exactly; crowns
|
||
are omitted and each original bough gains one outward lateral fork. They stay
|
||
within the original leafy clearance envelopes, at ground Y=-0.5 nodes, scale 1,
|
||
10 model units/node. Every winter triangle samples only the left bark half.
|
||
|
||
| Model | Vertices | Triangles | Leafy triangles |
|
||
| --- | --- | --- | --- |
|
||
| Young winter | 112 | 196 | 304 |
|
||
| Mature winter | 144 | 252 | 380 |
|
||
|
||
Seedlings intentionally reuse `hearth_tree_seedling.obj` in **all seasons**, with
|
||
the corresponding atlas, exactly as runtime requests. There is no unreferenced
|
||
winter seedling mesh. Deadwood remains on its original mesh and summer atlas.
|
||
No additional summer variant, snow overlay or tree-block construction is added.
|
||
|
||
### Ground flora and seasonal food
|
||
|
||
All sprites/icons below are **32×32 straight-alpha RGBA**, with crisp 0/255
|
||
alpha and zero hidden RGB. Picked-item icons additionally have empty outer
|
||
borders. Dormant flora uses straw/bark/earth, **no green and no blooms**, and has
|
||
reduced, visibly different silhouettes rather than a palette-only recolour.
|
||
|
||
| Runtime node(s) | Generated texture(s) | Appearance / availability |
|
||
| --- | --- | --- |
|
||
| `meadow_grass_winter` | `hearth_meadow_grass_dormant.png` | Bent dry grass blades in Winter; original living art otherwise |
|
||
| `fern_winter` | `hearth_fern_dormant.png` | Curled spent fronds in Winter; original living art otherwise |
|
||
| `wildflowers_autumn`, `wildflowers_winter` | `hearth_wildflowers_dormant.png` | Spent stalks/seed heads, no bloom; original bloom in Spring/Summer |
|
||
| `spring_greens_patch` | `hearth_spring_greens_dormant.png` | Low dry rosette outside Spring |
|
||
| `spring_greens_patch_spring` | `hearth_spring_greens.png` | Broad fresh basal blades with veins; available only in Spring |
|
||
| `autumn_mushrooms_patch` | `hearth_autumn_mushrooms_dormant.png` | Low litter/mycelial trace without pickable caps outside Autumn |
|
||
| `autumn_mushrooms_patch_autumn` | `hearth_autumn_mushrooms.png` | Three staggered copper/ochre caps and pale stalks; available only in Autumn |
|
||
|
||
Picked craftitems `hearth:spring_greens` and `hearth:autumn_mushrooms` use
|
||
`hearth_item_spring_greens.png` and `hearth_item_autumn_mushrooms.png` respectively:
|
||
a bound greens bundle and gathered mushrooms. **Patch node identifiers are not
|
||
texture stems**: no `_patch.png` aliases are generated. Existing `fungi` remains
|
||
visually distinct decomposer art, not seasonal edible forage. These are original
|
||
fictional food drawings, not real-world plant/mushroom identification guidance.
|
||
|
||
The generator adds **15 PNGs and two OBJs**: three seasonal atlases, three leaf
|
||
textures, three dormant ground textures, four forage patch textures, two item
|
||
icons and two winter meshes. All names are bundled even from minimal Lua-only
|
||
fixture sources and included in the existing SHA256 package integrity inventory.
|
||
No Lua callbacks, harvest cooldowns, seasonal authority, metadata or placement
|
||
rules are changed. There are no new dependencies, downloads, credentials or
|
||
external material paths; existing bounded safe-path writers are retained.
|
||
|
||
### Seasonal verification
|
||
|
||
[flora-art.test.mjs](../tests/flora-art.test.mjs) checks all six models with the
|
||
unchanged finite/indexed/nondegenerate/outward-normal, positive-volume and closed
|
||
oriented-manifold-per-part validations. Added checks cover wood-only winter UVs,
|
||
fewer triangles, outward lateral forks, original trunk/bough geometry preservation,
|
||
clearance envelopes, all four opaque atlases and byte-exact bark. Generated PNG
|
||
bytes, exact OBJ text and complete package integrity are verified from fixtures.
|
||
The current Lua explicit-tile wiring and separate patch/item stem contract are
|
||
read-only assertions, not a Lua execution or engine test.
|
||
|
||
[world-art.test.mjs](../tests/world-art.test.mjs) checks the exact 95-image
|
||
manifest, determinism/uniqueness, restrained palettes, alpha/masks/padding,
|
||
Spring buds, non-green Autumn leaves, bare Winter twigs, bloom-free dormancy,
|
||
forage/decomposer silhouette differences, dynamic sheet dimensions and every
|
||
seasonal sprite cell. Existing material, animal, PNG and safe-output tests stay.
|
||
|
||
Run all three focused asset suites, including the unchanged
|
||
[assets.test.mjs](../tests/assets.test.mjs), with the absolute Node 24.22.0
|
||
executable. Optional codec regeneration is not required: existing Ogg
|
||
framing/CRC/hash tests remain active. These checks validate pixels, topology and
|
||
packaging, **not an in-engine seasonal visual acceptance pass**. No full engine
|
||
tests or concurrent engine launches are part of this art task.
|
||
|
||
Latest seasonal run: **27 passed, 0 failed, 0 cancelled, 0 skipped**, exit 0,
|
||
empty stderr, **37.37 seconds**, using the absolute Windows Node 24.22.0
|
||
executable. All five owned JavaScript/test modules passed `node --check` with
|
||
exit 0 and empty stderr. The 95-image **320×320** generated contact sheet was
|
||
inspected separately. Optional codec decode/regeneration was not configured;
|
||
Ogg framing/CRC/hash checks passed. Windows file-symlink privilege was unavailable;
|
||
directory-junction refusal passed (the suite reported no skipped tests).
|
||
|
||
</details>
|
||
|
||
</details>
|