A cross-platform CLI tool for filling holes in triangle meshes using CGAL's implementation of the Liepa 2003 algorithm with Laplacian (harmonic) fairing.
- Robust Hole Filling: Uses constrained Delaunay triangulation (2D/3D) with automatic fallback
- Laplacian Fairing: Smooth blending with configurable continuity (C⁰, C¹, C²)
- Multi-Format Support: OBJ, PLY, OFF formats
- Scalable: Optimized for meshes with millions of polygons
- Cross-Platform: Windows, Linux, macOS
Based on:
- Liepa 2003: "Filling Holes in Meshes" - Eurographics Symposium on Geometry Processing
- Botsch et al. 2008: "On Linear Variational Surface Deformation Methods" - IEEE TVCG
- Triangulation: 2D CDT on best-fit plane, with 3D Delaunay fallback
- Refinement: Match local mesh density
- Fairing: Bi-Laplacian smoothing (Δ²f = 0) with boundary constraints
- C++17 compiler (GCC 7+, Clang 5+, MSVC 2017+)
- CMake 3.12+
- CGAL (located at
/mnt/e/GH/cgal/or setCGAL_DIR) - Eigen3 3.2+ (located at
/mnt/e/UBS/include/eigen3/or setEIGEN3_INCLUDE_DIR)
# Clone or extract project
cd MeshRepair
# Create build directory
mkdir build && cd build
# Configure
cmake -DCMAKE_BUILD_TYPE=Release ..
# Build
cmake --build . -j$(nproc)
# Install (optional)
cmake --install . --prefix /usr/localLinux/WSL:
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc)Windows (Visual Studio):
cmake -G "Visual Studio 17 2022" -A x64 ..
cmake --build . --config ReleasemacOS:
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(sysctl -n hw.ncpu)./mesh_hole_filler input.obj output.obj./mesh_hole_filler input.ply output.ply --verbose --stats./mesh_hole_filler mesh.obj repaired.obj \
--continuity 2 \
--max-boundary 500 \
--max-diameter 0.05 \
--validate| Option | Description | Default |
|---|---|---|
--continuity <0|1|2> |
Fairing continuity (C⁰/C¹/C²) | 1 |
--max-boundary <n> |
Max hole boundary vertices | 1000 |
--max-diameter <r> |
Max hole diameter ratio | 0.1 |
--no-2d-cdt |
Disable 2D constrained Delaunay | enabled |
--no-3d-delaunay |
Disable 3D Delaunay fallback | enabled |
--skip-cubic |
Skip cubic search | disabled |
--no-refine |
Disable mesh refinement | enabled |
-v, --verbose |
Verbose output (shows all hole details) | off |
--quiet |
Minimal output | off |
--stats |
Show detailed statistics | off |
--validate |
Validate mesh before/after | off |
--ascii-ply |
Save PLY files in ASCII format | off (binary) |
| Option | Description | Default |
|---|---|---|
--preprocess |
Enable all preprocessing steps | off |
--no-remove-duplicates |
Disable duplicate vertex removal | enabled |
--no-remove-non-manifold |
Disable non-manifold vertex removal | enabled |
--no-remove-isolated |
Disable isolated vertex cleanup | enabled |
--no-remove-small |
Disable small component removal | enabled |
--non-manifold-passes <n> |
Number of non-manifold removal passes | 2 |
--debug |
Dump intermediate meshes as binary PLY | off |
| Mesh Size | Holes | Avg Boundary | Expected Time |
|---|---|---|---|
| 100K tris | 10 | 50 verts | < 1 sec |
| 1M tris | 50 | 100 verts | 2-5 sec |
| 10M tris | 100 | 200 verts | 10-30 sec |
| 50M tris | 500 | 100 verts | 1-3 min |
Note: Performance depends on hole boundary size, not total mesh size.
./mesh_hole_filler damaged.obj repaired.obj./mesh_hole_filler input.ply output.ply --continuity 2./mesh_hole_filler large.obj fixed.obj --skip-cubic --no-refine./mesh_hole_filler mesh.obj result.obj --verbose --stats --validate./mesh_hole_filler damaged.obj repaired.obj --preprocess --verbose./mesh_hole_filler noisy.ply clean.ply --preprocess --non-manifold-passes 3./mesh_hole_filler input.obj output.obj --preprocess --debug --verboseThis creates:
debug_duplicates.ply- After duplicate vertex removaldebug_nonmanifold.ply- After non-manifold vertex removaldebug_isolated.ply- After isolated vertex removaldebug_largest_component.ply- After small component removal
=== MeshHoleFiller v1.0.0 ===
Loading mesh from: input.obj
Vertices: 12483
Faces: 24862
Edges: 37345
Detecting holes...
Detected 3 hole(s):
Hole 1: 47 boundary vertices, diameter ≈ 2.341
Hole 2: 23 boundary vertices, diameter ≈ 1.102
Hole 3: 65 boundary vertices, diameter ≈ 3.876
Filling 3 hole(s)...
Hole 1/3 (47 boundary vertices):
Filled: 89 faces, 42 vertices added
Hole 2/3 (23 boundary vertices):
Filled: 41 faces, 18 vertices added
Hole 3/3 (65 boundary vertices):
Filled: 127 faces, 62 vertices added
=== Hole Filling Summary ===
Filled successfully: 3
Failed: 0
Skipped (too large): 0
Faces added: 257
Vertices added: 122
Total time: 847.3 ms
Saving result to: output.obj
Vertices: 12605
Faces: 25119
Done! Successfully processed mesh.
- Increase boundary constraints (use smaller
--continuityvalue) - Check that hole boundary is valid and manifold
- Adjust
--max-boundaryor--max-diameter - Split large holes manually
- Verify Eigen3 3.2+ is installed
- Check that CGAL_DIR and EIGEN3_INCLUDE_DIR are set correctly
- Ensure C++17 support
This project uses CGAL, which is licensed under GPL-3.0 or commercial license. Your project inherits the GPL-3.0 license unless you obtain a commercial CGAL license.
- Peter Liepa. "Filling Holes in Meshes." Eurographics Symposium on Geometry Processing, 2003.
- Mario Botsch et al. "On Linear Variational Surface Deformation Methods." IEEE TVCG, 2008.
- CGAL Documentation: Polygon Mesh Processing
MeshRepair/
├── CMakeLists.txt # Root build configuration
├── README.md # This file
├── cmake/ # CMake helper modules
│ ├── FindCGAL.cmake
│ └── FindEigen3.cmake
├── meshrepair/ # Main source directory
│ ├── include/ # Public headers
│ │ ├── types.h
│ │ ├── config.h
│ │ ├── mesh_loader.h
│ │ ├── hole_detector.h
│ │ ├── hole_filler.h
│ │ ├── mesh_validator.h
│ │ ├── mesh_preprocessor.h
│ │ └── progress_reporter.h
│ ├── main.cpp
│ ├── mesh_loader.cpp
│ ├── hole_detector.cpp
│ ├── hole_filler.cpp
│ ├── mesh_validator.cpp
│ ├── mesh_preprocessor.cpp
│ └── progress_reporter.cpp
├── tests/ # Test suite
│ ├── CMakeLists.txt
│ └── test_data/
└── docs/ # Additional documentation
├── USAGE.md
└── ALGORITHM.md
Contributions welcome! Please ensure:
- Code follows existing style
- All tests pass
- Documentation is updated
For issues and questions, please open an issue on the project repository.