# Eliminating Non-Manifold LLM Meshes in Blender bpy and Three.js r170+

Published: 2026-10-18 | Category: 3D Geometry & Verification | Live Surface: https://assets.baltor.ai

## Outcome first: watertight geometry over visual tricks

When asked to create a fluted architectural column, a procedural ceramic vessel, or an articulated character mesh in Blender `bpy` or Three.js `r170+`, unassisted language models fail in two predictable ways. Either they emit a smooth cylinder with a 2D noise texture and call it "3D surface relief," or they emit raw vertex arrays with open seam edges (`valence != 2`), inverted face winding, and degenerate zero-area triangles at the poles. By placing a deterministic half-edge manifold verifier and parametric lathe generator inside the Baltor harness ([assets.baltor.ai](https://assets.baltor.ai) and [compare.baltor.ai/?mode=relief](https://compare.baltor.ai/?mode=relief)), 100% of promoted meshes satisfy watertight 2-manifold invariants before rendering or `.glb` export.

## Three geometric invariants every procedural mesh must pass

A triangle mesh `M = (V, E, F)` is a closed, orientable 2-manifold only if it satisfies three topological and geometric checks after spatial vertex welding (`epsilon = 1e-5`):

1. **Edge valence of exactly 2**: Every undirected edge must be shared by exactly two triangles. If an edge belongs to only one triangle (`valence == 1`), the mesh has a hole or unwelded UV seam. If an edge belongs to three or more triangles (`valence >= 3`), the mesh has internal non-manifold fins that break boolean modifiers and shadow maps.
2. **Consistent counter-clockwise (CCW) half-edge orientation**: If triangle `A` traverses edge `(u, v)`, the adjacent triangle `B` must traverse the opposite directed half-edge `(v, u)`. If both traverse `(u, v)`, their surface normals point in opposite directions, producing black shading bands under PBR lighting.
3. **Non-degenerate triangle area and outward signed volume**: Every triangle must have cross-product area `0.5 * ||(B - A) x (C - A)|| > 1e-8`, and the total signed volume summed over `dot(A, cross(B, C)) / 6.0` must be strictly positive.

```javascript
// Half-Edge Manifold & Winding Verifier for Three.js BufferGeometry
export function verifyWatertightManifold(positions, indices) {
  const directedEdges = new Map();
  const undirectedCounts = new Map();
  let degenerateFaces = 0;
  let signedVolume6 = 0;

  for (let i = 0; i < indices.length; i += 3) {
    const a = indices[i], b = indices[i + 1], c = indices[i + 2];
    const ax = positions[a*3], ay = positions[a*3+1], az = positions[a*3+2];
    const bx = positions[b*3], by = positions[b*3+1], bz = positions[b*3+2];
    const cx = positions[c*3], cy = positions[c*3+1], cz = positions[c*3+2];

    const abx = bx - ax, aby = by - ay, abz = bz - az;
    const acx = cx - ax, acy = cy - ay, acz = cz - az;
    const nx = aby * acz - abz * acy;
    const ny = abz * acx - abx * acz;
    const nz = abx * acy - aby * acx;
    if (Math.hypot(nx, ny, nz) < 1e-8) degenerateFaces++;

    signedVolume6 += ax * (by * cz - bz * cy) + ay * (bz * cx - bx * cz) + az * (bx * cy - by * cx);

    for (const [u, v] of [[a, b], [b, c], [c, a]]) {
      const dirKey = `${u}:${v}`;
      directedEdges.set(dirKey, (directedEdges.get(dirKey) || 0) + 1);
      const undirKey = u < v ? `${u}:${v}` : `${v}:${u}`;
      undirectedCounts.set(undirKey, (undirectedCounts.get(undirKey) || 0) + 1);
    }
  }

  let boundaryEdges = 0, nonManifoldEdges = 0, windingConflicts = 0;
  for (const [edge, count] of undirectedCounts.entries()) {
    if (count === 1) boundaryEdges++;
    else if (count > 2) nonManifoldEdges++;
    const [u, v] = edge.split(":");
    if (directedEdges.get(`${u}:${v}`) !== 1 || directedEdges.get(`${v}:${u}`) !== 1) {
      windingConflicts++;
    }
  }
  return {
    isManifold: boundaryEdges === 0 && nonManifoldEdges === 0 && windingConflicts === 0 && degenerateFaces === 0 && signedVolume6 > 0,
    boundaryEdges,
    nonManifoldEdges,
    windingConflicts,
    degenerateFaces,
    signedVolume: signedVolume6 / 6.0
  };
}
```

## Inspecting true relief vs. bump illusions with a 3x Loupe

On [compare.baltor.ai/?mode=relief](https://compare.baltor.ai/?mode=relief), you can hover the 3x magnification loupe across the vessel silhouette. The unassisted model's cylinder silhouette stays razor-flat because its fluting is only a fragment shader perturbation. The Baltor-verified mesh displaces radial vertices `r(theta, v)` along continuous harmonic flutes and welds the polar caps cleanly, casting true self-shadowing across the grazing rim light.
