Repository navigation
Guide Export to GUO
Goal: fit and render equipment with SpriteMotion, then stage it into GUO as data the GUO client and a private shard can load, without ever writing to your UO install.
Status (2026-10-06). Fitting inside GUO and staging through
tools/uopackwork today. A direct importer for SpriteMotion build jobs (uopack from-job) is coming: it is GUO's next integration milestone, built on a shared SpriteMotion package. Until then, a finished SpriteMotion build goes to the classic client through its.vd(see Render an item end to end), and GUO stages content through the routes below.
| Owns | |
|---|---|
| SpriteMotion | fitting, the canonical body, Blender renders, validated frames |
| GUO | UO IDs and range policy, tiledata, staging into MUL/UOP, the client and shard tests |
GUO pins one fixed SpriteMotion revision and installs it into its own isolated Python environment. It never updates your SpriteMotion checkout or your sidecar.
- A GUO checkout with its usual local configuration (
launchers/_shared/config.batandconfig.local.bat). -
UO_CLIENT_DATApointing at your UO client data. It is only ever read. - Python 3.12 (GUO's Python).
- For fitting: a prepared SpriteMotion workspace (the UO_Model3D body installed and a pack export; see
Installation), with
SPRITEMOTION_ROOTandSPRITEMOTION_FIT_PACKset in GUO's configuration.
Open the GUO editor, select the Fit Lab bottom tab and click Open Fit Lab. It is the same Fit Lab as in the browser: saved fits, backups and Blender jobs are shared, and closing GUO doesn't stop a running render. First use downloads a pinned, checksum-verified Chromium runtime (about 1 GB) and prepares SpriteMotion's environment.
From a terminal:
python tools/spritemotion/run.py open
python tools/spritemotion/test_setup.pyFit the slot as in Fit a slot in Fit Lab.
GUO hands out numeric IDs from a range policy, so packs staged separately never collide:
python tools/uodata_write/run.py reserve --stage <stage folder> --pack <pack name> --statics 4 --bodies 2
python tools/uodata_write/run.py scan --stage <stage folder>reserve records the pack's item IDs, animation bodies and their paperdoll gumps (50000/60000 + body) in
<stage>/slots.json. A shard maintainer can override the policy with --ranges <file> or UO_DATA_RANGES.
uopack works like UOFiddler's export/import, but with checks: unpack writes PNGs plus a JSON sidecar per asset, and pack
writes them back into a staged set.
python tools/uopack/run.py unpack --what anim --ids <body id> --out <folder>
python tools/uopack/run.py pack <folder> --source <data dir> --stage <stage folder>
python tools/uopack/run.py roundtrip <folder>unpack and roundtrip read from --from <data dir>, which defaults to UO_CLIENT_DATA; pack --source names the
same folder so unchanged entries keep their original bytes. Animation folders hold one sidecar per action and direction (anim/body_0581/a00_d0.json), listing its frame PNGs
with their centres. New or repainted frames can be RGBA: pack maps them onto the group's palette, or builds one.
Every record is decoded again and compared with its PNGs before it is written.
From SpriteMotion's 2D outfit lab (the older atlas workflow), there is a dedicated route:
python tools/uopack/run.py from-outfit-lab <SpriteMotion workspace>/ultima-online/outfit-lab --out <folder> --ids "<JSON: {item key: {item: id, body: body}}>"
python tools/uodata_write/run.py outfit --stage <stage folder> --source <outfit-lab folder> --pack <pack name>From a SpriteMotion 3D build job: uopack from-job is coming. It will read the job through SpriteMotion's
transfer-artifact reader and produce a uopack folder like the ones above.
python tools/uodata_write/run.py verify --stage <stage folder>verify checks that every copied install file still hashes as it did when it was copied. The stage contains
files_override.txt for the client, and it is the folder a shard lists first in its data directories.
-
<stage>/slots.jsonwith your pack's reserved ranges. - A staged data set whose records decode equal to their PNGs, with
verifypassing. - Your UO install unchanged.
No screenshot here on purpose: staged data and decoded frames are made from your client data and stay on your machine.
-
Publishing a stage. A staged set contains data from your install (
contains_ea_data: true), so GUO's release and publishing tools refuse it. Share SpriteMotion fits, CC0 sources and code, not stages. - Reusing an example's IDs. Reserve a range per pack; never stage over the example shield's IDs.
-
Expecting the editor to update SpriteMotion. GUO uses its pinned revision unless
SPRITEMOTION_ROOTpoints at your checkout. Updating the pin is a deliberate GUO change, tested against the shared package. - Treating a fresh public install as a ready render workstation. The canonical body and asset catalogs are not in either public repository; prepare them as Installation describes.
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