Nodes/ComfyUI-Tools/3d - Simplify Trimesh using meshoptimizer
ComfyUI Node

3d - Simplify Trimesh using meshoptimizer

Drag a generation-scale mesh back to something that opens

By visualbruno·Created 8 months ago·Updated a day ago· 5
3d - Simplify Trimesh using meshoptimizer
  • trimesh
  • trimesh
◄target_face_count100000►
◄weld_verticestrue►
◄lock_bordertrue►
◄sparsefalse►
◄error_absolutefalse►
◄prunefalse►
◄regularizefalse►
◄permissivefalse►
◄regularize_lightfalse►

The node that makes the file openable

Image-to-3D output is not polite about triangle counts. TRELLIS.2 decodes through a voxel field that can be a billion cubes' worth of detail, remesh cascades multiply faces, and the result is a mesh that stalls Blender's import, bloats a GLB past what a game engine will take, and makes any export step crawl. The advice that circulates in the TRELLIS workflow threads is always the same and always vague - "lower all the simplify mesh nodes" - so here's the node doing it.

3d - Simplify Trimesh using meshoptimizer is a decimator. It takes a TRIMESH and gives you back a TRIMESH with fewer triangles, using meshoptimizer, the library game engines have used for LOD generation for years. Unlike the 3D generation models themselves, it has no CUDA requirement and no model to download - it loads a prebuilt libmeshoptimizer.so / meshoptimizer.dll that ships in the pack's libs/ folder via ctypes and calls straight into it.

How the simplification actually works

Two passes, in this order, and the order matters.

First the node welds vertices if weld_vertices is on (it is by default). meshoptimizer's remap routine merges vertices that sit at exactly the same position, then rewrites the index buffer to match. This is the step that makes or breaks the result: generated meshes are usually split along UV and normal seams, so what looks like one surface is secretly disconnected triangles. A simplifier can't collapse across a seam it doesn't know is shared, so welding first is what gives it continuous topology to chew on. Leave this on.

Then it calls meshopt_simplify with your target face count (multiplied by three to get the index count it actually wants) and compacts the vertex buffer afterwards. The wrapper passes a fixed internal error budget of 1e-2, relative to the mesh's extents unless you flip error_absolute. That budget is the reason target_face_count is a request rather than a promise: the simplifier stops when it runs out of allowed geometric error, so a very aggressive target on a mesh with fine detail can leave you with more faces than you asked for.

Inputs that matter

  • trimesh - the mesh to thin out.
  • target_face_count - 100,000 by default, anywhere from 1 to 10,000,000. For printing, a couple of hundred thousand is generous; for a game prop, low tens of thousands.
  • weld_vertices - keep it true.
  • lock_border - true by default. It pins vertices on open boundary edges so the mesh outline doesn't move. Harmless on a closed mesh, and the thing you want on a mesh with deliberate openings - though on a triangle-soup mesh it can also be what stops you hitting the target.
  • The rest - sparse, error_absolute, prune, regularize, permissive, regularize_light - are meshoptimizer's own option flags passed straight through as booleans, and they're for topology that isn't a clean closed surface. prune lets the simplifier drop loose geometry and bail out early once the error budget is spent; sparse is for meshes built from disconnected pieces (voxel-grid output being the obvious case); regularize chases evenly sized triangles; permissive stops respecting topology and boundaries, which is what you want on pure triangle soup and never on a rig-relevant mesh. If you need exact semantics, these map one-to-one onto meshoptimizer's documented meshopt_Simplify* flags - the node is a thin wrapper over them.

Output: trimesh. It's an output node, so it runs even if nothing consumes it - handy when you just want the geometry collapsed before an export.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/visualbruno/ComfyUI-Tools

Or search "ComfyUI-Tools" in ComfyUI Manager and restart. The pack's requirements.txt installs trimesh, which this node needs. There is currently no README in the repo - it's brand new - so the source is the documentation.

Where people get burned

It returns geometry, not attributes. The simplifier is fed vertex positions only (a 12-byte stride, XYZ) and rebuilds vertices and faces. UVs, normals and vertex colours tied to the old vertex buffer are not carried along, so don't simplify a textured mesh and expect the mapping to survive - simplify first and unwrap/texture after, which is the order a projection-texturing pass wants anyway.

On a platform without the prebuilt binary, nothing in the pack loads. The library is grabbed with ctypes.CDLL at import time - Linux x86-64 gets libmeshoptimizer.so, Windows gets the DLL, and anything else (Apple Silicon, ARM Linux, macOS generally) throws on import and takes every other node in the pack down with it, not just this one.

TRIMESH is just a socket type name. The node wants an actual trimesh.Trimesh, so it only connects to nodes from packs that produce that same object type. If you can't connect a mesh you're looking at, you're holding someone else's mesh type - the TRELLIS.2 variant of this node exists for exactly that reason.

Decimation is not retopology. You're collapsing triangles, not producing clean quad loops. It gets a mesh small enough to work with; it doesn't get you game-ready topology, and nothing in open 3D generation does.

CategoryVisualBrunoTools/3d

Inputs (10)

NameTypeDefaultDescription
trimeshTRIMESH—
target_face_countINT1000001–10000000—
weld_verticesBOOLEANtrue—
lock_borderBOOLEANtrue—
sparseBOOLEANfalse—
error_absoluteBOOLEANfalse—
pruneBOOLEANfalse—
regularizeBOOLEANfalse—
permissiveBOOLEANfalse—
regularize_lightBOOLEANfalse—

Outputs (1)

NameTypeDescription
trimeshTRIMESH—