Unity Workflow LibraryUWLAbout

Topic 8 · Art & models

AI mesh cleanup

Repair, reduce and re-UV generated meshes before import.

Load when you repair, reduce, re-UV, rebake or retexture a generated model. Generation rules (concepts, rerolls, rigging) live in the ai modelgen page; asset conventions and the FBX recipe in the blender page; how to drive Blender in the blender mcp page. Surveyed.

Fix it at the generator first (AI-MESH-FIX-UPSTREAM)

A cleanup step you do not need is the cheapest one. Every generate call pins its model version (ai_model / model_version) and asks for:

  • Meshy POST /openapi/v1/image-to-3d:
  • enable_pbr=true
  • remove_lighting=true (meshy-6 only)
  • pose_mode="a-pose" for characters
  • either should_remesh=true with topology and target_polycount, or a later POST /openapi/v1/remesh (5 credits; 100–300k faces; exports glb, fbx, obj, blend and others).
  • Tripo POST /v2/openapi/task, image_to_model:
  • pbr=true
  • smart_low_poly with face_limit (1000–20000), or quad (500–10000)
  • generate_parts when the model has separable parts
  • compress off: the meshopt or Draco default needs an importer extension Blender may lack.
  • Or run the separate smart-low-poly task with bake=true, so Tripo bakes the detail onto the low-poly mesh itself.
  • Fused limbs, a weapon fused to the body, and lighting baked into the albedo have no geometric repair. Regenerate (the ai modelgen page WEAPON-FUSED-REGENERATE-NEVER-CUT, GEN-REF-NO-SHADOW-BLEED) before paying for a manual fix.
  • Read back the consumed credits on every call.

Measure before and after (AI-MESH-MEASURE-FIRST)

Run the numbers gate first and last:

blender -b --factory-startup --python-exit-code 1 --python tools/blender/ai_mesh_report.py -- <model> --budget <tris>

It prints one AI_MESH_REPORT {json} line with: - triangle count - shells, counted after joining vertices at the same position - open and non-manifold edges, counted by position - zero-area faces, UV layers, textures and their colour spaces, and size in metres - a verdict: solid or island-soup, automated_repair_allowed, and which steps apply

  • GLTF-SEAM-SPLIT-SHELLS: a glTF stores a separate vertex on every UV seam and hard edge, so an imported mesh looks like hundreds of loose pieces when it is one solid piece.
  • Measured on three Meshy building parts: 133, 423 and 3812 pieces as stored, each 1 after joining vertices at the same position.
  • Any island count, island cull or "largest component" check that runs on the as-stored mesh is measuring UV seams. Count by position (the report does) before calling a mesh island-soup.
  • island-soup (largest positional shell under 90% of the vertices) follows the ai modelgen page: no automated cull, weld or remesh. Regenerate, or repair by hand.
  • solid meshes may take the automated steps below.

The pass, in order (AI-MESH-CLEANUP-PIPELINE)

Heavy steps run headless on a copy (the blender mcp page BLENDER-HEADLESS-RECIPE). The mesh libraries run in their own environment (uv run --project tools/blender/cleanup python ...: pymeshlab, pymeshfix, xatlas, trimesh, all pinned), never inside Blender's Python. Hand meshes between the two as .ply, .obj or .glb.

# Step Tool Guard
1 Measure ai_mesh_report.py Decides which of the steps below run.
2 Weld Merge by Distance, threshold about 1e-5 × largest dimension A fixed threshold collapses fingers and straps on small models. UVs are stored per face corner, so welding keeps the UV seams.
3 Normals clear custom split normals, normals_make_consistent(inside=False), smooth by angle the blender page has the full normals step.
4 Holes, non-manifold, self-intersection pymeshlab meshing_repair_non_manifold_edges / _vertices, meshing_close_holes(maxholesize=...); self-intersecting faces removed, then holes closed. Last resort: pymeshfix. pymeshfix and "remove connected component" keep only the largest piece by default, which deletes eyes, teeth and accessories. Cull by relative size (under 1–2% of the bounding box), never "keep largest".
5 Surface noise pymeshlab apply_coord_taubin_smoothing (keeps volume) Laplacian smoothing shrinks the mesh. Any smoothing moves vertices, so plan the rebake in step 8.
6 Reduce to budget Decimate COLLAPSE, or pymeshlab meshing_decimation_quadric_edge_collapse_with_texture Decimation keeps UVs. Every remesher throws them away: voxel, QuadriFlow, Instant Meshes, Quad Remesher and vendor quad remesh. A remesh commits you to steps 7 and 8.
7 UVs, only if lost xatlas (segments, unwraps and packs in one call; texels_per_unit sets texel density) SLIM or Minimum Stretch needs seams, and AI meshes have none, so it returns one distorted island. Smart UV Project is the in-Blender fallback.
8 Bake high to low Cycles Selected to Active: diffuse colour, then tangent-space normal, then AO. Extrusion 0.5–2% of the bounding box, margin 16 px at 2k. Every material needs an active Image Texture node. The image exists only in memory until image.save. A cage too small leaves holes; too large projects from the wrong surface (armpits, between fingers).
9 Albedo lighting Regenerate with remove_lighting, or retexture (step 10) No reliable headless de-lighter exists for UV atlases. Substance Sampler's delight is 2D and GUI-only. ArtEngine was discontinued in 2022.
10 Retexture Meshy POST /openapi/v1/retexture with enable_original_uv=true and enable_pbr=true (10–15 credits, refunded on failure; recipe in the blender page) Tripo texture_model accepts only a Tripo task id, and whether it keeps UVs is undocumented. Test one model before relying on it.
11 Lightmap UV2 Unity importer "Generate Lightmap UVs"; a separate xatlas channel if that fails Never reuse a tiled UV0. No UV2 on skinned meshes.
12 Size, pivot, export the blender page prep pass and FBX recipe, or GLB Re-run step 1 as the exit gate, then check in Unity.
  • Live fixes in the GUI: blend-ai's analyze_mesh_quality, its sculpt tools, and a screenshot after each change, for one local defect. the ai modelgen page LOCALIZED-REPAIR-PROJECTION covers repairing one region.
  • Paid tools are the developer's call:
  • Quad Remesher gives the best quads, but it needs a GUI Blender and does not finish reliably under -b.
  • UVPackmaster 3 (from $44) only packs; it does not unwrap.
  • Neither is required by this pass.
  • Skills on skills.sh (meshy-dev/meshy-3d-agent, tripo and retopology skills) mostly wrap the generation APIs. None implements this measure-repair-rebake-export loop, so treat them as references.

BAKE-TRAPS-ON-GENERATED-MESHES

Measured while baking generated props to a budget mesh:

  • A mirrored half keeps its winding: 15,049 of 30,666 faces rendered inside out until the mirror ran reverse_faces. Gate on an inverted-face count.
  • Keep the generator's maps on a mirrored piece; re-baking from the mirrored source blurred edge detail (edge energy 9 vs 27). Shade mirrored donor copies flat: smoothed normals lean across a flat panel and the mirror flips the lean into a light seam.
  • Cap occlusion reach at half the thinnest side: a 6 cm door baked 17% of its texels black at a reach of 5% of its 2.14 m size (8% after). Bake occlusion from the high-poly source; the budget mesh lacks the relief (median 255/255). Skip occlusion on flush sheets, where rays start under the face (37% black).
  • Pin hole-fill face UVs to one point: a fill face copies neighbour UVs and stretches visible texture across the surface. Remove pinch edges (shared by three faces) before filling; 36 open edges survived otherwise.
  • Cuts through a generated mesh: a per-vertex squash folded 349 faces, a boolean took 26 GB and dropped posts, EXACT left 294 islands; Blender's MANIFOLD boolean solver closed it (4 islands).
  • Render review boards with ACES, not Blender's default AgX, to match HDRP, and review a lit/clay/albedo sheet: a flat Workbench board hid missing occlusion, roughness and normals.
  • Held first-person items need their own texel density: 12k tris at 512 px read soft next to the source; a held pass (LOD0 40k tris at 4096 px/m) fixed it.