Auto-fix for GEOMETRY_INCONSISTENT_WINDING — flood-fills
triangle winding from a seed, flipping every triangle whose
shared edge runs the same direction as its source neighbour
(i.e. winds the wrong way relative to the seed).
Algorithm:
Build an undirected-edge → adjacent-triangle map.
Pick triangle 0 as the seed (assumed correct winding).
DFS through neighbours: for each unvisited triangle that
shares an edge with the current one, look at the post-flip
directed edges of both. If they traverse the shared edge
in the same direction, mark the neighbour as needing a
flip.
Repeat from each unvisited triangle to handle disconnected
components.
Rewrite geom.indices: triangles flagged for flip get
their last two indices swapped (i0,i1,i2 → i0,i2,i1).
Vertex normals don't need updating — smooth normals are
direction-independent of the per-triangle winding that produced
them. edgeIndices is undirected, also untouched.
Caveat — seed selection. If more than half of the original
triangles were wound wrong, the seed is one of those wrong
triangles, and the fix flips the correct half to match. The
resulting mesh is consistently wound but inside-out. Re-run the
inspection: GEOMETRY_INCONSISTENT_WINDING will be silent
(consistent) but visual inspection will show the model rendering
back-faces. In that case the user can apply the fix again with
the index ordering reversed (manual triage) or re-author the
model upstream.
Idempotent: returns {fixed: false} on a non-triangle
primitive, missing indices, or a mesh that already has every
directed edge unique.
Auto-fix for
GEOMETRY_INCONSISTENT_WINDING— flood-fills triangle winding from a seed, flipping every triangle whose shared edge runs the same direction as its source neighbour (i.e. winds the wrong way relative to the seed).Algorithm:
geom.indices: triangles flagged for flip get their last two indices swapped (i0,i1,i2→i0,i2,i1).Vertex normals don't need updating — smooth normals are direction-independent of the per-triangle winding that produced them.
edgeIndicesis undirected, also untouched.Caveat — seed selection. If more than half of the original triangles were wound wrong, the seed is one of those wrong triangles, and the fix flips the correct half to match. The resulting mesh is consistently wound but inside-out. Re-run the inspection:
GEOMETRY_INCONSISTENT_WINDINGwill be silent (consistent) but visual inspection will show the model rendering back-faces. In that case the user can apply the fix again with the index ordering reversed (manual triage) or re-author the model upstream.Idempotent: returns
{fixed: false}on a non-triangle primitive, missing indices, or a mesh that already has every directed edge unique.