ComfyUI Node

🧡 Hole Filler

A real ear-clipping triangulator, with two traps in the defaults

By AntonioilevΒ·Created 2 days agoΒ·Updated a day agoΒ· 0
🧡 Hole Filler
  • mesh
  • mesh
  • info
β—„min_hole_dim0.00β–Ί
β—„max_hole_dim50.0β–Ί
β—„max_points300β–Ί
β—„iterations5β–Ί
β—„relax_steps0β–Ί

Every generated mesh has holes. Not dramatic ones - punctures where the reconstruction lost confidence, gaps where the alpha-hair cards don't meet the skull, a rim where the base was cut. For 3D printing those holes are fatal; for rendering they show up as bright specks of background through the surface.

Hole Filler is the blunt instrument: it finds boundary loops and plugs them, and unlike a lot of "fill holes" buttons it actually triangulates rather than fanning everything to a centre point.

How it works

It loops iterations times (default 5). Each pass does two things. First, trimesh's built-in fill_holes(use_fan=True), which is fast and good at the trivial 1–3 triangle cases. Then the interesting part: it rebuilds the boundary graph, extracts every cycle, filters them, and triangulates each surviving loop with ear clipping in a best-fit plane - the loop's points get projected to 2D via SVD, an ear-clipping pass produces the triangles, and the result is re-oriented so the patch's normal matches the loop's plane normal. Then it merges, de-dupes, and optionally relaxes.

That best-fit-plane projection is why this node produces patches that don't look like a crumpled crisps packet on non-planar holes. For anything bigger than a pinhole it genuinely beats the naive fan.

If you turn on relax_steps, be aware it runs trimesh.smoothing.filter_laplacian over the entire mesh, not just the new patches. On a 30-step relax you are not "settling the patch", you're re-smoothing the whole model and you will lose detail. Leave it at 0 unless you specifically want that. (The pack's Hole Surgeon relaxes only its new centre vertices - that's the node to prefer when you want local relaxation.)

The inputs

  • mesh - in. TRIMESH.
  • max_points (default 300) - the loop-size ceiling. Loops with more vertices than this are skipped entirely. This is your hair protection: a boundary with 400 vertices is not a puncture, it's the outline of a card or a skirt, and fanning it will visibly collapse geometry.
  • max_hole_dim (default 50) and min_hole_dim (default 0) - the bounding-box size filter, in mesh units. A loop is only filled if its largest bounding-box dimension falls in that range.
  • iterations (default 5) - passes. Filling holes creates new boundary loops on multi-shell meshes, so more than one pass is often needed. 5 is generous; if 5 passes are changing things you're probably fighting a topology problem, not a hole.
  • relax_steps - see above. Zero.

Two traps, both of them scale

The defaults are calibrated for meshes around a dozen units across. max_hole_dim: 50 with min_hole_dim: 0 means every hole in that range gets filled - which on a unit-scale model is all of them, including the ones you wanted kept. This is the opposite failure of most fill-holes nodes: not "it did nothing", but "it closed the neck and the hair gaps". Tighten max_hole_dim to a small number relative to your model's extents and you'll get surgical behaviour.

It resets the visual block. Before doing anything it assigns a blank ColorVisuals() to the mesh. So if you coloured your parts with Mesh Colorizer or applied a texture earlier in the chain, that's gone. This node goes before colour and texturing, not after.

The info string is genuinely useful and it's slot one (mesh is slot two - the pack's convention for its Fix nodes):

Added ~1841 faces | 52104 β†’ 53945 | watertight=True

That watertight flag is the one to watch. False after a fill usually means there's still a boundary loop beyond max_points, i.e. real hair geometry.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
python -m pip install trimesh networkx

Restart. This node needs networkx specifically - it's one of the few in the pack that does - and the repo declares no dependencies at all, so Manager's clone won't install it. Run pip against the interpreter ComfyUI uses (portable builds: python_embeded\python.exe -m pip install networkx) and confirm the startup banner reads [Antonioilev_Light_pack] Loaded N nodes (0 failed) with no !!! HoleFiller: line under it.

Where it fits: after the destructive geometry passes (Clean Mesh, Mesh Fixer Fast) and before surface work. There's no point finessing a patch on a shell you're about to delete.

CategoryAntonioilev/Mesh/Fix

Inputs (6)

NameTypeDefaultDescription
meshTRIMESHβ€”
min_hole_dimFLOAT0.000–50β€”
max_hole_dimFLOAT50.00.01–500β€”
max_pointsINT3003–2000β€”
iterationsINT51–15β€”
relax_stepsINT00–30β€”

Outputs (2)

NameTypeDescription
meshTRIMESHβ€”
infoSTRINGβ€”