Repository navigation
Blender Build Pipeline
Every UO equipment build ends in Blender: the item is bound to the canonical rig, animated through the original
actions, rendered through the UO camera with the body as a holdout, and encoded as a UOFiddler .vd file.
Content Studio and Fit Lab both drive this same pipeline.
Source: tools/uo-content/README.md, docs/body-occlusion.md.
The pipeline uses UO_Model3D v13 by Levy, installed with pipeline.py setup --source <folder> (see
Installation). The October 2026 update is supported: the same body topology, a 112-bone rig
UO_Rig (v13's 108 plus weapon1h.R, axe2h.L, bow.L, polearm.L), new lighting and a renderer with a native
256x256 canvas.
| Constant | Value |
|---|---|
| Canvas | 256 x 256 px |
| Anchor (ground origin) | (128, 192) |
| Scale | 36 pixels per metre, camera orientation preserved |
| Directions | 5 stored (0–4); the client mirrors 5, 6, 7 from 3, 2, 1 |
| Timeline | UO frame i = scene frame 1 + 3i |
| Facing | Driver -d*pi/4 on the rig's Z rotation |
item mesh ──► place on body region ──► bind to UO_Rig ──► apply fit + corrections ──► hide body under clothes
──► for each action x stored direction: pose, render with body holdout ──► PNG frames ──► .vd ──► validate
-
Place and bind. Automatic placement at the slot's body region, or your supplied world coordinates. Helmets
bind rigidly to the head, clothing gets surface-weight transfer, cloaks/skirts use cloth templates; pack parts use
their mapping (
skinnedorrigid). - Fit. Slot fit, item and left/right offsets and scoped corrections from Fit Lab are applied to rest geometry before animation, in Blender XYZ metres/degrees.
-
Hide body. With
hide_bodyenabled, body faces under the part are deleted from the fitting body. The count is written toscene-report.json. - Render. Each action/direction block is rendered from the same untouched base geometry.
- Encode and validate. Frames become a VD file, and every frame's alpha and anchor is checked by SpriteMotion's own independent MUL decoder.
The body is a holdout: it hides parts of the item behind it but never appears in the item image. Body poking through clothing would therefore cut holes in the item sprite, which is why body hiding exists.
- A pristine copy of the complete animated body (torso included) decides final visibility, even when the fitting body has hidden faces.
- Clothing mode allows a bounded contact tolerance only where the nearest body and item surfaces are the same anatomical region; left and right stay distinct.
- Body mode has no garment allowance. None disables body masking.
- Rigid items do not get the mesh-deforming push-out solver.
- The upstream original-sprite outline correction and mounted horse masking remain in place.
python tools/uo-content/pipeline.py rebuild <job> --adjustments <lab-adjustments.json>The rebuild compares the resolved fit for every existing block and renders only blocks that changed, into a new
validated revision. Untouched VD blocks stay byte-identical; the source build is preserved. A failed revision
stays under staging/ and is not promoted.
A rebuild is refused when the canonical model, renderer code, source meshes or palette changed since the source
build (render_fingerprint and source_fingerprints are recorded per job). Make a fresh build in that case.
workspace/ultima-online/content-studio/jobs/<id>/ holds item.blend, item.vd, render/clothing/ PNGs,
review/ atlases and viewer, inventory.png, contact-sheet.png, validation.json, scene settings, input
provenance and import-package.zip.
Import the full item.vd into an unused people/equipment animation slot.
Both commands write new files in a fresh folder, with source/output hashes and a verification report. Your
source client is never modified. Pass your own UO client folder (the same one as SPRITEMOTION_UO_SOURCE).
python tools/uo-content/client_import.py stage --vd <job>/item.vd --client <client-folder> --body <unused-animation-id> --out <new-staging-folder>
python tools/uo-content/equipment.py --job <job> --client <client-folder> --body <unused-animation-id> --graphic <unused-static-graphic-id> --server modernuoThe second command also stages inventory art in art.mul/artidx.mul, clones a known equipment tiledata record with
the new animation ID and name, and generates a cosmetic ModernUO or ServUO item class (no combat stats).
- Both reject animation IDs that are already used; equipment staging also rejects occupied art/tiledata.
- Animation ID and static graphic ID are different numbers.
-
artLegacyMUL.uopcan shadow MUL art; the tool reports this and gives youinventory.pngfor a UOP importer. - Not authored automatically:
Body.def/Bodyconv.def/Equipconv.defrules, custom paperdoll gumps and the female-body conversion. - Generated server code must be compiled in your server and tested in game. Staging does not update a running shard.
- A failed staging may leave a partial output folder; fix the error and use a fresh destination.
tools/vd/ (contributed by Levy from his UO_Model3D project) reads and checks VD files:
python tools/vd/mul2vd.py "<your UO client folder>/anim.idx" "<your UO client folder>/anim.mul" <out dir> 400 0x1F3
python tools/vd/vdtool.py info <out dir>/anim_0400.vd
python tools/vd/vdtool.py extract anim_0400.vd frames/
python tools/vd/vdtool.py pack frames/ new.vd
python tools/vd/vdtool.py verify anim_0400.vd new.vdFormat in brief: a header (magic 6, type 2 = people/equipment, 35 actions), one index entry per action x 5 stored
directions, and per block a 256-color 15-bit palette plus RLE frames. Color 0x0000 is transparent, so pure black
is written as 0x0001. Alpha is on or off; soft edges and glows get flattened.
- Running a fresh physics cloth bake is not automated.
- Mounted occlusion uses the source project's horse proxy and needs visual review.
- Not yet verified on the updated model: a full 35-action lab build, mounted actions, cloaks with the torso mask, weapons on the new weapon bones. See UO Parity Roadmap.
SpriteMotion is MIT licensed · Repository · License · Third-party notices
Not affiliated with or endorsed by Electronic Arts or Broadsword. Ultima Online is a trademark of its owners. No game data is distributed with this project.
Start
Guides
- Fit a slot in Fit Lab
- Render an item end to end
- Clean up with masking
- Troubleshoot a failing step
- Take content into GUO
Tools
Reference
Project