mesh-avatar-studio
Turn one illustration into an animated 2D mesh avatar with a coding agent and a local editor
Editor and toolkit for the MXW asset family: 3D meshes, skeletons, textures, sprites and item data.
DesignJavaScript · PythonGitHub @GomesForgeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
An editor and toolkit for the MXW asset family — a container
format and its companions, from an early-2000s application whose
servers are long gone. It opens the files, shows them in 3D, lets you
change them, and writes working files back out.
There is a Tutorial button in the editor itself, in violet at the
top. Twelve sections with diagrams, from the shape of the window to
four worked examples start to finish.
Nothing here ships any asset. These are tools; bring your own files.
Open the link above, or index.html locally. No build step, no server.Read the full README ↗An editor and toolkit for the MXW asset family — a container format and its companions, from an early-2000s application whose servers are long gone. It opens the files, shows them in 3D, lets you change them, and writes working files back out.
There is a Tutorial button in the editor itself, in violet at the top. Twelve sections with diagrams, from the shape of the window to four worked examples start to finish.
Nothing here ships any asset. These are tools; bring your own files.
| format | holds | status |
|---|---|---|
.bin / .MXW | 3D meshes, skeletons and textures | read + write |
get_item_list.bin | the server's index of every item | read + write |
.gra / .spr | 2D sprite frames and animations | read + write |
.obj | geometry, in and out of any modeller | read + write |
.eft | effects | first group decoded, not opened |
map.spr, block.spr | map tile grids | grid decoded, not opened |
Open the link above, or index.html locally. No build step, no server.
The layout follows the shape of an image editor: file and export actions on one bar across the top, which changes with the kind of file open; the drawing tools down the left; the canvas or the 3D view in the middle; the panels that describe and change what is open docked on the right; and one status line along the bottom.
.bin, body .bin or .MXW files on the pagemodel tab, so
the file does not go away while you edit it — move between the
two as often as you like, and nothing is discarded until you apply or
close the edit. A dot on the tab means the edit is still unappliedfit to frame the whole thing, and its corner to
resize. It holds the renderer's own canvas rather than a second
renderer, so there is one GL context and one copy of each texture.obj, so geometry can go through any
modeller. The round trip is lossless against this project's own
export: every vertex and texture coordinate comes back identical.
Importing into a loaded file keeps the textures, texture names and
material bindings, and says plainly what an OBJ cannot carryA badge in the status bar says whether the file currently writes back byte-identical, so you always know if you have changed anything.
The characters face −Z, measured rather than assumed — see docs/mxw-format.md. Front, Face and Whole look from that side, so the model faces you.
The meshes carry a skeleton — 74 bones on the boy, 75 on the girl, with a vertex range per bone — but no animation. Both bodies parse with no leftover bytes at all, so nothing is being skipped: the file holds the hierarchy and the bind pose and stops there. Whatever plays a motion reads it from somewhere else.
The rig is real: the bone order, the parent chain, the rest pose and
which vertices follow which bone all come out of the file. What the six
i16 per bone meant was derived rather than assumed — see
docs/motion-format.md — and the joints
land where a skeleton should have them.
ST stand, WA walk, MO carrying, PA hit, PU
push, TH throw, DD down, WI win — then run, sit, wave,
nod and the rest. Scrub the frame, set the speed, loop or notpython/check_motion.py measures the torso angle, the feet against
the floor, the angle at each knee, the hands' clearance from the
torso and the head's height, frame by frame, against that body's own
bind poseThe action set is read from the files; the joint angles are ours. No motion data for the 3D avatars survives anywhere we can find — the meshes carry no keyframes, and docs/motion-format.md lists everything ruled out looking for it. What does survive is the 2D sprite sets, and those gave the vocabulary and the timing. They could not give the angles: a different figure, one projection, a few dozen pixels tall. When a real motion file turns up it plays through the same player, because it is the same thing — a rotation per bone per frame.
Drop a .gra, .spr or .eft and the page switches to sprite mode.
[ and ] size the brush, + and − zoom, alt picks a
colour without leaving the brush, and the colours you have used stay
one click awaypython python/gra.py <file> inspect
python python/gra.py --png <file> a PNG per frame
python python/gra.py --strip <file> one PNG of every frame
python python/gra.py --check <file> prove the round-trip
python python/mxw.py <file> [...] inspect
python python/dump_skeleton.py <file> the bone tree, and what each bone moves
python python/mxw.py --obj <file> write .obj per mesh chunk
python python/mxw.py --gif <file> write every texture
python python/mxw.py --check <file> [...] prove the round-trip
from mxw import MXW
m = MXW(open('1210001.bin', 'rb').read())
m.meshes[0].textures # ['girl_01', 'girl_01_f']
m.skeletons[0].bones # 75 bones
m.replace_gif(0, open('new.gif', 'rb').read())
open('out.bin', 'wb').write(m.write())
python/get_item_list_decoder.py reads and rebuilds the item index.
For meshes and sprites, yes, and it is measured rather than hoped.
| class | read | written back identical | editable here |
|---|---|---|---|
mesh .bin .MXW | 50 | 50 of 50 | geometry, textures, texture names, material bindings, mesh id |
sprite .gra .spr | 1535 | 1535 of 1535 | frames, colours, pixels |
| item index | 1 | identical | through python/get_item_list_decoder.py |
effects .eft | first group only | no | no |
| map block and layout | recognised | no | not yet |
| encrypted caches | no | no | no |
That second column is the one that matters. A file you have not changed writes back byte for byte, which means the writer agrees with the reader on every field, including the ones this editor never shows you. Change one thing and only that thing moves.
What is not ready. Most effects are .eft, 130 files against 27
.gra in the same folders. The first group of an .eft decodes and
the section after it does not, so those are refused rather than opened
and written back broken. Map grids are recognised but have no editor
yet. The encrypted caches sit at 8.000 bits of entropy per byte.
python python/test_corpus.py <a directory of real files>
It reads every file, writes it back, compares byte for byte, checks the invariants the format implies, then simulates edits and re-reads the result. Read-only on the directory.
Over the 1611 distinct files available while this was written:
| meshes read | 50, all byte-identical |
| sprites read | 1535, all byte-identical |
| frames | 8732 |
| pixels checked | 36.9 million |
| problems | none |
Everything else in those folders is classified as a different format rather than counted as a failure: 18 effect files, 2 map layouts, 2 map block files, 1 interface file, the item index and 2 installer files.
Round-tripping on its own only proves the reader and writer agree; both could share a wrong assumption. That is what the invariant checks and the edit simulations are for — and they are what caught a header field being overwritten with zero, and the item index being accepted as a mesh and rewritten wrongly.
index.html loads each script as name.js?v=N. The page and the
scripts are separate cache entries, so without that token a browser can
pair a fresh page with yesterday's script — which is exactly how
"no file open" once ended up sitting beside an open file. Bump N on
every script change, in all the tags at the bottom of index.html.
Issues and pull requests are welcome. main is protected: changes land
through a reviewed pull request.
What would help most right now is the dress room and character export — see the roadmap.
Community researchers got a long way into these files first, in forum
threads from 2008 to 2010. Segovia noticed in 2008 that item files
differ at offsets 0A-0B without knowing those bytes were the mesh id,
and Broomop had worked out the item index well before any of this.
MIT, see LICENSE. This project is unaffiliated with any rights holder, ships none of their files, and exists for interoperability and preservation. Every name in the format documentation is a literal byte string or file name found inside the formats themselves.
.bin files. 575 wearables, 32
faces, 2 bodies, 24 motions and 5 cameras, 16 MB