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

7.5 KiB
Raw Permalink Blame History

Level editor – texture tools (editor.tools.textures)

Plugin TexturesEditorPlugin (phase 3). User guide: editor-guide.md#textures.

texture manager

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

  • 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

  • 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).