Nodes/ComfyUI-UniMate/UniMate Mesh Preparation
ComfyUI Node

UniMate Mesh Preparation

Reading UniMate Mesh Preparation's report

By visualbruno·Created 21 days ago·Updated 21 days ago· 5
UniMate Mesh Preparation
  • model
  • asset
  • report
◄rigged_mesh—►
◄name►
◄face_right►
◄face_left►
◄force_rebuildfalse►

Every node in this pack is one step of a small pipeline, and this is the step everyone wants to skip and nobody should. UniMate Mesh Preparation is where your mesh stops being a mesh and becomes an asset - a skeleton the model can be conditioned on, with named joints, a known facing direction, and a canonical rest pose. It's also the only node in the pack that needs Blender, it takes about half a minute, and it produces a block of text that tells you whether the next three nodes will work.

Read that text. I mean it. The model is conditioned on the joint labels and the facing pair this step produces, so if the labeller gets either wrong you get a creature animating sideways or backwards and no error anywhere.

What it actually runs

The node shells out to UniMate's own data_process.rig_preprocess:

python -m data_process.rig_preprocess run --input <mesh.glb> --output_dir <cache> \
    --no_review --exp_dir <your run> --overwrite

Note the interpreter. That subprocess is UniMate's venv, not ComfyUI's - rig_preprocess needs bpy, which is a ~1 GB wheel pinned to a specific Blender version, and installing that into ComfyUI's environment to prepare a rig once per asset is a terrible trade. The pack resolves the interpreter for you: it uses UniMate's own venv/, or whatever UNIMATE_PYTHON points at. Same pattern as the Blender bridge in ComfyUI-SkinToken.

Output goes to ComfyUI/output/unimate_assets/<name>_<hash>/, holding a cond.npy and the canonical GLB. That hash is a sha1 of the file's contents plus the asset name and the facing overrides. Change the mesh, it re-runs. Re-run the graph, it doesn't - the report says (cached) and the node returns in milliseconds. That's the design: pay once per mesh, iterate freely on the prompt afterwards.

Then it loads cond.npy, counts the joints, compares against the checkpoint's limit, and writes the report.

Inputs and outputs

  • rigged_mesh - a forced string input, i.e. wire it from Mesh Loader. You can't type a path here.
  • model - the UNIMATE_MODEL handle from Model Loader; preparation needs the run for its config.
  • name - optional asset name, defaults to the file stem. Cosmetic, but it's what shows up in the report and the cache folder.
  • face_right / face_left - raw bone names of the facing joints. Leave empty to let the offline labeller auto-pick, which it does decently and not perfectly.
  • force_rebuild - boolean, wipes the cache folder and redoes the Blender pass.

Out come asset (UNIMATE_ASSET, wire it to both the sampler and the export node) and report, a string.

Reading the report

It looks roughly like this:

Rimuru: 41 joints (built in 27s)
facing pair: r=thigh_R l=thigh_L body_axis=True
labels: hip x2, knee x2, foot x2, ...

Three things to check, in order. The joint count against your checkpoint's limit - 71 on the v3 runs. Over that, the node fails here deliberately, with a message naming the number, rather than letting the sampler refuse silently thirty seconds later. Rig_preprocess will not prune for you, so the fix is deleting finger, face and accessory bones in Blender. That's the trade the pack made and it's the right one: fail loud and early.

The facing pair. r= and l= should be the joints pointing right and left on your creature. If preparation picked a left/right pair off a tail or a wing, the model reads the body as rotated and the whole animation comes out wrong - and there is no error. Override it with the raw bone names in face_right / face_left. Because those values are part of the cache hash, filling them in re-runs preparation automatically; leave one empty and the override is ignored, so fill in both.

The label vocabulary. This is literally what the model is conditioned on - the counts show what the labeller decided each joint is. A quadruped whose front legs got labelled as arms will animate like something that isn't a quadruped.

Traps worth knowing

Blender is only needed here. If rig_preprocess can't start, you get an explicit message about bpy and UNIMATE_PYTHON - point it at any interpreter that can import bpy and you're fine. The export node at the end of the graph deliberately doesn't use Blender at all (UniMate's own mesh-driving path is broken on Blender 4.4+), so you don't need a matching Blender version anywhere else.

Skeleton-only rigs can be sampled but not exported. If your source had no skinned mesh, rig_preprocess writes no canonical GLB, and the export node has nothing to write animation onto. You'll get a perfectly good .npy motion you can't look at. Preparation is the last point where this is cheap to notice.

The cache is content-addressed, so don't fight it. Re-exporting your mesh from Blender changes the file, changes the hash, and re-runs the whole thing - that's correct behaviour, not a bug. force_rebuild is for when you want a clean pass anyway, and it's worth knowing it deletes the folder rather than merging into it.

Category3D/UniMate

Inputs (6)

NameTypeDefaultDescription
rigged_meshSTRING—
modelUNIMATE_MODEL—
nameoptSTRINGAsset name; defaults to the file name.
face_rightoptSTRINGRaw bone name of the RIGHT facing joint (leave empty to auto-pick).
face_leftoptSTRINGRaw bone name of the LEFT facing joint.
force_rebuildoptBOOLEANfalse—

Outputs (2)

NameTypeDescription
assetUNIMATE_ASSET—
reportSTRING—