⚓ Hard Bottom Cap (Strict)
It closes the flat cut and leaves your hair alone
- mesh
- info
- mesh
Generated busts and statues have two kinds of openings: the honest one (a clean horizontal cut where the model was chopped at the base) and the dishonest ones (hair strands with gaps between them, a neck hole, alpha-card outlines). A generic hole filler closes all of them and turns your character's hair into a plastic helmet.
Hard Bottom Cap does one thing. It finds horizontal boundary edges near the bottom of the mesh and plugs them with a flat floor. Nothing else.
The mechanism
It merges vertices, then finds boundary edges the reliable way - edges that appear exactly once when sorted and de-duplicated. Then the "strict" filter, which is the entire point of the node:
- Both endpoints of the edge must be below
z_min + z_ratio × Z-extentof the mesh. Defaultz_ratio0.05 means 5% up from the lowest point. - The edge must be nearly horizontal - difference in Z between its endpoints under 0.01 world units, hard-coded.
Edges passing both tests are the rim of the cut. It then adds one new vertex at the mean of those rim points, dropped by cap_offset below the mesh's lowest point (default 0.001 - one millimetre-ish in a metre-scale model), fans triangles from the rim to that point, and repairs winding and normals so the floor faces outward.
Result: the flat bottom is sealed, the neck hole and every hair gap is untouched, and the info string tells you exactly what happened - Sealed N faces at Z=..., or No horizontal boundary found below ..., or Mesh already watertight.
The two inputs
cap_offset(-0.05 to 0.05, default 0.001) - how far below the lowest vertex the plug sits. The default gives you a hair of separation rather than a coplanar floor, which is usually the safer geometry. A negative value pushes the plug up into the model instead, which occasionally reads better in a slicer.z_ratio(0.01–0.2, default 0.05) - how far up from the bottom the search band goes. Raise it for a model whose cut isn't quite flat or which lost the bottom few percent in generation; lower it if the model's actual feet are near the bottom and you don't want to touch them.
Outputs: info (STRING, slot one) then mesh (slot two). Yes, the string is first. It's the same convention as CapGPU, Clean Mesh, and Hole Surgeon in this pack - check the socket index before you drag.
The scale trap
Both thresholds - z_ratio's extent multiplier is fine, but the 0.01 horizontality tolerance is absolute world units - are scale-sensitive. On a mesh normalised to ~1 unit tall, a 0.01 slope is a genuine horizontal edge and everything works. On a mesh exported in centimetres or in a 100-unit Blender scene, your "flat" rim has slopes larger than 0.01 and the strict filter finds nothing. You'll get No horizontal boundary found on a model that visibly has a flat bottom.
Fix: scale the mesh to roughly 1–2 units of height before capping (normalise on load), or accept that this node wants unit-scale input. This isn't a bug so much as a design assumption the author never surfaced.
When to pick it over CapGPU
CapGPU takes the largest boundary loop, so on a mesh whose neck hole is bigger than the base it caps the neck. Hard Bottom Cap is location-based, not size-based, so it's the right tool for a bust with a broad open neck. Meanwhile CapGPU in advanced_cap mode is what you reach for if you want the base to look like something - a plinth, a socket - rather than just be closed.
For a print: Hard Bottom Cap → MeshFixer Fast (which handles the small punctures it deliberately ignored) → Composite Mesh Saver to .stl.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
python -m pip install trimesh
Restart ComfyUI. ComfyUI Manager will clone the pack for you (search Antonioilev Light Pack), but there is no requirements.txt in the repo, so trimesh - the only dependency this node has - has to be installed by hand into ComfyUI's environment. If the node is missing from the menu after a restart, look for the pack's startup banner: [Antonioilev_Light_pack] Loaded N nodes (M failed) followed by the import error is the whole diagnosis.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| mesh | TRIMESH | — | |
| cap_offset | FLOAT | 0.001-0.05–0.05 | — |
| z_ratio | FLOAT | 0.050.01–0.2 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| info | STRING | — |
| mesh | TRIMESH | — |