Nodes/Melite Poser Nodes/🔧 Composite Character (body+hair+outfit)
ComfyUI Node

🔧 Composite Character (body+hair+outfit)

Body + hair + outfit in one GLB, without duplicating the skeleton

By JayDataEngineer·Created 2 months ago·Updated 4 days ago· 1
🔧 Composite Character (body+hair+outfit)
    • glb_path
    ◄body_glb_path—►
    â—„hair_glb_pathâ–º
    â—„outfit_glb_pathâ–º

    The character pipeline in this pack generates the body, the hair, and the outfit as three separate meshes, because that's how image-to-3D works - you point the generator at a picture of hair, get hair. Which leaves the assembly problem: you now have three GLBs and you need one character that animates as a unit.

    Composite Character does that merge, and it uses two completely different strategies for the two parts, because hair and clothing are not the same problem.

    The two merge strategies

    Hair is a helmet. Hair from TRELLIS has no skeleton - it's a static mesh shaped like a wig. So the node parents it as a child of the body's head bone, with its local matrix set to the inverse of the head's world transform at bind pose. At rest, that multiplies back to identity, so the hair sits exactly where it was authored. During animation, it inherits the head bone's transform and follows. No texture projection, no re-binding: it's a real 3D wig riding the skull. The code looks for the head joint under the names mixamorig:Head, Head, head, Bip01 Head, head_bone - first match wins.

    The outfit is a skinned sibling. Clothing that's already been rigged (the pack's weight-transfer step) arrives with its own JOINTS_0 indices referencing a copy of the same skeleton, in the same joint order. Rather than merging two skeletons, the node just appends the outfit's mesh primitive to the body's mesh and appends its accessor data into the body's buffer. The joint indices already line up. No skeleton duplication, one rig, two meshes.

    Everything is bytes-in, bytes-out - no trimesh round-trip, no temp files between stages. It's sequential: body → plus hair → plus outfit, each step producing a fresh GLB.

    Inputs and outputs

    • body_glb_path (required) - the rigged (and ideally animated) body. Rigged matters: the hair parenting needs a head bone to attach to.
    • hair_glb_path (optional, default empty) - a static GLB.
    • outfit_glb_path (optional, default empty) - a rigged GLB whose joint order matches the body's.

    Both optional inputs can be left empty. Body-only is explicitly legal - the 2026-09-05 note in the source says the body alone counts as the composite when the hair/outfit stages aren't in play, and even then the body is parse-validated so a malformed GLB refuses loudly instead of being copied blindly.

    One output: glb_path, written to ComfyUI's output/, with a three_model UI entry. Output node, so it runs standalone.

    Install

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

    Restart ComfyUI, or install melite-poser-nodes from ComfyUI Manager. Pack deps: py-soma-x[anny], trimesh, xatlas. This node needs none of them - it's struct packing and numpy. The rigid part is upstream: the outfit has to have been skinned correctly before it gets here.

    Where people get burned

    Joint order, not joint names. The outfit merge assumes the outfit's JOINTS_0 indices refer to the same logical joints in the same order as the body's skin. If you rigged the outfit with a different tool that reordered anything, the append still runs and you get a character whose sleeve is welded to their knee. The pack's own weight-transfer step guarantees the ordering; a third-party rig doesn't.

    The head bone has to exist under a recognised name. A non-Mixamo rig with a differently-named head joint will fail the hair merge. The candidate list is right there in the source if you need to know whether yours qualifies.

    Hair is rigidly attached, on purpose. It won't billboard, it won't swing, it won't respond to physics. It's welded to the skull like a plastic wig. For long loose hair that's a visible limitation - and a real one, not something a setting fixes.

    Failures raise, there's no partial output. The source is emphatic that silent crashes were "the original sin of this pipeline", so every failure mode raises a RuntimeError with a concrete message. Good news: when it breaks, you get told what. Bad news: nothing gets written for you to inspect half-finished.

    The pack is obscure by design. MIT, published from a private inference-estate repo in September 2026, with no issues and no forum threads. The class docstrings are unusually good, so when something goes sideways, read the source rather than searching for it.

    CategoryTechNoir/Mesh

    Inputs (3)

    NameTypeDefaultDescription
    body_glb_pathSTRING—
    hair_glb_pathoptSTRING—
    outfit_glb_pathoptSTRING—

    Outputs (1)

    NameTypeDescription
    glb_pathSTRING—