--- license: mit tags: - onnx - auto-rigging - skeleton-prediction - rigging - 3d - qtmesheditor library_name: onnx pipeline_tag: other --- # UniRig (skeleton stage) — ONNX export **ONNX re-export of the skeleton-prediction stage of [VAST-AI/UniRig](https://huggingface.co/VAST-AI/UniRig)** (*"One Model to Rig Them All"*, SIGGRAPH 2025 — MIT code + MIT weights, trained on Articulation-XL2.0 / CC-BY-4.0): an autoregressive transformer that predicts a full skeleton (joints + hierarchy) from raw mesh geometry — no template or markers needed. All credit for the original weights goes to VAST-AI-Research. Exported for **[QtMeshEditor](https://github.com/fernandotonon/QtMeshEditor)** (issue #408), powering `qtmesh rig --algo unirig`, the Inspector's **Generate Rig (AI)** button, and the `auto_rig` MCP tool — local inference via ONNX Runtime with a template-rig fallback. > The files QtMeshEditor downloads at runtime live in the shared > [`fernandotonon/QtMeshEditor-models`](https://huggingface.co/fernandotonon/QtMeshEditor-models) > repo under `unirig/`. This repo is the standalone model card + mirror. Note: UniRig's **skin-weight head is not included** — it depends on spconv/PTv3, which has no ONNX lowering. For ML skin weights see [`QtMeshEditor-skintokens-onnx`](https://huggingface.co/fernandotonon/QtMeshEditor-skintokens-onnx). ## Files | file | role | |---|---| | `encoder.onnx` | Michelangelo point-cloud encoder: `pc [1,N,3]` + `feats [1,N,3]` (normals), N ≤ 65536 → latent prefix | | `decoder.onnx` | ~350M-param causal-LM **KV-cache step** | | `embed.onnx` | token id → embedding | ## Inference contract 1. Surface-sample up to 65536 points + normals, normalise into a centred unit box (+Y up). 2. Run the encoder → latent prefix for the LM. 3. Greedy **constrained** autoregressive decode with a manual KV cache, masking each step to the tokenizer FSM's valid next tokens (a documented simplification of upstream's beam+sampling — deterministic, still yields a valid tree). Safety cap 2048 tokens; generation ends at EOS (typically ~180 tokens). 4. Detokenize: 256 coordinate bins over `[-1,1]`, `undiscretize(t) = (t+0.5)/256*2-1`; vocab 267 (`branch=256`, `bos=257`, `eos=258`); per-bone records `[branch?, parent xyz, joint xyz]` → joints + parent indices. Match branch parents to the nearest earlier joint rather than requiring exact bin equality — real decodes frequently miss the exact bin. ## Reproducing `scripts/export-unirig-onnx.py` in the QtMeshEditor repo (one-time, offline; via `optimum` with a hand-rolled KV-cache fallback). ## License MIT (same as the upstream code and weights). Training data: Articulation-XL2.0 (CC-BY-4.0) — credit VAST-AI-Research.