π§· UV-Safe Hard Weld
UV-Safe Hard Weld
- input_mesh
- mesh
- info
A welded mesh and a textured mesh pull in opposite directions, and every image-to-3D pipeline drops you right in between them. The printer only wants watertight geometry - print people genuinely don't care about topology, tri count or rigging, they want something solid to slice. The texture department wants the opposite: every UV seam in a generated mesh is stored as duplicated vertices, so the moment you merge those duplicates the seam collapses and the texture stretches across the model. UV-Safe Hard Weld is the pack author's attempt to have both.
It's the node labelled π§· UV-Safe Hard Weld in the Antonioilev Light Pack (class AntonioilevUVWatertight), filed under Antonioilev/Research - one of 40-odd utilities for doing 3D cleanup inside ComfyUI instead of bouncing out to Blender.
What it actually does
Read the source and the sequence is short and specific. It copies your mesh, converts the visual to a texture visual (so UVs ride along as texture coordinates rather than per-face colours), then hard-welds by deduplicating raw vertex positions with trimesh.grouping.unique_rows, remapping faces through the returned inverse index. Then it cleans up after itself: duplicate faces out, degenerate triangles out (any face where two indices are equal), unreferenced vertices dropped, normals fixed, holes filled if the result still isn't watertight. Finally it clears the mesh cache and calls mesh.process() and reports status.
That's a genuine hard weld, not a tolerance-based merge-settings toggle. The merge_distance widget is the part people misread: the code turns it into decimal places via int(-log10(merge_distance)) and snaps vertex coordinates at that precision. Which means 1e-5 is "round to 5 decimals", not "merge verts within 0.00001 units" - 0.001 and 0.0009 behave identically, and a coarse 0.01 can fold geometry that merely looked coincident. Keep the default unless your mesh is scaled unusually small or large.
Inputs and outputs that matter
input_mesh(required, TRIMESH) - the only socket you must wire.merge_distance(optional float, default 1e-5, range 1e-8 to 0.01) - the snapping precision described above.fix_normals(default on) andfill_holes(default on) - leave both on unless you're welding a deliberately open sheet where filling holes would close the surface you wanted open.
Outputs: mesh (TRIMESH) goes into anything downstream that takes a TRIMESH socket - this pack's own CapGPU, RemeshGPU, HardBottomCap, its viewers, or whatever mesh saver you're using. The second output, info, is a plain string like Watertight OK | Watertight=True | V=18422 | F=36903; wire it to a text/note node, not an image preview, and keep it visible. It's your only feedback channel.
Install
Manager first: search ComfyUI_Antonioilev_Lightpack (publisher antonioilev) and install. Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
# restart ComfyUI
No model downloads, no checkpoints - this is a geometry utility, and it starts instantly. The pack's pyproject.toml declares no dependencies, so pip installs nothing for you; the node imports only numpy and trimesh. If you have any other 3D node pack you already have trimesh, otherwise:
pip install trimesh
Node search names: look for "UV-Safe Hard Weld" or "AntonioilevUVWatertight". Typing "UVWatertight" into the search box finds nothing.
Where people get burned
Errors don't raise. Everything is wrapped in a try/except that stuffs the message into the status string, and the mesh is returned either way. A node that ran green can still be handing you the pre-repair mesh with Critical Error: ... sitting in the info output. Read the string.
"UV-Safe" is intent, not a guarantee. The weld works on vertex positions only - the texture-aware step is whatever trimesh does later in process(). Nothing in the node measures UV quality, and the info string only counts verts, faces and watertightness. So weld, then look at the result in a UV or textured preview before you commit a whole batch. The author's own UV preview node in the same pack is the intended second half of this workflow - there's even a cache-clear hack in the code specifically to keep that preview happy.
Watertight by trimesh isn't the same as slicer-ready. is_watertight checks edge manifoldness and winding consistency. Self-intersections and non-manifold edges survive the weld and can still break a slicer or a boolean op. If the print fails after this node, escalate to the pack's CapGPU or HardBottomCap.
The socket type is TRIMESH, and that matters. The pack's own LoadMesh node outputs MESH, which will not connect - a classic silent "why won't this plug in". Use a TRIMESH producer on the front: GLBTextureExtractor in this pack outputs one alongside the GLB's texture, and other trimesh-based mesh packs (mesh-tool suites, TRELLIS/Hunyuan3D wrappers) often expose TRIMESH too - but check the label on the upstream output, because plenty of packs use plain MESH instead, and the two never connect.
One honest caveat about reputation: this pack has essentially no Reddit footprint - a search of the corpus for the author's name returns zero threads, and a loose one just returns unrelated noise. It comes from a solo dev with an active YouTube channel and a Boosty page, not a crowd-vetted ecosystem hub. Test it on a copy of your mesh before you wire it into something load-bearing.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| input_mesh | TRIMESH | β | |
| merge_distanceopt | FLOAT | 01e-8β0.01 | β |
| fix_normalsopt | BOOLEAN | true | β |
| fill_holesopt | BOOLEAN | true | β |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| mesh | TRIMESH | β |
| info | STRING | β |