Files
llamamod/notes/editor-tools-textures.md
T

122 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Level editor – texture tools (`editor.tools.textures`)
Plugin `TexturesEditorPlugin` (phase 3). User guide: `editor-guide.md#textures`.
![texture manager](img/editor/tex_manager.jpg)
## What it adds
| Item | Where | |
|---|---|---|
| Texture manager | panel `textures` (bottom dock, Ctrl+Shift+T; **Window** button = own 1300×760 frame) | pages, atlas, table, details, animation editor |
| Import texture… | File menu (replaces the built-in `file.importTexture`), panel toolbar | `ImportDialog` |
| Replace texture pixels… | Edit menu, panel toolbar | image → the selected texture's rectangle |
| Paint texture | tool `texpaint`, key **M** (own toolbar group) | `PaintTool` |
| Texture eyedropper | Edit menu, key **I**; Shift+click in the paint tool | |
| Checks | Build/Check | > 127 pages = error, entry on a missing page = error |
`TexState` holds the window's *current texture* (shared by panel, tool, eyedropper; selecting a
`TextureEntry` anywhere, e.g. in the outliner, makes it current).
### Texture manager (`TextureManagerPanel`)
* **Pages**: thumbnail, index, blend class of the page format (`Blend.ofPage`). Context menu:
export page PNG, replace page from PNG (scaled to 256×256, reduced with the page's class).
* **Atlas**: the page (nearest-neighbour, fit to the view until the wheel zooms) with all
rectangles: white = used, red = unused, cyan = animated, orange = current with 8 handles.
Drag inside the current rectangle = move, handles = resize (one undo step per drag:
`TextureLibrary.setRect` with a merge key). Click selects the smallest rectangle under the
cursor; double-click activates the paint tool; tooltips show size/usage.
* **Table**: #, preview, name (editable), page, rectangle, blend, faces (tile-model faces +
brushes), models, animation ("frame" = used as a frame). Filters: all / on this page / used /
unused / animated / imported; search by name or number.
* **Texture tab**: name, X/Y/W/H spinners, blend class (sets the entry flags), raw flags,
usage line, *Duplicate* (new entry with the same rectangle, e.g. for frames), *Select users*
(tiles whose models use it).
* **Animation tab** (`AnimEditor`): create/remove, type flip-book / scroll up / scroll down,
frame delay (≈ fps at 30 game frames/s), back and forth, frame list (+ − ◀ ▶), scroll band /
speed / top row, live preview (`AnimSpec.frameAt/offsetAt`).
Usage counts (`TextureLibrary.usage`): faces of TRAK tile models (`faces[i+4]`), brush string
parameters `"#n"`, animation targets and frames. Object (STPC) models are not counted – their
textures are in the same table, so "unused" means *not used by tiles/brushes/animations*.
### Import (`ImportDialog`, `ColorReduction`, `TextureLibrary.importItems`)
![import dialog](img/editor/tex_import.jpg)
* Size = longest side (16…256 or original), aspect kept; rectangles need not be powers of two.
* **Blend classes** (`Blend`), derived from the flag pairs of all original level WADs
(entry flags ↔ page flags & 0x7f, counts over 33 WADs):
| Class | entry flags | page flags | originals |
|---|---|---|---|
| Opaque | 0x0104 | 0x81 | 16 551 entries |
| Cut-out | 0x0406 | 0x84 | 1 774 |
| Semi-transparent (mode 1) | 0x1406 | 0x87 | 528 |
| Additive (mode 2) | 0x2406 | 0x85 | 649 |
| Darken (mode 3) | 0x3406 | 0x82 | 574 |
Others seen: 0x1104/class 6 (186), 0x2104/class 3 (38), 0x4406/class 5 (469), object-model
entries 0x8cc6/0xbbc6/0x9cc6/0xb8c6 (bit 15). Entry bits 12–14 = blend mode, 0x400 = has
transparent pixels.
* **Colour reduction** to ABGR1555: alpha < cut → 0x0000; with "keep half-transparent" alpha
< 192 → STP 0 (black becomes 0x0421, never 0); optional colour key (magenta / top-left pixel)
and 4×4 ordered dithering; opaque class ignores alpha. Preview = the game's 15-bit result.
* **Packing**: with "pack into free space" the importer searches pages of the same format
class for a free block (`FreeMap`, summed-area table over a 256×256 occupancy mask built from
all rectangles plus scroll bands), 8-pixel grid, largest images first; otherwise / if full it
appends pages (≤ 127). Optional 1-pixel border (edge pixels repeated) against filtering seams.
One undoable `Batch`: `SetProp pixels` of changed existing pages (the project stores only the
changed pixel runs), `AddNode` pages, `AddNode` entries (always appended – faces reference the
index).
* Village test: two 32×32 imports land in free space of existing pages, a 200×100 semi-transparent
one gets a new 0x87 page; the WAD builds and re-imports (`TextureLibraryTest`).
### Paint tool (`PaintTool`)
![paint](img/editor/tex_paint.jpg)
* Hover shows the face under the cursor (white outline) and model/face/texture/flags in the
status bar (plus "model shared by n tiles"). Face picking: `ctx.pickNode` → tile, then a
Möller–Trumbore ray test against that tile model's triangles (tile transform, export space).
* Click = assign the current texture; scope **Face / Same texture in model / Whole model**.
Shift+click or the eyedropper button / **I** = pick. The clicked face stays selected (orange).
* **Rotate** cycles the face's corners (v0,v1,v2 → v1,v2,v0; winding and plane unchanged),
**Mirror** toggles 0x20 (mirror U), **Other half** cycles 0x800/0x10 (`Geometry.faceUvs`: no
0x800 = corners (a,v1)(b,v1)(a,v0); 0x800 = (a,v1)(b,v1)(b,v0); 0x810 = (b,v0)(a,v0)(a,v1)).
* **Unique**: if the tile's model is shared, the edit first adds a copy of the model and points
the tile at it (one undo step).
* Brushes: the clicked side's normal (from three depth samples) picks the parameter `top`
(n.y > 0.7) / `bottom` / `side`, else the first texture parameter (`tex`, `post`, `trunk` …);
the value becomes `"#n"`. Rotate increments `texRot` (0–3) of the last painted brush.
## Texture animations (decoded, `AnimSpec`)
Loader FUN_00406e40 (16-byte runtime records at 0x581138, count 0x581134), update FUN_0040c6e0,
called once per rendered frame from FUN_0054b590 while the game is not paused. The runtime
texture record (20 bytes at 0x581154 + tex·0x14) holds the page and the UVs as floats
(+0xc / +0x10 = v0 / v1, divided by the page height at 0x58114c + page·0x20 + 8).
| Type | File payload | Behaviour |
|---|---|---|
| 8 flip-book | delay, n, pingPong, n × u16 texture | every delay+1 frames the texture's runtime record is replaced by the next frame's (loop or back and forth). 214 in the levels (torches, water, flags), e.g. `03 08 00 d0 03 …` |
| 1 / 2 scroll | band−1, speed, v0 | V range = v0+off … v0+off+band, off −= / += speed per frame, wraps in the band. 7× type 1, 2× type 2 (rivers, lava: `1f 02 01`) |
| 10 / 11 | band−1, speed, v0, n, n × u16 | scroll as 2 / 1, each wrap switches to the next / previous frame. Unused |
| 7 / 9 | u16 palette, u16 delay, u8 first, u8 last | palette cycling of 8-bit pages. Unused (no palettes) |
The editor viewport does not animate textures; the Animation tab previews them.
## Known limits
* Texture entries are never deleted (indices are referenced by faces and object models);
unused ones can be reused by moving their rectangle.
* Object (STPC) model faces cannot be painted.
* Painting a shared original tile model changes every tile using it unless **Unique** is on.
* The page format of an existing page is not changed by the importer; importing into a page of
another class is avoided.
Files: `src/main/java/com/eliasmueller/llamamod/editor/tools/textures/*`, tests
`TextureLibraryTest` (colour reduction, anim round trip of all village animations, packing,
build + undo), tour steps `TexTourSteps` (with `playtest.EdTour`).