🔧 Clean Mesh §4.42 (weld→repair→QEM decimate)
The decimation pass your TRELLIS asset was begging for
- glb_path
Image-to-3D models hand you triangle soup. That is not a bug in TRELLIS or Hunyuan3D - it's the actual state of the art, and the KB's 3D generation essay is blunt about it: the silhouette is great, the geometry underneath is not. What lands on disk is typically tens of thousands of faces of marching-cubes mush with duplicated vertices everywhere. CleanMesh is the node that turns that into something you can put in a scene without your GPU crying.
It's labelled "§4.42" because it's a versioned stage inside a bigger internal pipeline, and that tells you what it isn't: a general-purpose mesh lab. It's the one boring pass - weld, repair, decimate - done in one click, with a receipt.
What it actually does
Three things, in order, all pure Python via trimesh. No Blender subprocess, no CUDA, no model downloads.
- Weld.
merge_vertices()with angle tolerance so duplicated seams collapse. This is the same jobMeshDedupdoes in isolation. - Repair. Removes degenerate faces, removes duplicate faces, then
fix_normals(). - Decimate. If the mesh still has more faces than your budget,
simplify_quadric_decimation(target_faces)runs quadric error metric decimation down to the target.
QEM is the good kind of decimation - it preserves silhouette shape far better than clustering vertices, which is why the node is worth using over "just export at lower resolution". The catch is the same as every QEM implementation: it thinks in geometry, not in texture space.
Inputs and outputs
You set two knobs, realistically.
target_faces(default 5000, range 100–200000) - the face budget after decimation. 5k is a sensible hero prop; 1500–2500 for background filler; the author explicitly caps at 200k because past that this isn't your cleanup node.weld_threshold(default 0.0001, i.e. 0.1mm) - vertex merge distance. Raise it to 0.001–0.005 if you're still seeing Z-fighting or soup after a clean pass. Too high and you flatten lips and eyelids.
glb_path goes in - a filename inside ComfyUI's input/ folder, or an absolute path - and glb_path comes out, written to output/. It's an output node, so it runs even if nothing is wired downstream, and it logs the before/after face counts so you can sanity-check that decimation actually happened.
Install
Grab it through ComfyUI Manager (search the pack title, melite-poser-nodes) or do it by hand:
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git
Restart ComfyUI. The pack's requirements.txt only declares its additive deps - py-soma-x[anny], trimesh, xatlas - and the comment in there is worth reading, because it says trimesh was audited against ComfyUI's own requirements and isn't provided by the runtime. So trimesh comes from this pack, not from core. CleanMesh doesn't touch py-soma-x at all, so if the anny wheel is the thing failing on your box, you can still use this node.
Where people get burned
Reduce the wrong mesh and the texture goes with it. Decimation maps face attributes through, which means UVs survive the trip, but nothing in the simplifier knows that a UV island boundary is a seam worth protecting. Cut a 60k-face asset down to 2k and expect visible texture slippage around the atlas edges - the fix is to decimate less, or to decimate before you project the texture.
Multi-material meshes come out flattened. The loader concatenates every geometry in the scene into one mesh before working on it. That's the point (you wanted one clean mesh), but if you were relying on separate materials surviving per-part, they don't.
Everything in this pack is path-in, path-out strings. There's no MESH socket here; you're chaining filenames. Upload your source GLB into ComfyUI's input/, and read the result from output/ - the node's UI payload points at the file it wrote.
Expect a warning, not an error, when it can't hit the budget. Decimation can bottom out before your target on meshes with lots of disconnected shells. The log prints OVER BUDGET when it overshoots by more than 10% - check the console instead of assuming your number was honoured.
One honest note on the pack: it was published from a private inference estate in September 2026 and has essentially no public footprint - no forum threads, no issues to search. That's fine for a deterministic trimesh pass like this one, but it means you're reading the source, not community consensus, when something misbehaves.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| glb_path | STRING | — | |
| target_faces | INT | 5000100–200000 | Target face count after QEM decimation. |
| weld_threshold | FLOAT | 0.00010.000001–0.1 | Vertex merge distance threshold. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| glb_path | STRING | — |