This commit is contained in:
2026-07-04 18:03:35 +02:00
parent 605d0e73b4
commit e0fdf2ff16
8 changed files with 488 additions and 52 deletions
+92 -1
View File
@@ -96,6 +96,58 @@ Keep the terrain/crosser vocabulary **small and deliberate** per tileset.
Visual water/void may dip (e.g. canal at Z = 0.3) **only under a non-walkable
walkmesh** — never a playable under/over space (matches `KICKOFF.md`).
### 2.3 How a builder places tiles — TWO systems (this defines "paintable")
The toolset has two **independent** placement systems. Confusing them is why a
palette can list tile names that refuse to place (the wgt01 2026-07-04 symptom).
**System 1 — Paint Terrain / Paint Features (this lays the ground).** The builder
picks a **terrain** (or a **crosser**) and brushes it across the grid. The toolset
then **auto-selects which tile model to drop** by matching the four **corner
terrains + heights** (and edge **crossers**) against the neighbours — the builder
never picks a specific model. This is how streets/plazas/canals actually get laid.
It needs, in the `.set`:
- `[TERRAIN TYPES]` / `[CROSSER TYPES]` — the paintable vocabulary;
- enough `[TILE#]` entries that **every corner combination the brush can produce**
is covered by at least one tile (else the brush has nothing to place);
- `[GENERAL]`: `Default` (terrain that fills a new area), `Border` (area edge),
`Floor` (the terrain the **eraser** paints), `HasHeightTransition=1` (turns on
Raise/Lower).
`[PRIMARY RULES]` only *refine* auto-picking (cascading corner transitions) and are
**optional** — stock custom sets ship `Count=0` (verified: `wut01.set`). **Eraser,
Raise/Lower, and fill are built-in toolbar actions driven by those `[GENERAL]`
fields — they are not palette entries.**
**System 2 — the palette (`.itp`): Features & Groups only.** The right-side
"Standard" tree click-places **prefabs**: a *feature* (1×1) or a *group* (N×M). A
palette entry is placeable **only if it is backed by a `[GROUP#]` in the `.set`**
the entry's `RESREF` is that group's **first-tile** model name and its `NAME`
matches the group's `Name`; the toolset locates the first tile in `[GROUPS]` and
pulls the rest from there. A palette entry pointing at a **raw tile with no
`[GROUP#]`** shows its name but **cannot be placed**.
**Verified palette shape** (`wut01palstd.itp`, GFF `ITP `): `MAIN` = a few
**category** structs, each `{ID byte, STRREF (dialog.tlk folder label), LIST}`;
leaves are `{NAME cexostring, RESREF}`. `RESREF` means different things per folder:
| Folder (its `STRREF`) | Leaf `RESREF` is… | Holds |
|---|---|---|
| **Terrain** (8282) | the **terrain/crosser _name_** (`Sand`, `Bridge`, …), **plus a magic `eraser` leaf** (STRREF 63291, no `NAME`) | the paintable terrains + crossers + eraser — i.e. the "Terrain" section a builder expects |
| **Features** (63261) | the feature's **first-tile resref** | 1×1 prefabs; `NAME` == its `[GROUP#]` `Name` |
| **Groups** (63262) | the group's **first-tile resref** | multi-tile prefabs (3×3, 4×1, 3×5…) |
So the "Terrain" section (paintable terrains + eraser) **is category-2 of the
`.itp`** — terrains listed by *name* with an `eraser` entry — not a list of models.
The three folder `STRREF`s above are stock dialog.tlk labels; reuse them.
**wgt01's bug (root cause):** its `.itp` was **one flat folder of 40 raw tile
resrefs** — no Terrain folder, no `eraser`, and the `.set` had `[GROUPS] Count=0`.
So none of the 40 entries is a feature/group → names render but nothing places; and
with no Terrain folder there's no paint/eraser UI at all. **Fix:** emit the
3-folder palette (Terrain+eraser / Features / Groups) and add `[GROUP#]` entries
for anything click-placeable, then lay ground via **Paint Terrain**, not by
clicking tiles. See `wgt01/STATUS.md`.
---
## 3. The tile model (`.mdl`)
@@ -247,7 +299,7 @@ and `Tile0..` indices **ordered bottom-left → right → up → top-right**. A
| File | Purpose | Notes |
|---|---|---|
| `<prefix>.set` | tileset definition | CRLF, references tiles by index |
| `<prefix>palstd.itp` | toolset palette tree | **GFF binary**; references tiles **by name** — must match `.set` |
| `<prefix>palstd.itp` | toolset palette tree | **GFF binary**; 3 folders — Terrain+eraser / Features / Groups (§2.3). Feature/Group leaves must be backed by a `[GROUP#]`; Terrain leaves are terrain/crosser **names**. |
| `*.mdl` | tile models (ASCII) | one per tile/variant |
| `*.wok` | tile walkmeshes | baked from the AABB node |
| `*.tga` / `*.dds` | minimap icons (`ImageMap2D`) | ≥16×16 px, name ≤16 chars |
@@ -291,6 +343,15 @@ tracks Blender 3.x4.x, **not** nixpkgs' Blender 5.x. Do not "upgrade" it.
- **blender-mcp-nwn** (see `/nix/nixos/blender-mcp-nwn`) — MCP server exposing
`run_blender_script`, `read_blend_metadata`, `export_for_nwn` so Claude can
drive Blender 4.0.2 headless against these `.blend` sources.
- **Community authoring/inspection tools** (not in-repo; handy for reverse-checking
our generated files against stock sets):
[SetEditor 0.85 (Jlen)](https://neverwintervault.org/project/nwn1/other/tool/set-editor-beta-085)
— GUI `.set` editor; [BioWare GFF Editor](https://neverwintervault.org/project/nwn1/other/tool/gffeditor)
— edit `.itp`/GFF; [NWN Explorer Reborn](https://neverwintervault.org/project/nwn1/other/tool/nwn-explorer-reborn)
— browse/extract stock BIFs (pull a reference `.set`/`.itp`);
[Basic Interior Tileset Template (Symphony)](https://neverwintervault.org/project/nwn1/hakpak/tileset/basic-interior-tileset-template)
— minimal working set to diff against. We can inspect any of these headlessly via
`nwn_gff` / `nwn_erf` instead of the GUIs.
- **wgt01 generator kit** (`wgt01/generator/`, driven by `wgt01/flake.nix` apps):
`build.py` (`nix run .#build`) builds the `.blend` tile library from the shared
`tiles.py` table; `export_mdl.py` (`nix run .#export -- <blend> <outdir>`)
@@ -383,6 +444,32 @@ area with the tileset and walk-test. Much faster than pack→reload; pack the
- **NeverBlender not installed** (other machines) → `export_for_nwn` gives you
OBJ/FBX + an `mdl.json` config, **not** a real `.mdl`. Use `make_wok.py` for an
ASCII `.wok` fallback, or install the addon; say so rather than claiming MDL.
- **NeverBlender flattens multi-material tiles.** A mesh with N materials exports
as **one** `trimesh` with a single arbitrary `materialname` — the per-face
grammar is lost. NWN wants one `trimesh` node **per texture**. Either split the
mesh by material into separate objects before export, or emit the `.mdl`
directly (wgt01's `make_mdl.py` does the latter, in pure Python).
- **A tile `.mdl` needs `tverts` + `bitmap`.** No UV map → NeverBlender emits no
`tverts`; a node-based material with no image → `materialname` (a `.mtr`
lookup) instead of a loadable `bitmap`. A trimesh with faces but no tverts/
bitmap won't load → **blank tileset name + "could not create area"** in the New
Area wizard. Give each material a `bitmap <resref≤16>` and ship the `.tga`.
- **Toolset needs `<set>palstd.itp`** to register a tileset. Every stock/custom
set ships one (GFF `ITP `: `MAIN` list of category structs, each `ID` byte +
`LIST` of tile entries). Missing → set won't register.
- **`.itp` entries need a local `NAME` cexostring, not just a `STRREF`.** Sets
without a tileset TLK give each leaf `{NAME (cexostring), RESREF}` (verified
vs `wut01palstd.itp`) — the `NAME` is the palette label. An entry with only
`STRREF 0xFFFFFFFF` (or no name field) renders **blank**.
- **A named `.itp` leaf still won't place unless it's a real terrain or a
`[GROUP#]`-backed prefab (§2.3).** The palette only *paints terrain* or
*click-places features/groups*; it does not place raw tile models. A flat
palette of tile resrefs (wgt01's first cut) shows names but nothing is
selectable/paintable — you need a **Terrain** folder (terrains-by-name +
`eraser`) and `[GROUPS]` for any click-placeable tile. Ground is laid by **Paint
Terrain** (corner-matching auto-picks the model), not by picking tiles.
- **`WalkMesh=` in the `.set` is a label, not a file** — 95k stock tiles use
`msb01`, others use author codes; per-tile walkmesh is matched by model name.
- **Don't upgrade Blender past 4.x** here — breaks NeverBlender/cleanmodels.
---
@@ -396,6 +483,10 @@ area with the tileset and walk-test. Much faster than pack→reload; pack the
- [Walkmesh — NWNWiki](https://nwn.fandom.com/wiki/Walkmesh)
- [Tile — NWNWiki](https://nwn.fandom.com/wiki/Tile)
- [Common Errors and Their Causes — nwn.wiki](https://nwn.wiki/display/NWN1/Common%20Errors%20and%20Their%20Causes)
- [SET (file format) — nwn.wiki](https://nwn.wiki/display/NWN1/SET) — terrain/crosser/rules/groups fields (§2.3, §4)
- [Tile Path Nodes — nwn.wiki](https://nwn.wiki/spaces/NWN1/pages/139689996/Tile+Path+Nodes) — pathnode types AZ/az, exit-to-region, alignment
- [Tutorial: Creating Custom Tiles (Mr X) — Neverwinter Vault](https://neverwintervault.org/article/tutorial/tutorial-creating-custom-tiles) — comprehensive walkthrough (Cloudflare-gated to WebFetch; open in a browser)
- [Resources for custom NWN tileset creation — NWN Workshop](https://www.nwnworkshop.org/forums/topic/resources-for-custom-nwn-tileset-creation/) — tool/tutorial index (SetEditor, GFF Editor, NWN Explorer, templates)
_Verify specifics against the wiki before relying on them; correct this file when
you find drift._