Nodes/Atlas Camera/Atlas Planar Hole Patch ◩
ComfyUI Node

Atlas Planar Hole Patch ◩

Fill relief-mesh holes with conservative local planes

By mikejamesvfx·Created 2 months ago·Updated a day ago· 1
Atlas Planar Hole Patch ◩
  • solve
  • hole_mask
  • solve
  • remaining_holes
  • report
  • created_islands
layer
ring_cells2
max_components64
normal_tolerance_deg25
max_plane_error_m0.15
max_hole_fraction0.050
enclosed_onlytrue
min_normal_support_fraction0.30
max_patch_edge_factor20
max_patch_depth_factor2.0

When you derive a relief mesh from a photo, you get holes - the spots an occluder hid from the camera, where the mesh has no data. The naive fix is to just let the mesh sink or smear there. Atlas Planar Hole Patch is the considered fix: it fits conservative local planes into the holes, using the valid geometry that already surrounds them. It's the in Atlas/06 · Patch & Repair, and it's the first chapter of the pack's repair story.

"Conservative" is doing a lot of work there. This node does not invent surfaces from nothing - it recovers the relief mesh's image-space lattice, groups the missing quads into enclosed components, finds a cluster of agreeing normals around each island, averages them into one patch plane, and generates the patch interior on exact camera rays. Accepted patches reuse the perimeter indices, so they become part of the relief mesh with projective UVs - not separate wall/box primitives floating in the scene.

How it works

The gate structure is the real design. Every hole island has to clear a stack of tests before it's filled:

  • enclosed_only (default on) - reject components touching the image frame. The doctrine: frame and sky openings are not occlusion holes, they're clean-plate/background-layer territory.
  • normal_tolerance_deg - max boundary-normal spread; lower is safer at corners, higher accepts rougher/curved surfaces.
  • max_plane_error_m - max 95th-percentile residual for the densest plane-position band among the agreeing normals. This is the "does the plane actually fit" check.
  • min_normal_support_fraction - the share of surrounding valid normals that must agree on one plane angle; lower lets you do one-sided facade repairs.
  • max_hole_fraction - largest accepted component as a fraction of relief grid cells, which keeps big sky/background gaps out of the fill path.
  • max_patch_edge_factor / max_patch_depth_factor - reject grazing ray spikes and forward/backward Z needles that would produce spikes or floating patches.

Rejected islands pass through unchanged - the docstring's word is that ambiguous multi-surface boundaries "pass through for a clean plate or hidden-geometry layer to handle." This node is honest about what it won't touch.

Inputs and outputs

Required: solve. Optional: hole_mask (leave unwired to treat every hole in the target layers as a candidate - what you want for a whole-solve sweep; the gates still bound what gets filled), layer (blank = primary relief; a name = that layer; * = every relief mesh, chaining one pass per layer), and the gate inputs above.

Outputs: solve (patches added), remaining_holes, report, and created_islands (a mask of what it actually filled).

Installing it

Core-tier node:

cd <ComfyUI>/custom_nodes
git clone https://github.com/mikejamesvfx/atlas-camera.git

Restart, find it in Atlas/06 · Patch & Repair (ComfyUI Manager: "atlas-camera").

Common issues

Run order matters: this node goes before AtlasRetopologizeLayer, so retopology sees the relief plus accepted patches as one mesh. Run it after and the retopo ignores your patches.

If holes aren't being filled, read the report before loosening the gates - most likely they're frame-touching (flip enclosed_only only if you genuinely want frame-edge fills) or too large for max_hole_fraction. And remember this node fills closed holes; the open-tear leftovers are AtlasPathGuidedHoleRepair's job, feeding a second pass with relaxed settings.

CategoryAtlas/06 · Patch & Repair

Inputs (12)

NameTypeDefaultDescription
solveATLAS_SOLVE
hole_maskoptMASKSelected relief holes to patch — usually the hole_mask output of AtlasDeriveReliefMesh or AtlasCleanPlateLayer, optionally through a scope mask. LEAVE UNWIRED to treat EVERY hole in the target layer(s) as a candidate, which is what you want for a whole-solve sweep; the enclosed_only / max_hole_fraction / max_components gates still bound what actually gets filled.
layeroptSTRINGBlank = primary relief mesh; a ProjectionSource name = that layer's relief mesh; '*' = every relief mesh (primary + all sources), chaining the solve through one pass per layer.
ring_cellsoptINT21–12How many valid grid rings around each hole supply normal and plane-fit support.
max_componentsoptINT641–1024Maximum eligible hole islands to fit, smallest first. Frame/oversize rejections do not consume this budget.
normal_tolerance_degoptFLOAT251–89Maximum boundary-normal spread. Lower is safer at corners; higher accepts rougher/curved surfaces.
max_plane_error_moptFLOAT0.150.001–10Maximum 95th-percentile residual in metres for the densest plane-position band among agreeing normals.
max_hole_fractionoptFLOAT0.0500.0001–1Largest accepted component as a fraction of relief grid cells. Keeps large sky/background gaps out.
enclosed_onlyoptBOOLEANtrueReject components touching the image frame. Recommended: frame/sky openings are not occlusion holes.
min_normal_support_fractionoptFLOAT0.300.1–1Minimum share of surrounding valid normals that must agree on one plane angle. Lower values allow one-sided facade/occlusion repairs. Agreeing normals are averaged; max_plane_error_m then checks their positional fit.
max_patch_edge_factoroptFLOAT201–100Reject a generated island when its longest triangle edge exceeds this multiple of its source-pixel span times the boundary's median metres-per-pixel. Prevents grazing camera-ray spikes at any image/camera scale.
max_patch_depth_factoroptFLOAT2.01–20Reject a patch whose nearest/farthest camera depth extends beyond this multiple of the fitted local support depth band. Catches forward and backward Z needles independently of the edge-length gate.

Outputs (4)

NameTypeDescription
solveATLAS_SOLVE
remaining_holesMASK
reportSTRING
created_islandsMASK