MeshTools UV Unwrap
The fix for machine-baked UVs that a texture artist would reject
- trimesh
- trimesh
If you've generated a 3D asset with Hunyuan3D, TRELLIS, or TripoSR, you've probably opened it and noticed the texture looks like it was shrink-wrapped by a machine. That's because it was. The KB's 3D generation essay names "messed-up UV unwrapping" as one of the two consistent flaws of generated assets, right next to bad topology. MeshTools UV Unwrap is the ComfyUI-native answer: it takes your TRIMESH and generates proper UV coordinates with xatlas, the fast open-source unwrapper - the same library that powers texture tooling in a lot of real pipelines.
Why reach for it? Because most generated meshes don't ship with a UV map you can actually use, and without UVs you can't paint textures, bake maps, or export a properly textured GLB for a game engine. The node's tooltip says it plainly: UV unwrap is required before texture painting. Run this after generation and before export, and the mesh arrives texture-ready instead of vertex-colored soup.
How it works
The mechanism is worth knowing because it explains a weird side effect. xatlas works in two stages:
- It cuts the mesh into charts - connected patches that can be flattened without too much distortion.
- It lays those charts out in 2D UV space, packing them into a texture atlas.
The catch: to make the charts lie flat, xatlas has to split vertices at the seams. So your vertex count goes up. A clean quad mesh of 10,000 verts might come back as 14,000. That's not a bug or a sign of damage - it's how all real UV unwrappers work, and it's a big reason you don't do this on a mesh you still need to edit by hand.
The node's other quirk: if you hand it a trimesh.Scene (multi-part) instead of a single mesh, it concatenates the parts first. And there's a safety valve - meshes over 500 million faces raise an error rather than melting your RAM.
Inputs and outputs
The whole node is one wire in, one wire out. You don't get a "seam count" slider or a "rotation" knob:
trimesh(TRIMESH) - the mesh to unwrap. If it already has UVs, they get replaced, not merged.trimesh(TRIMESH) - the same mesh, now carrying UV coordinates in its vertex data.
Because there are no tunable parameters, your control is entirely about where you place the node. Run it once, as a deliberate stage of the pipeline - not on every iteration, since it re-splits the geometry each time.
Installing
It ships in the ComfyUI-MeshTools pack:
cd ComfyUI/custom_nodes
git clone https://github.com/agenticvibes/ComfyUI-MeshTools
cd ComfyUI-MeshTools
pip install -r requirements.txt
Or search "ComfyUI-MeshTools" in ComfyUI Manager and restart. The xatlas dependency comes from requirements.txt - no model downloads, CPU only.
Common issues
- Vertex count jumped after unwrapping - expected. Seam splitting, not corruption.
- "Cannot build wheel for xatlas" on install - this was a classic pain point back in the stable-dreamfusion days. It's mostly gone now because
xatlasships prebuilt wheels for common platforms on PyPI, but if you're on an unusual Python or OS combo it can still try to compile - that needs a C++ toolchain and cmake. Try upgrading pip first (pip install -U pip xatlas); it usually finds a wheel. - You don't see any UVs in the viewer - ComfyUI's built-in 3D preview doesn't always show UV layouts. Export the mesh with MeshTools Export and inspect the OBJ in Blender to confirm.
One honest caveat: automatic unwrapping gets you usable UVs, not art-directed ones. For a prop or a print it's fine. For a hero asset where UV density and seam placement matter, you'll still end up in Blender - but you'll start from a sane unwrap instead of garbage.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| trimesh | TRIMESH | 3D mesh to UV unwrap. Required before texture painting |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| trimesh | TRIMESH | — |