UV libigl LSCM (backend)
The angle-preserving UV unwrap hiding behind GeometryPack's dropdown
- trimesh
- unwrapped_mesh
- info
If you've used ComfyUI-GeometryPack (search "GeometryPack" in ComfyUI Manager) to unwrap a mesh, you've already used this node - you just didn't drag it onto the canvas. GeomPackUV_LibiglLSCM is the hidden backend that runs when you pick libigl_lscm from the backend dropdown on the pack's UV Unwrap node. ComfyUI's node expansion dispatches to it automatically, which is why it's flagged dev-only and you'll rarely, if ever, add it by hand. Still, it's worth understanding, because it explains why your unwrap came out the way it did.
What LSCM actually does
LSCM stands for Least Squares Conformal Maps, and it's a classic piece of libigl. "Conformal" means angle-preserving: the algorithm tries to flatten the mesh so that every face keeps its shape - angles stay true - while trading away accuracy in area. For organic shapes that's usually exactly what you want. A circular pattern stamped onto a flat quad isn't going to get stretched into an oval the way a pure area-preserving unwrap might.
The flip side is that LSCM is a global parameterization. It doesn't cut seams the way an automatic unwrapper would, so it flattens the whole mesh as one continuous sheet. Feed it a character with arms and legs - a surface that genuinely can't lie flat - and you'll get heavy distortion somewhere, because it has to spread the "can't flatten this" pain across the whole thing. That's the real difference between this backend and the xatlas one in the same pack: xatlas cuts the mesh into islands and packs them; LSCM keeps the topology intact and just does its best.
How it works
Under the hood it's straightforward, and the code spells it out: the node pins two vertices to give the math a unique solution, runs igl.lscm on your vertices and faces, then normalizes the resulting UVs into the 0–1 range. The unwrapped result is a copy of your mesh with those UVs written into its texture visuals, plus a metadata tag recording the algorithm. One thing worth knowing: unlike xatlas, LSCM does not split vertices at seams - it preserves your original topology, which keeps the vertex count identical to the input.
Inputs and outputs
The schema is minimal because it's a backend, not a frontend:
- trimesh - the only input. It's the custom
TRIMESHtype that flows through this whole pack, so wire it from a Load Mesh node. - unwrapped_mesh - the copy with UVs attached. Feed it into one of the pack's preview nodes or Save Mesh.
- info - a string summary of vertex/face counts and the algorithm used, handy for confirming nothing went sideways.
Installing it
You install the pack, not the node - the node ships with it. The README offers three routes:
cd ComfyUI/custom_nodes
git clone https://github.com/PozzettiAndrea/ComfyUI-GeometryPack.git
cd ComfyUI-GeometryPack
pip install -r requirements.txt --upgrade
python install.py
or search GeometryPack in ComfyUI Manager and click install (if that fails, try the nightly build - the README calls Manager "recommended" and manual install "most reliable", which tells you which one it trusts more). Then restart ComfyUI.
Where people get burned
This pack installs via an experimental comfy-env isolated environment that downloads pixi and heavy native libraries - Blender, CGAL, libigl, VTK. First launch after install can chug for a while as that environment builds, so don't panic and kill it. And since the whole pack is declared work-in-progress ("fast development, occasional breakage"), expect the occasional rough edge; the author explicitly asks for issue reports. One code-level gotcha: if the isolated environment fails to provision, the node throws "libigl not installed (should be in requirements.txt)" - in a manual install you can satisfy it with pip install igl yourself. But for everyday use, just pick this backend from the UV Unwrap dropdown and let the dispatcher do its thing. It's angle-true UVs without ever leaving ComfyUI, and that's a genuinely nice thing to have.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| trimesh | TRIMESH | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| unwrapped_mesh | TRIMESH | — |
| info | STRING | — |