rf-geom / ridgeforge Public

main▾ 1 branch · 12 tags Go to file Code
n.korsak  decimate: interpolate normals and one UV set at the collapsed vertex a1f3c0d · 3 weeks ago · - 412 commits
includesplit MeshView out of Mesh; headers are install-time stable4 mo ago
srcpack collapse-heap entry 36→28 bytes, fold version counters2 mo ago
examplesadd ex-budget: hit a triangle count without over-shooting2 weeks ago
testsregression mesh for RF-72 (remesher ate 2-triangle shells)3 weeks ago
cmakeRF_SANITIZE toggle; find_package config export7 mo ago
toolsrfmesh check --self-intersect, --max-pairs gate3 weeks ago
.clang-formatLLVM base, 100 columns, east const1 yr ago
CMakeLists.txtrequire 3.20; rf_amalgamate target writes ridgeforge_all.hpp4 mo ago
CHANGELOG.md0.9.43 weeks ago
LICENSEMIT2 yr ago
README.mddrop the "roadmap" section nobody kept current3 weeks ago
README.mdMIT · C++17 · no deps

RidgeForge

release0.9.4 licenseMIT c++17 cipassing tests1,284

Triangle meshes in, clean triangle meshes out. RidgeForge is a small C++17 library for the unglamorous middle of a geometry pipeline: welding a scan that arrived as loose triangle soup, cutting it down to a triangle budget without rounding off the edges that matter, and reporting where a surface runs through itself.

No third-party dependencies. OpenMP is used if you have it and ignored if you don't. It builds a static library and, optionally, a one-header amalgamation and the rfmesh command-line tool.

What it does

Four things, and deliberately nothing else:

  • Repair - weld coincident vertices, drop degenerate and duplicate triangles, orient faces, fill holes up to a loop length, delete components below a size threshold.
  • Decimation - quadric-error edge collapse, with explicit protection for boundaries, for marked vertices, and for sharp features found by dihedral angle.
  • Ridge extraction - chain sharp edges into ordered polylines, split into ridges and valleys by the sign of the dihedral angle, with a minimum-length filter so a noisy scan does not emit ten thousand two-edge fragments.
  • Self-intersection - every intersecting triangle pair through a BVH, exact predicates, no tolerance knob; an experimental local resolve is behind a separate call.

There is no scene graph, no material system, no renderer and no solver. If you need those, this is the wrong library.

Quick start

Load a mesh, decimate to forty thousand triangles while keeping anything sharper than 35°, write it back - fifteen lines, no boilerplate:

// lod.cpp - c++ -std=c++17 lod.cpp -lridgeforge
#include <ridgeforge/mesh.hpp>
#include <ridgeforge/decimate.hpp>
#include <ridgeforge/io.hpp>

int main() {
  auto loaded = rf::io::read("blade.ply");
  if (!loaded) return 1;          // Result<Mesh> is falsy on error
  rf::Mesh mesh = std::move(*loaded);

  rf::DecimateOptions opt;
  opt.target_triangles = 40'000;
  opt.preserve_boundary = true;
  opt.ridge_angle_deg   = 35.0f;

  rf::Decimator{opt}.run(mesh);
  return rf::io::write(mesh, "blade_lod1.ply") ? 0 : 2;
}

That is the API shape throughout: a plain options struct, an object built from it, one run() that mutates in place. See six more worked programs.

Add it to a build

# CMakeLists.txt - needs CMake 3.20+
add_subdirectory(third_party/ridgeforge)
target_link_libraries(myapp PRIVATE RidgeForge::ridgeforge)

An installed copy works through find_package(RidgeForge 0.9 REQUIRED). Not using CMake? The rf_amalgamate target emits a single ridgeforge_all.hpp you compile directly - details on the build page.

Component status

These words are load-bearing. Stable: the signature holds until 2.0 and is covered by tests. Experimental: it worked on the meshes we tried and may change in any minor release. Partial: it does part of what the name says and the gap is written down.

ComponentStatusSinceNote
Mesh, MeshViewstable0.4.0indexed triangles, 32-bit indices
I/O: OBJ, PLY, STLstable0.5.0ascii and binary PLY
Repairstable0.6.0weld, orient, fill, prune
Decimatorstable0.7.0quadric error, feature-aware
RidgeExtractorstable0.8.0ridge/valley polylines
SelfIntersection::findstable0.9.0exact, no tolerance
SelfIntersection::resolveexperimental0.9.2fails on nested shells - RF-41
Remesherexperimental0.9.0isotropic only, no sizing field
Attribute carry-throughpartial0.9.4normals and one UV set
glTF import/exportnot planned-out of scope, see FAQ

Scope

In: closed and open triangle meshes up to ~2³¹ vertices, deterministic output for a given input and option set, single process, optional OpenMP. Out: quad and polygon meshes, subdivision surfaces, NURBS, textures and materials, boolean/CSG, GPU and distributed execution. The boundary is firm; requests to move it land on the FAQ.

Documentation

License

MIT. Do what you like; keep the copyright line. Contributions go through the normal pull request flow and are taken under the same license.