MeshTools Decimate (Advanced)
Decimation with every knob exposed
- trimesh
- trimesh
MeshTools Decimate (Advanced) is the full-parameter version of the pack's decimator, and the first thing to know about it is that you probably don't need it. The pack ships a Simple Decimate node that does the same job with two settings, and the author's own docs say the simple one is easier for most users. So what is this for? When simple edge-collapse to a target count isn't good enough - when the shape is drifting, when you're pushing an extreme reduction, when you want the mesh to survive aggressive culling with minimal visual damage - the Advanced node hands you the entire meshlib DecimateSettings object as a pile of widgets and lets you drive it.
Under the hood it's the same machinery as Simple Decimate: meshlib's decimateMesh, with packMesh on and parallel processing via subdivideParts. The difference is every quality lever the library exposes. That's the pitch: edge-collapse decimation is a greedy process that decides which edges to collapse, and meshlib gives you knobs to bias that decision toward shape preservation, boundary stability, and triangle quality. This node exposes all of them.
The inputs that matter
There are a lot of fields here (the full list is the whole meshlib settings struct), but a beginner sets four:
target_face_numortarget_face_ratio- the destination, exactly like Simple Decimate. You must set one; if both are 0, the node raises aValueError. One of the two, not neither.subdivideParts(default 16) - parallel threads. Match your core count.strategy- the big one:MinimizeErrorpreserves shape,ShortestEdgeFirstis faster. If you care about quality, this is the first knob to set.
Then there's the quality tier you'll touch when the defaults disappoint:
maxError- maximum geometric error allowed per decimation step. Lower = more accurate. This is the classic "how much shape drift do I tolerate" dial.maxAngleChange- caps how much a face normal can swing per step (in radians). Great for keeping flat regions flat.maxTriangleAspectRatio- rejects decimations that create thin, sliver triangles;criticalTriAspectRatiois the hard version of the same idea.
Everything else - maxEdgeLen, maxBdShift, tinyEdgeLength, stabilizer, angleWeightedDistToPlane, optimizeVertexPos, collapseNearNotFlippable, touchNearBdEdges, decimateBetweenParts, minFacesInPart - defaults to "off/unset" and only applies when you give it a value. The node only sets a settings field if the widget is above its zero-value, so you can't break it by leaving them alone.
How it works
The mechanism detail that matters: a value only takes effect if it's non-zero (or true). maxError defaults to 0, which meshlib reads as "no constraint." So the sane workflow is: set your target count and strategy, run it, and then turn on maxError or maxTriangleAspectRatio one at a time if the output looks wrong. Throwing all sixteen knobs at the first run is how you end up unable to tell what moved.
The node also prints the resulting vertex/face counts to the console, which is the fastest way to confirm the decimation actually did what you asked.
Output is a single trimesh socket, ready to chain into Export or Postprocess.
Installing
cd ComfyUI/custom_nodes
git clone https://github.com/agenticvibes/ComfyUI-MeshTools
cd ComfyUI-MeshTools
pip install -r requirements.txt
Or search "ComfyUI-MeshTools" in ComfyUI Manager. The meshlib dependency comes from requirements.txt - MeshLib is a big industrial 3D library but installs from PyPI without model downloads or a GPU.
Common issues
- "target_face_num or target_face_ratio must be set" - same trap as the simple node. Both default to 0, and the node refuses to guess.
- It looks worse than Simple Decimate - you ran it with defaults, which is the same as Simple Decimate but slower to think about. Set
strategytoMinimizeErrorand givemaxErrora small value (0.01–0.05) for the shape-preserving behavior. - The output has thin triangles - push
maxTriangleAspectRatiodown. If you need to hard-guarantee quality, setcriticalTriAspectRatioand steps that violate it get rejected outright. - Slow on huge meshes - it's threaded via
subdivideParts, so make sure that's set to something near your core count rather than the default on a 4-core machine.
Honest take: most people will live happily in Simple Decimate. Reach for this one the day a decimated mesh makes you squint - and then change one knob at a time, because you'll never know which of the sixteen fixed it otherwise.
Inputs (19)
| Name | Type | Default | Description |
|---|---|---|---|
| trimesh | TRIMESH | Input mesh to decimate | |
| subdivideParts | INT | 161–64 | Number of parallel threads for decimation. Match to your CPU core count |
| target_face_numopt | INT | 0–10000000 | Absolute target face count. Set either this or target_face_ratio |
| target_face_ratioopt | FLOAT | 0–0.999 | Target as fraction of original faces (0.5 = half). Set either this or target_face_num |
| strategyopt | COMBO | None | Decimation priority: MinimizeError preserves shape, ShortestEdgeFirst is faster |
| maxErroropt | FLOAT | 0–1 | Maximum geometric error allowed per decimation step. Lower = more accurate |
| maxEdgeLenopt | FLOAT | Maximum allowed edge length after decimation | |
| maxBdShiftopt | FLOAT | Maximum boundary vertex displacement during decimation | |
| maxTriangleAspectRatioopt | FLOAT | Reject decimations that create thin triangles above this ratio | |
| criticalTriAspectRatioopt | FLOAT | Hard limit on triangle aspect ratio — decimation steps that exceed this are rejected | |
| tinyEdgeLengthopt | FLOAT | Edges shorter than this are prioritized for collapse | |
| stabilizeropt | FLOAT | Stabilization factor to prevent oscillation during optimization | |
| angleWeightedDistToPlaneopt | BOOLEAN | Weight error metric by face angles for more perceptually uniform results | |
| optimizeVertexPosopt | BOOLEAN | Optimize vertex positions after each collapse for better shape preservation | |
| collapseNearNotFlippableopt | BOOLEAN | Allow collapsing edges near non-flippable boundaries | |
| touchNearBdEdgesopt | BOOLEAN | Allow decimation of edges near mesh boundaries | |
| maxAngleChangeopt | FLOAT | Maximum allowed change in face normal angle per step (radians) | |
| decimateBetweenPartsopt | BOOLEAN | Allow decimation across part boundaries in multi-part meshes | |
| minFacesInPartopt | INT | Minimum faces to keep in each mesh part during parallel decimation |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| trimesh | TRIMESH | — |