Brief: modelling a group of UO objects
You model and bake one group of world objects for UO Remastered, a remaster of
Ultima Online with 2.5D pixel-art sprites lit per pixel. Work in the git
worktree /Users/aler/projects/daily/day-1014-uo/.claude/worktrees/uo-objects
(branch feat/uo-objects): run every command from there, with plain relative
paths, never cd elsewhere. Other agents model other groups in parallel in
the same worktree.
Read first
docs/uo-objects.md: the catalog, conventions (scale, facings, walls, multi-tile origin) and your group's list. Model every sprite listed for your group.docs/asset-pipeline.md§1 Modelling (Principled BSDF only, relief as geometry, closed meshes, think in texels, emission).tools/blender/models/object_kit.py: shared helpers (param,material,pal/PALETTE,flame,glow,box,span,cylinder,post,cone,sphere,torus,lathe(caps=Falsefor open shapes),ring(hoops, rims),rod/beam(between two points),extrude(2D outline prism),dome(rests on the ground),footprint_root(the_s/_erotation for multi-tile objects),empty,cutter/subtract,add_bevel; constantsWALL_PLANE= 0.425 andWALL_FACE= 0.35, the front of a north wall where wall-mounted items hang). Look at an existing group kit before writing helpers (e.g.lights_crafting/lc_kit.py: fire, candles, coals, masonry;civic_dungeon/civic_kit.py;nature/nature_kit.py: blobs, crowns). Note: the preview's lit row has no environment light, so metals look darker there than in the game (toon mode lights metals diffusely): don't lowermetallicto compensate.- Reference models:
tools/blender/models/wallkit.py+wall_x_window.py(walls, booleans, trims, glass glow),crate.py,brazier.py(flames),tree.py,character.py(scale).
What to produce
-
Scripts in
tools/blender/models/<group>/, one per asset or one per family selected byparam(...). Each must build its model into an empty scene with no other input. Import the kit withimport os, sys sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) from object_kit import ... # noqa: E402A group-private helper module (e.g.
<group>/<group>_kit.py) is fine. -
Manifest entries in
assets/manifest.d/<group>.toml(create it), e.g.[[asset]] name = "chair_wood" model_script = "../../tools/blender/models/furniture/chair_wood.py" directions = 4 [[asset]] name = "wall_stone_window_x" model_script = "../../tools/blender/models/walls/wallset_stone.py" params = { piece = "window_x" }Multi-tile objects with a front (beds, long tables, fireplaces, troughs) do not use
directions = 4(rotating about the origin would leave the footprint). Like UO, bake one asset per orientation,<name>_s(front south) and<name>_e(front east), from one script withparams = { facing = "s" }/"e", each with its ownfootprint([w, h]and[h, w]), origin at the north-west tile centre.Keys:
directions(1 | 4),footprint = [w, h]for multi-tile objects,params(seen by the script asparam("piece")),padding(default 2). -
Baked sprites in
assets/sprites/<name>/(the baker writes them).
Modelling rules
- 1 unit = 1 tile; origin on the ground at the tile centre (multi-tile: centre
of the north-west-most tile, extending east +X and south −Y);
Z >= 0; front faces −Y (south). Fit inside the footprint (a 1x1 object ≤ ~0.9 wide). - Think in texels: a tile is 32 px wide, 1 texel ≈ 0.045 units. Build on
the texel lattice (
pixel_kit.py, rules indocs/asset-pipeline.md): whole texels and levels, no sub-texel details or bevels. Make silhouettes bold, exaggerate key features (a chair back, a sword hilt), use colour contrast between parts (dark frame / light panel, metal vs wood) rather than fine geometry. - UO look: muted, warm, readable; use
PALETTEso groups match. Metalsmetallic = 1; glass/water low roughness; flames and glowing things use emission (flame(),glow()), nothing else emits. - Bevel hard edges (
bevel=/add_bevel) so normals catch highlights; smooth-shade curved parts. Closed meshes; attached parts overlap a few mm. - Small items lying on the ground (weapons, tools, goods) are modelled at a readable, slightly exaggerated size (a sword ~0.7 long, lying flat or propped), still within the tile.
Bake and review
Binaries are built; never run cargo. From the worktree root:
target/release/uo-baker bake-all --only chair_wood --only stool --preview
python3 -I tools/gallery.py --group assets/manifest.d/<group>.toml -o <scratch>/<group>.png
bake-all reads assets/manifest.toml plus every assets/manifest.d/*.toml.
Validation must pass for every sprite. Look at the gallery (albedo row and lit
row per facing; --max-frames, --scale 2 for detail; write sheets and
crops to the scratch directory you are given, never into the repo) and
iterate until each object reads clearly as what it is at this size, sits on
the ground at the right scale, and the facings are right (South frame shows
the front). Compare scale against assets/sprites/character and
assets/sprites/crate in the gallery.
Constraints
- Only create/edit files of your group:
tools/blender/models/<group>/**,assets/manifest.d/<group>.toml,assets/sprites/<your sprites>/. - Do NOT edit
object_kit.py, other groups' files,assets/manifest.toml, docs,CLAUDE.mdor Rust code; put suggestions in your report. - Do NOT use the Blender MCP / live Blender; bake headless only.
- Never kill processes you did not start; no git commands that change state.
Report
Files created, sprites baked (all names), anything skipped and why, and any
object that still does not read well; plus suggestions for object_kit.py.