Nodes/Melite Poser Nodes/🦴 Poser Deform SOMA (joints → mesh)
ComfyUI Node

🦴 Poser Deform SOMA (joints → mesh)

Turning 77 joints into an actual posed mesh

By JayDataEngineer·Created 2 months ago·Updated 5 days ago· 1
🦴 Poser Deform SOMA (joints → mesh)
  • joints
  • mesh
â—„identity_modelannyâ–º

Joints are numbers. A mesh is what a renderer, a depth ControlNet, or an exported GLB actually needs. Poser Deform SOMA is the bridge: it takes a SOMA-77 joint set and applies linear blend skinning to the bind mesh, returning posed vertices. In the pack's own pipeline this is the node that turns "I moved the shoulders" into "here is a body".

It wraps the same SkinEngine.deform() call the pack's interactive HTTP route uses, so an interactive slider drag in their Body Studio and a queued graph run produce the same geometry. The difference is observability: a typed node with a validateable input is something you can see, log, and replay.

How it works

Linear blend skinning, the standard rig maths - each vertex is transformed by a weighted combination of its bone transforms, weights coming from the identity template. The engine does that in metres; the node handles the conversion in both directions.

The validation here is unusually thorough, and deliberately so, because the failure modes are all silent. It checks that

  • joints really is a (positions, rotations) tuple from PoserBindPose,
  • positions are (T, 77, 3) - a bare (77, 3) gets rejected, because numpy would happily broadcast it wrong,
  • rotations are (T, 77, 3, 3) - the source comment notes that the transposed (T, 3, 3, 77) variant "silently reduces along the wrong axis", which is exactly the bug that burned them,
  • and both have the same frame count T.

Then it converts cm → m, deforms, squeezes the batch dimension if the engine returned (1, V, 3), converts m → cm back, and pulls face indices from the same identity's engine - with a comment explaining why cross-identity faces would be catastrophic (indices pointing at vertices that don't exist, or wrapping silently through numpy indexing).

Inputs and outputs

  • joints - a JOINTS value, normally from PoserBindPose. Positions in centimetres, rotations world-space. The frame count T can be more than 1, but with T > 1 the engine returns a (T, V, 3) array and this node expects to squeeze (1, V, 3) - one pose per execution is the well-trodden path.
  • identity_model (default anny) - must be the same model the joints came from. This is a free-text string, not a combo, so a mismatch is your responsibility.

One output: mesh, typed MESH - (vertices, faces) with vertices float32 in centimetres. Wire it into PoserRender for a depth/normal/rgb pass, into a GLB export, or into whatever else wants geometry.

Install

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git

Restart, or install melite-poser-nodes via ComfyUI Manager. The deform path needs py-soma-x[anny] plus a local copy of the identity assets - the pack's requirements.txt carries a note about a 0.3.0 wheel bug where soma.body.assets isn't shipped, which skin.py works around by passing a data_root. _pick_device() prefers CUDA because the anny pass is roughly 10× faster there, and falls back to CPU without complaining.

Where people get burned

Units: the pack's most-repeated warning lives in this node's source. The single positions_cm / 100.0 line is flagged as "the most silent-failure-prone line in the chain". Feed it metres and your deformed body is tiny; every shape check passes and every downstream render just shows a speck. Centimetres in, centimetres out, always.

soma cannot deform. The NPZ-backed SOMA engine has no deform support. Use PoserBindMesh for a rest-pose soma, or use anny. This node's docstring is explicit that PoserDeformSoma "would fail" on soma.

Geometry that isn't PoserBindPose output. The tuple structure isn't decorative - anything else gets a TypeError listing what it got. If you're synthesising joints yourself (a motion retarget, a mocap NPZ), match (1, 77, 3) and (1, 77, 3, 3) exactly, cm and world-space rotations.

Identity mismatch between joints and deform. The node can't detect that your joints were shaped with identity_coeffs='0.7,0.4,0.55' while the engine is holding neutral. You get a subtly wrong body, which is much worse than an error. Keep a single source of truth for the identity in your graph.

CategoryTechNoir/Poser

Inputs (2)

NameTypeDefaultDescription
jointsJOINTS—
identity_modelSTRINGanny—

Outputs (1)

NameTypeDescription
meshMESH—