The crate stack

Hover or focus a crate to highlight it; each box links to its entry on the crates page. Dashed bands are outside the repo. Arrows show where data flows: the tool crates read the two file layers and write only the Unity layer; the runtime rig consumes FBX/glTF meshes; the surface crates wrap the same graph for the terminal, an agent host, and the browser.

How the crates stack Five bands: the FBX layer and the Unity-YAML layer on top; the diagnose/generate/transform crates below them; the runtime rig, preview and OSC crates; and the CLI, MCP, wasm and testkit surface at the bottom. Unity + VRCSDK upload and the running VRChat instance sit outside, on the right. readread · editgenerated assets →upload → liveRawMesh 1 · 3D asset layer — .fbxarmature · bind poses · skinning · blendshapesavatar-fbx — binary read + writeavatar-fbxbinary read + writeavatar-armature — humanoid map · repairavatar-armaturehumanoid map · repairavatar-mesh — RawMesh interchangeavatar-meshRawMesh interchange2 · Unity / VRChat layer — UnityYAMLdescriptor · animators · menus/params · prefabs · packagesunity-yaml — read · splice-editunity-yamlread · splice-editunity-asset — .controller / .animunity-asset.controller / .animvrc-descriptor — descriptor · menusvrc-descriptordescriptor · menusvpm — project discoveryvpmproject discoveryunitypackage — gzip+tar read · extract · testbedunitypackagegzip+tar read · extract · testbed3 · Diagnose · generate · transformreads layers 1 + 2, writes layer 2avatar-lint — VRC001–VRC062avatar-lintVRC001–VRC062avatar-stats — performance rankavatar-statsperformance rankavatar-anim-gen — clips · FX · params/menusavatar-anim-genclips · FX · params/menusavatar-migrate — SDK2→SDK3 · physboneavatar-migrateSDK2→SDK3 · physboneUnity + VRCSDK — not oursbuild & upload (interactive login)4 · Runtime & previewrenderer-agnostic rig · wgpu preview · live OSCavatar-gltf — glTF → RawMeshavatar-gltfglTF → RawMeshavatar-pose — posing · IK · skinavatar-poseposing · IK · skinavatar-input — tracker inputavatar-inputtracker inputavatar-render — wgpu · headless/viewavatar-renderwgpu · headless/viewosc · osc-gestures — params · gesture daemonosc · osc-gesturesparams · gesture daemonRunning VRChatOSC in :9000 · out :9001 · OSCQuery5 · App & agent surfaceone binary; the same graph over MCP and in the browseravatar-cli — the avatar binaryavatar-clithe avatar binaryavatar-mcp — stdio MCP serveravatar-mcpstdio MCP serveravatar-web-analyzer — wasm · the inspector pageavatar-web-analyzerwasm · the inspector pageavatar-testkit — goldens · synthetic FBXavatar-testkitgoldens · synthetic FBX
Twenty-two crates on five bands. Package names are avatar-<slug>; the diagram drops the prefix where it is obvious.

The pipeline, end to end

  1. Unpackavatar unitypackage extract turns a shipped .unitypackage into an FBX + Unity project tree (format).
  2. Diagnose & fix the 3D assetfbx inspect, armature check/fix (armature repair).
  3. Lint & rank the projectavatar lint and avatar stats (rules, metrics).
  4. Generate, edit, migrateanim-gen, toggle, asset set, migrate sdk3, physbone write Unity YAML with deterministic fileIDs or splice an existing asset in place.
  5. Preview offlineavatar render / avatar view (render).
  6. You build & upload in Unity via the VRCSDK.
  7. Drive at runtimeavatar osc and the analog-gesture daemon (OSC runtime).

This repo is file transformation / analysis / validation / generation plus an OSC runtime daemon — not a Unity replacement.

Layer 1 — the 3D asset (.fbx)

Armature/skeleton, bone names, bind poses, skinning weights, blendshapes. This is where "the armature isn't set up right" lives. Fully ours in Rust on top of fbxcel — native binary read and write. The write-back landed in M3 (avatar_fbx::FbxDocument), resolving the project's long-standing biggest risk.

Ground rules

  • FBX support is binary only (FBX 7.x, the Autodesk/Unity/Blender default). ASCII FBX must be re-exported as binary.
  • Objects are edited by FBX object id (skin/anim refs are by id, not name), so renames are safe.
  • Bind comes from TransformLink/inverse-bind, never recomposed from Lcl + PreRotation.
Known limit
A rewritten full skinned avatar imported invisible in Unity 2022.3 (undiagnosed). Renames are safe; treat any other written FBX destined for Unity as unverified, and prefer the Blender-script route for geometry-level repairs. See armature repair.

Crates: avatar-fbx, avatar-armature. See docs/reference/armature-repair.md and docs/reference/rig-runtime.md.

Layer 2 — the Unity / VRChat project (UnityYAML)

.anim, .controller, .asset, .prefab, scenes, .meta: the Avatar Descriptor, humanoid bone mapping, animator layers, expression menus/params, blend trees, gesture keyframes. Three modes of access, in increasing order of risk:

ModeRiskHow
Read / lintlowStructural recognition of VRChat assets (no hardcoded script GUIDs) + a guid→path .meta index for cross-asset references. avatar-unity-yaml, avatar-unity-asset, avatar-vrc-descriptor.
Edit in placelowEditableUnityFile span-splices value and structural changes into the raw text, so fileIDs, references, key order, and formatting survive round-trip (editing Unity YAML). This is what makes whole-avatar operations safe: migration retypes the descriptor at its existing fileIDs; PhysBone tuning rewrites one component body.
Generatethe hard partAssets Unity accepts need correct fileIDs, field names, and GUID references. Solved (M4) with a faithful hand-written YAML emitter and deterministic FNV-1a-seeded fileIDs; verified by a headless Unity import gate (asset generation).
A Unity GUID is 32 hex chars and must contain letters in test fixtures: an all-digit "guid" parses as a number and silently breaks resolution. SDK3 script references are DLL class hashes, not 11500000.

Layer 4 — runtime & preview

The renderer-agnostic runtime rig (mesh, gltf, pose, input) loads a rig and drives it; render draws it headlessly with wgpu or in a window; osc / osc-gestures talk to the running game. glam (f32) is confined to these crates and never reaches the diagnose / generate / OSC graph — an invariant cargo tree verifies.

What is not ours — build & upload

The VRCSDK control panel pushes to VRChat servers. It needs interactive VRChat-account login and a Unity license that is painful in containers — effectively Windows-only. We deliberately stop here: we generate assets you drop into Unity, you build and upload there, and then the avatar osc layer drives the running avatar's parameters at runtime.

The one place we reach into Unity is the optional headless Unity acceptance workflow, which imports a repaired FBX and generated assets inside a GameCI Docker image to assert they configure and parse cleanly — the "last mile" the Rust tests can't cover. It self-skips until a Unity license secret is configured.

Milestones

MilestoneScopeStatus
M0Workspace scaffolddone
M1Armature diagnosis, hierarchy-aware humanoid mappingdone
M2Project SDK3 linting (avatar lint)done
M3Armature repair + native binary FBX write-backdone
M4Asset generation (avatar-anim-gen)done
M5OSC runtime + analog-gesture daemonOpenXR input pending

The runtime rig layer, wgpu preview, Unity-YAML editing, migration + PhysBone tuning, the MCP server, and the browser inspector landed alongside M4/M5. Full detail in PLAN.md.