π§ Mesh UV Watertight
The Watertight Check That Talks to Your Console, Not Your Screen
- mesh
- mesh
What it's actually for
Every 3D pipeline needs one node that answers a single question: is this thing actually closed, and does it have usable UVs? Before you remesh, before you print, before you weld - you want the numbers, not a guess based on how the viewport looks.
This is that node. Be clear about what it is, though: in the shipped version it's a diagnostic pass-through. The mesh goes in, the same mesh comes out, and the useful output is the print in your console. That's not a criticism so much as a calibration - the node's whole value is the console readout.
How it works
The watertight check is stricter than trimesh's default. It requires both mesh.is_watertight and a Euler number of 2 - the topology signature of a single closed surface with no handles. So a mesh that's closed but has an extra tunnel through it (a hole-drilled solid, or a generated shape that grew a loop) reads as not watertight. If that check throws, it falls back to plain is_watertight, and if that throws too, it reports false.
The UV side extracts mesh.visual.uv, sanitises NaNs, and reports the min/max bounds, whether the layout sits inside the unit square, and an island count. That last number is measured with a rough heuristic rather than real island analysis - it walks the UV array, finds jumps in UV distance larger than mean+Ο, and counts them. It's a smell test, not a census. A layout that's been sorted or re-indexed will fool it.
Point clouds and empty meshes are short-circuited: if there are no faces, the node returns immediately without pretending to have an opinion.
The show_checker, show_wireframe, show_seams and show_chart_ids options exist in the node's schema and are computed into the UI payload the code builds - but the shipped preview() returns the mesh alone, so in practice those toggles don't reach a viewer. If you were expecting a checker-textured viewport, use preview_mesh_uv or Ultimate3DViewSaveLoad instead.
The inputs that matter
- mesh - the TRIMESH to inspect. That's the only required one.
- show_seams / show_wireframe / show_chart_ids - present in the schema; currently inert in the shipped code. Set them if you like; watch the console for the report either way.
One output: mesh, unchanged. Slot it inline in a chain - inspect, print, pass through - by wiring the output to the next node. Because it's an output node it always runs, so you get a fresh readout on every queue, which is genuinely handy at the end of a repair chain.
The console output is the deliverable:
[Mesh] V=48213 F=96422 watertight=False
[mesh_uv_watertight] Preparing preview
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
# restart ComfyUI
Manager search: "Antonioilev Light Pack". No requirements.txt ships with the repo and __init__.py swallows import errors behind a loaded/failed count, so a missing node means a missing Python package. This one needs trimesh, plus folder_paths (ComfyUI itself) for the output directory - no extra pip work in a normal install.
Where people get burned
- Unwelded meshes always fail the watertight test. If your generated asset reports
watertight=Falsewith a hole count in the hundreds, that's chart splits, not holes. Weld first (AntonioilevUVWatertight), then re-run this and watch the verdict flip. - The Euler-number check is strict. A mesh you know is solid can report false if it has an internal loop. If that surprises you, that's the node doing its job.
- The island count is approximate. Treat it as "how chaotic is this UV array" rather than a real count; for a genuine number, run xatlas and count what comes back.
- Nothing appears in the UI. Expected. This node's interface is stdout - keep the ComfyUI console visible, or you'll see a node that seems to do nothing at all.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| mesh | TRIMESH | β | |
| show_checkeropt | BOOLEAN | false | β |
| show_wireframeopt | BOOLEAN | true | β |
| show_seamsopt | BOOLEAN | true | β |
| show_chart_idsopt | BOOLEAN | false | β |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| mesh | TRIMESH | β |