Fit Prop (seat a static prop onto a SOMAX body)
Seat a hat, sword or coat onto a SOMAX body and get the measurements back
- glb_path
- fit_record
Props are the easy half of asset generation and the annoying half of assembly. A generated sword looks great sitting on your desk in a viewer. Then you have to put it in a hand, and "in a hand" turns out to mean a scale, an offset, a rotation, and a reference bone - three knobs and a bunch of guessing, repeated for every prop. Fit Prop is the node that does the seating as one operation, and - this is the good part - reports the numbers it used.
That second output is the difference between a tool and a gamble. fit_record is a JSON measurement record of the seat maths, so you can log it, diff it, or hand it to a verification pass instead of squinting at a viewport.
How it works
It takes three things - the body GLB, the prop GLB, and the attachment row - parses the row through the same gated contract the pack's estate tooling uses, and runs the seat solver. The row's mode is inferred, not selected: a grip row uses the hand-ref law, a row with FitRatio > 0 or a FitRefBone measures the body to size the prop, and everything else is a plain socket attach with an offset and a rotation.
The row gate is not decorative. It refuses conflicting rows loudly rather than picking a winner: socket rows can't carry fit knobs, fit rows need both a reference bone and a non-zero ratio and can't also set a target width, and grip rows reject an explicit position or rotation because the grip law derives orientation itself. Numbers that are negative, non-finite, or not numbers get thrown back at you rather than silently clamped.
The source comments describe this file as the pack-side twin of an estate tool with "same function names, same math, same refusal strings", with a parity test proving they agree bit-for-bit on real assets. That's the kind of thing you normally only see in 3D tooling at studios, so it's worth a nod.
Inputs and outputs
Three required inputs, all path-style strings that normally come from other nodes:
body_glb_path- the SOMAX body: rigged, with mixamorig bone names, sitting in ComfyUI'sinput/folder (or an absolute path).prop_glb_path- the static prop. Unskinned, unanimated. A generated TRELLIS asset fits here.row_json- the attachment row, verbatim. Wire it fromFitRowrather than typing it.
Two outputs:
glb_path- the fitted GLB, written tooutput/.fit_record- a JSON string of the measurement record, including ameasuredkey for fit-mode rows. Log it with a text-formatting node; it's your audit trail.
It's an output node, so it runs whether or not anything downstream consumes it.
Install
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git
Restart ComfyUI, or install melite-poser-nodes via ComfyUI Manager. Pack deps are py-soma-x[anny], trimesh and xatlas; the fit solver itself is stdlib + numpy, so like the rest of the fit pair it's unusually lightweight for what it does.
Where people get burned
A prop whose own scale is wrong will seat "correctly" and still look wrong. Socket mode trusts the prop's model-space size. If your generated sword is 40cm long in a scene that expects metres, the row is fine and the result is a letter-opener. Use target_width (socket) or fit_ratio + fit_ref_bone (fit) when anatomical scale actually matters - that's what those modes exist for.
Bone names are the contract. The row names a bone on the body rig. If the body GLB isn't a SOMAX/Mixamo-style rig, the named bone probably doesn't exist, and you find out at queue time.
Don't hand-write the row. row_json accepts any valid JSON with the right field names, which means you can paste a literal in and have it work right up until a knob changes. Build it with FitRow and there's one source of truth.
Empty or malformed JSON fails at the door. Non-JSON gets a row_json is not JSON error; a structurally valid JSON object missing Model or Bone gets refused by the gate. Both are good errors - trust them.
File resolution is input-first. Both paths resolve against ComfyUI's input/ (absolute paths also work). If you're chaining from a node that wrote to output/, you may need to copy the file across first; the CompositeCharacter and SkinApply nodes in this pack are less fussy, this one is documented as input-side.
One practical workflow note: props that must follow an animated hand are a different problem from props seated at bind pose. This is a seating operation. Think of it as the pose-your-reference step, not the retarget step.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| body_glb_path | STRING | — | |
| prop_glb_path | STRING | — | |
| row_json | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| glb_path | STRING | — |
| fit_record | STRING | — |