TripoSG Decode Mesh ⚡
TripoSG's Marching Cubes Step
- triposg_model
- shape_latent
- mesh
This is the node that hands you something you can actually use. A shape latent goes in, a ComfyUI core MESH comes out - watertight, untextured, and dense as hell. Everything about the geometry quality of a TripoSG run is settled here, in three integers.
What it does
TripoSG's VAE isn't an image VAE. It's an SDF decoder: given a latent and a 3D coordinate, it tells you how far that point is from the surface. So to get a mesh you sample the field and run marching cubes, and the whole trick of this node is where you sample.
- A dense occupancy grid at
2^dense_octree_depthper axis - default 8, so 256³. Cheap, coarse, and enough to find roughly where the surface lives. - Then a hierarchical octree refined to
2^hierarchical_octree_depth- default 9, so 512³ - but evaluated only in cells near the surface. That's the whole economy of the thing: 512³ of resolution without paying for 134 million field evaluations in empty space. - Lewiner marching cubes (via
scikit-image) extracts the isosurface from that hierarchy, inside a cube of half-extentbounds. - The result is batched into core's
MESH, with padded vertices/faces and explicit counts when you feed it more than one latent - so a batch of images decodes to a batch of meshes without cross-contamination.
The output mesh is core's own type, which is the nicest sentence in this article. SaveGLB, Preview 3D, Mesh To File 3D, Decimate Mesh, Fill Holes, Unwrap Mesh, Save 3D Model - all of them take it directly, no conversion, no adapter.
The three numbers
dense_octree_depth- 5 to 10, default 8. Sets the coarse grid. Raising it finds the surface more reliably on sparse geometry but costs memory fast; 2¹⁰ per axis is 1024³ booleans and you'll feel it.hierarchical_octree_depth- 5 to 11, default 9. This is effectively your output resolution. 9 gives 512³ - sub-millimetre detail on a 10cm object, and hundreds of thousands of faces. Push to 10 for a print-ready surface on a small object, drop to 8 if you're iterating and don't care yet. It must be at leastdense_octree_depth; the node checks and raises if you invert them, which saves you from a confusing "why is my mesh empty".bounds- 0.5 to 2.0, default 1.005, step 0.005. Half-extent of the sampled cube. The POC's 1.005 is deliberate: slightly more than 1.0 so the surface isn't clipped at the model's normalised extent. Shrink it to zoom in on a detail; grow it if your shape is getting cut off at the edges. Both are unusual moves - the default is right almost always.
Output cost, honestly: at the POC defaults you get a mesh with hundreds of thousands of faces. That's not a bug, it's marching cubes at 512³, and every refinement step quadruples the face count in the areas it touches. If you're shipping into a game engine or a slicer, put Decimate Mesh after this and aim for what your target actually wants.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/ChrisColeTech/ComfyUI-GGUF-Loader
pip install -r ComfyUI/custom_nodes/ComfyUI-GGUF-Loader/requirements.txt
Manager install is fine. The one dependency that matters here is scikit-image:
pip install scikit-image
Skip it and the node fails with "TripoSG Decode Mesh needs scikit-image (pip install scikit-image; listed in requirements.txt)" - which is a better error than most, and mildly annoying because pip install --upgrade gguf (the README's headline install) doesn't pull it.
Traps and honest limits
The mesh is watertight but it is not clean. Lewiner marching cubes gives you a closed, printable surface with sane normals - no holes on a manifold it understood - but the topology is uniform triangles following a voxel grid, not edge loops. This is the field-wide problem the KB keeps landing on: great for a static prop or a 3D print, useless for anything that has to deform. Rigging a mesh like this means retopology first, and UniRig's skinning stage in this same pack will bind to whatever you feed it, voxel-ish triangles included.
No texture, no UV. TripoSG is shape only. Come in expecting to unwrap and paint, or accept grey.
Empty or blobby mesh? In order: check the octree depths aren't inverted (it raises, but people swap the widgets and don't read the traceback), check bounds hasn't been shrunk so far the surface is outside the cube, and check you didn't sample at 20 steps with guidance_scale at 2. The decode node is faithfully extracting whatever the sampler imagined.
Batch of meshes, one saved file. SaveGLB handles batches differently from what you might expect, and padded batches carry zeroed vertices past each item's count. Decode one at a time if you want one file per object; the padding is for correctness, not for saving.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| triposg_model | CCTECH_TRIPOSG_MODEL | — | |
| shape_latent | CCTECH_TRIPOSG_LATENT | — | |
| dense_octree_depth | INT | 85–10 | Dense occupancy grid = 2^depth per axis (POC: 8 -> 256^3). |
| hierarchical_octree_depth | INT | 95–11 | Final surface resolution = 2^depth per axis, refined only near the surface (POC: 9 -> 512^3). Must be >= dense depth. |
| bounds | FLOAT | 1.0050.5–2 | Half-extent of the sampled cube (POC: 1.005). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| mesh | MESH | — |