Fast Mesh Booleans in C++
Learn how to perform fast mesh boolean operations in C++. Union, intersection, and difference at interactive speed on million-polygon meshes. Exact arithmetic, with support for non-manifold topology and coplanar primitives.
trueform is the fastest C++ mesh boolean library for real-world meshes — performing exact boolean union, intersection, and difference at interactive speed. This tutorial covers the basics: loading meshes, running booleans, and working with results. It then shows how to precompute spatial and topological structures and apply transformations to avoid rebuilding them, enabling boolean operations on moving geometry at real-time rates.
We will use the Stanford Dragon to demonstrate boolean union, intersection, and difference between two offset copies of the same mesh.




Each boolean under 14ms on 2×500K polygons. Let's begin by installing trueform.
Install
Header-only. Add it to your project via CMake FetchContent:
FetchContent_Declare(
trueform
GIT_REPOSITORY https://github.com/polydera/trueform
)
FetchContent_MakeAvailable(trueform)
target_link_libraries(my_app PRIVATE tf::trueform)
Also available via conan, nuget, and pip. See the installation guide for all options.
Loading the mesh
Include the library:
#include <trueform/trueform.hpp>
All functions live in the tf:: namespace. To load a mesh from a file:
auto dragon_buffer = tf::read_stl("stanford_dragon.stl");
// semantic view into the data
auto dragon = dragon_buffer.polygons();
If you already have vertex and face data in memory, construct views directly — no copy required:
std::vector<float> flat_points;
std::vector<int> flat_triangles;
auto dragon = tf::make_polygons(
tf::make_faces<3>(flat_triangles),
tf::make_points<3>(flat_points));
The resulting dragon gives you full mesh semantics on the views:
auto [id0, id1, id2] = dragon.faces().front();
auto [pt0, pt1, pt2] = dragon.front();
auto pt = dragon.points().front();
auto pt3 = pt0 + (pt2 - pt1) / 2;
For details on working with geometric ranges, see the Geometry Walkthrough.
Translated views
To perform a boolean between two copies of the dragon at different positions, we tag a transformation onto the form. The underlying data is shared — no vertices are copied.
auto T = tf::make_transformation_from_translation(
tf::make_vector(0.f, 1.f, 0.f));
auto translated = dragon | tf::tag(T);
translated retains the full API of dragon through the policy system. Any operation that accepts dragon also accepts translated.
Boolean operations
With the two forms ready, a boolean is a single call. Full documentation.
Union
auto [result_buffer, labels, face_labels] = tf::make_boolean(
dragon, translated, tf::boolean_op::merge);
Difference
auto [result_buffer, labels, face_labels] = tf::make_boolean(
dragon, translated, tf::boolean_op::left_difference);
left_difference keeps the left mesh minus the right. right_difference does the opposite.
Intersection
auto [result_buffer, labels, face_labels] = tf::make_boolean(
dragon, translated, tf::boolean_op::intersection);
Working with results
Every boolean returns three values. result_buffer holds the output mesh. labels marks each output face — 0 for faces originating from the first input, 1 from the second. face_labels maps each output face back to the index of its source face in the input, enabling attribute transfer.
To split the result into separate meshes by label:
auto [components, component_labels] =
tf::split_into_components(result_buffer.polygons(), labels);
Write the two halves to file:
tf::write_stl(components[0].polygons(), "first.stl");
tf::write_obj(components[1].polygons(), "second.obj");
Precomputed structures
When running multiple booleans on the same geometry — for example, subtracting a tool at different positions along a path — rebuilding spatial and topological structures every time is wasteful. Build the aabb_tree, face_membership, and manifold_edge_link once, then tag them onto the form:
tf::aabb_tree<int, float, 3> tree;
tree.build(dragon, tf::config_tree(4, 4));
auto fm = tf::make_face_membership(dragon);
auto mel = tf::make_manifold_edge_link(dragon);
auto tagged = dragon
| tf::tag(tree)
| tf::tag(fm)
| tf::tag(mel);
Now boolean at different positions. The structures are reused — only the transformation changes:
for (float t = 0.1f; t <= 1.0f; t += 0.1f) {
auto T = tf::make_transformation_from_translation(
tf::make_vector(0.f, t, 0.f));
auto [result, labels, face_labels] = tf::make_boolean(
tagged,
tagged | tf::tag(T),
tf::boolean_op::left_difference);
}
10 booleans on 2×500K polygon meshes in under 140ms total. Spatial and topological structures computed once.
Extracting intersection curves
Any boolean can also return the intersection boundary as polylines embedded on the surface. Pass tf::return_curves as an additional argument:
auto [result, labels, face_labels, curves] = tf::make_boolean(
dragon, translated,
tf::boolean_op::left_difference,
tf::return_curves);
auto paths = curves.paths_buffer();
auto points = curves.points_buffer();
If only the intersection curves are needed without the boolean itself:
auto curves_buffer = tf::make_intersection_curves(dragon, translated);
Higher precision
By default, trueform uses int32 coordinates with int64 intermediates and int128 predicates. For meshes spanning very large coordinate ranges, switch to int64 base precision:
auto [result, labels, face_labels] =
tf::make_boolean<tf::exact::int64>(
dragon, translated,
tf::boolean_op::left_difference);
Same API. Wider coordinate range.
Performance and robustness
All operations run at interactive speed on million-polygon meshes.
Apple M4 Max 16 threads Clang -O3 mimalloc
Real meshes are not ideal manifolds. trueform handles them directly — no preprocessing, no manifold requirement.
trueform provides a robust alternative to CGAL, MeshLib, and libigl for fast and exact mesh boolean operations.
Full benchmarks and methodology · How the algorithm works
@article{polydera:fast-mesh-booleans-in-cpp,
title={Fast Mesh Booleans in C++},
author={Sajovic, {\v{Z}}iga, Polydera},
year={2026},
url={https://polydera.com/tutorials/fast-mesh-booleans-in-cpp},
organization={Polydera}
}