π UV Fixer & Packer
The Handy Little Node That Un-Flips Your UV Islands and Packs Them
- mesh
- info
- mesh
What it's actually for
Two of the most annoying things about machine-generated UVs, fixed in one pass. First: mirrored islands. A UV island with negative signed area is inside-out, which means its texture reads mirrored and its normal map lies to you - a soft, hard-to-name wrongness that people spend an hour blaming on the model. Second: islands wandering outside the 0-1 tile, because a texel that lives at u=1.4 has no texture at all.
It's a small utility, honestly - the kind of node you drop in after an unwrap and forget about until it saves you. But it's the difference between a texture that lands correctly and one that's quietly mirrored on the left half.
How it works
"Pack" here does not mean an atlas packer, and that's the main thing to understand. There's no bin-packing, no island sorting, no wasted-space optimisation. It's a normalise-and-fit: everything gets shifted so the UVs start at the origin, scaled uniformly so the whole layout fits inside the unit square, and shifted in by margin so nothing touches the border.
The flip fix comes first, and it's per-island. The node groups faces into islands with trimesh's connected-components over face adjacency, then sums the signed UV area of each island. Any island whose total area comes out negative is mirrored, so its U coordinates get negated. It counts those and reports the number back to you. That report is genuinely useful: "0 flipped islands" tells you the mesh was already clean, and a large number tells you the unwrapper you used has a habit.
Note what it does not do - the fix is a reflection, not a re-parameterisation. If an island is mirrored, flipping it corrects the orientation but leaves the UVs where they were, which is fine for a correct-looking texture and not fine if you needed the island to also change position.
The inputs that matter
- mesh - the TRIMESH to fix. If it has no UV data, the node returns the mesh untouched and a string saying
No UV foundrather than erroring. - margin (default 0.01, 0-0.1) - the border inset in UV units, i.e. the fraction of the tile left empty around the edge. 0.01 is a sensible default. Push it to 0.02-0.05 if you're going to bakes or mip-maps and want to avoid edge bleed.
Outputs are info (a short string - Fixed N flipped islands. UV Packed.) and mesh, the corrected TRIMESH. The output order is unusual - info first, mesh second - so check which socket you're grabbing when you wire it up.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
# restart ComfyUI
Manager search: "Antonioilev Light Pack". The repo ships no requirements.txt; __init__.py wraps imports in try/except and prints a loaded/failed count, so the only symptom of a missing dependency is the node not appearing. This one needs nothing beyond numpy and trimesh.
Where people get burned
- It rescales your whole UV layout to fit the tile. If your texture was baked to the previous UV positions, a scale-and-shift of the layout means the texture no longer lines up. Fix UVs, then bake - not the other way around.
- Flipping an island in place can create overlap with a neighbouring island it previously sat clear of. It's rare with normal layouts, but check the pack's UV preview if the texture looks doubled somewhere.
- It's not a packer despite the name. If you need actual atlas packing of separate islands, that's
AntonioilevUVUnwrap(xatlas) orAntonioilevUVShellScalefor sizing islands in place. - Non-uniform layouts stay non-uniform. Every island is transformed by the same scale, so relative texel density between islands is preserved - including if it was bad to begin with.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| mesh | TRIMESH | β | |
| margin | FLOAT | 0.0100β0.1 | β |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| info | STRING | β |
| mesh | TRIMESH | β |