🔧 Fix Mesh Winding (TRELLIS Cleanup)
Why your TRELLIS mesh renders inside-out
- glb_path
You drop a generated GLB into a viewer and half the surface is black, or flat-shading spikes shoot out of the shoulders like the model is having a bad day. That's not a lighting bug and it's not your renderer - a big chunk of the faces have their vertices wound backwards, so their geometric normal points into the body. MeshFixWinding is a single-purpose fix for exactly that, and the pack's own docstring says it plainly: TRELLIS output arrives with roughly 25–45% of faces inward-facing.
If you've ever watched a vision model score your asset 1/10 and call it "severe mesh corruption", this is usually the cause. The silhouette looks fine in a thumbnail; the normals are garbage.
How it works
For every triangle it computes the geometric normal with a cross product, then compares it against a vector from the mesh centroid to the face centre. Dot product negative means the face is pointing back toward the middle of the object, so it swaps vertex 1 and vertex 2, which flips the normal outward. Then it writes the patched indices back into the BIN chunk and re-emits the GLB.
There's a second job hiding in the same node: texture re-encoding. Generated meshes often pack WebP textures, and a lot of downstream consumers choke on those. With preserve_rig off (the default), it also converts packed textures to PNG. That's why the flag exists - the conversion runs the mesh through a trimesh round-trip, which is not something you want happening to a rigged file.
Inputs and outputs
glb_path- the GLB to repair: a filename in ComfyUI'sinput/, or an absolute path.preserve_rig- leavefalsefor fresh TRELLIS output you're about to texture and rig. Flip it totruefor any GLB that already has skinning, joints, or animation, so the texture pass is skipped and the rig stays intact.
The output is a new glb_path in ComfyUI's output/, with a three_model UI entry pointing at it. It's marked as an output node, so it fires even with nothing wired after it.
Where you'd put it in a workflow
Front of the mesh chain. Generate → Fix Winding → dedup → decimate → rig/texture. Doing this last is a common mistake: if you fix winding after you've already baked a normal map or projected a texture, you've done the work against broken normals and you get to start over.
Install
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git
Restart ComfyUI, or install the pack from ComfyUI Manager by searching melite-poser-nodes. requirements.txt pulls py-soma-x[anny], trimesh and xatlas - the fix-winding path itself needs trimesh (for the texture re-encode) plus numpy and PIL, which ComfyUI already has.
Where people get burned
The centroid test is a heuristic, and it fails on concave shapes. "Face points away from the middle of the object" is true for a torso, a sword, a helmet shell. It is wrong for geometry that legitimately faces inward: the inside of a cup, the inner surface of a hollow coat, an ear canal, a hollow skull. Those faces can get flipped away from you and now the interior renders backwards. If you're cleaning something with real hollow structure, check the result in a viewer instead of trusting the log percentage.
Only the first primitive gets fixed. The flip pass reads meshes[0].primitives[0] - one primitive. A GLB with multiple primitives per mesh (common when a model has several material slots) will have the others left exactly as they were. If your asset has more than one material, run the node and then look at the parts, don't assume it's uniform.
preserve_rig: false on a rigged file is not recoverable. The PNG conversion rebuilds the mesh from scratch. Keep the original. There is no undo beyond re-running upstream.
Nothing here fixes self-intersections or non-manifold geometry. Winding is one specific defect. If the mesh is still ugly after this, the problem is topology, and the honest advice from the KB's 3D generation doc applies: no open model gives you production topology, so for anything that has to deform you're retopologising anyway.
The pack is MIT, published from an internal estate pipeline in late 2026, and has no public issue tracker chatter to lean on - the class docstring is unusually candid about the mechanism, which is exactly what made this article possible. Read the source when the output surprises you; it's short.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| glb_path | STRING | — | |
| preserve_rig | BOOLEAN | false | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| glb_path | STRING | — |