---
title: "AI 3D model generation"
slug: /workflows/ai-3d-model-generation/
markdown: /workflows/ai-3d-model-generation.md
section: art-audio-animation
tier: 2
summary: "Generate game 3D assets from images with measured proportions."
sources: ['ai-modelgen.md']
canonical: https://unityworkflowlibrary.dev/workflows/ai-3d-model-generation/
read_first: /read-first.md
---

> Read first, on every prompt: [non-destructive rules](/read-first.md).

# AI 3D model generation

> Generate game 3D assets from images with measured proportions.

Cross-project rules for generating game 3D assets from AI (Meshy multi-image-to-3d and kin).
Project-level procedure lives in the project's own pipeline doc; these are the lessons that transfer.

- **GEN-REF-NO-SHADOW-BLEED (HARD) — strip cast shadows / AO / lighting gradients from every
  plate before image-to-3D or retexture.** The 3D stage bakes whatever lighting is in the
  pixels into the albedo. A cinematic lit ref becomes a dirty texture even when the mesh is
  right. Feed even-unlit plates (flat illumination, no ground contact dark, no rim). Also
  drop micro-holes/filigree the poly budget cannot hold — simplify the ref rather than
  hoping more tris reconstruct cutouts. (a mobile strategy game: Undead castle mesh accepted,
  shadows in the ref bled into paint.)
- **MULTI-OBJECT-INPUT (HARD) — every image fed to an image-to-3D task must contain exactly ONE
  subject, and you must EYEBALL EVERY image, not just the first.** The 3D stage faithfully builds
  everything in frame: a turnaround sheet on one canvas, two poses side by side, a prop, a second
  figure — each becomes a real extra object fused into the output mesh. Multi-view concept
  generators sometimes return one image CONTAINING several poses — crop to a single subject or
  reject before spending on 3D. (a mobile strategy game air-unit batch; required after
  duplicate-object outputs.)
- **SKELETON-FIT-LANDMARKS (HARD) — "A-pose" in a prompt is not a bind contract.** If generated
  bodies must wear a licensed pack skeleton, encode **measured** joint fractions of the pack's
  *visual* mesh height (pelvis / shoulder / fist height and fist |x|), and scale the copied
  armature by `new_mesh_h / pack_visual_h`, never `new_mesh_h / armature_h`. Bone-span is shorter
  than the skinned mesh (helm/boots live outside Head/Foot); filling the mesh with the bone chain
  overscales A-pose arms (~1.23× on a Bip001 infantry pack) and is why a "slightly larger"
  weapon-free body misses the fists. Gates must print a **named fail class**
  (`ARMS_GLUED`, `STUBBY_ARM_SPAN`, `BONE_FILL_OVERSCALE`) — a boolean limb-count OK that only
  catches extra arms will green-light glued-arm barrels. (a mobile strategy game: pack visual
  1.595 m vs bone-span 1.293 m; 1.70 m melee is a 1.07× visual scale, not 1.32× bone-fill.)
- **HUMANOID-CLIPS-NOT-BLENDER-ORIGINAL (HARD) — do not play Unity Humanoid pack clips in Blender as the bind control.** Rest overlay vs the pack **mesh** FBX at authored rest. Walk/attack/death stills come from Unity Humanoid playback. See the blender page.
- **FREE-MULTIVIEW-IDENTITY (HARD) — when multi-view is not a single provider multiview task,
  only `view_0` is a new identity gen; every other angle is image-edit of `view_0` (“exact same
  character, only change camera”).** Three independent free gens as a “turnaround” = three
  different characters and poisons 3D. Prefer front · elevated three-quarter · back for RTS-like
  games — pure orthographic side T-pose invents extra arms. Bodies for socketed weapons: empty
  closed fists, no held/fused weapons on the body concept (weapon = separate asset). Copy
  accepted images into a durable repo work dir the same turn; free/Imagine tools rate-limit —
  keep parallel gens low (≈2). (a mobile strategy game free-concept / Grok Imagine.)
- **Reconstruction hallucinations are a separate failure class view-auditing cannot prevent:**
  when supplied views disagree hard (long tail reads as a neck from behind; side profile shares
  no silhouette with the front), the solver duplicates the subject or fuses parts wrong
  (two-headed tail). Cap the cost: **two failed 3D rolls on the same concept = STOP** — change
  the concept pose (raised/V-folded wings, compact connected silhouette) or take the design back
  to the developer. Never roll a third time on identical inputs.
- **Island-soup meshes take NO geometry heuristics.** AI-generated meshes are hundreds of loose
  shells with no solid core (largest island can be <100 verts of thousands); every automated
  island pass tried — size-threshold debris cull, nearest-gap docking — false-positived en masse
  and destroyed the model. Manual per-case Blender fixes or a reroll; never a heuristic. Visual
  gap fixes that ARE safe: double-sided material (`_Cull 0`) for single-sheet cloth/wings, and an
  interior occluder plug (separate object inside the cavity) for holes backed by geometry.
  **Count shells by vertex POSITION before calling a mesh island-soup:** glTF splits every UV seam, and
  three Meshy parts stored as 133, 423 and 3812 pieces were each ONE shell once coincident vertices
  were joined (the ai mesh cleanup page GLTF-SEAM-SPLIT-SHELLS, `tools/blender/ai_mesh_report.py`).
- **IP-DISTINCTIVENESS gates the 2D concept (HARD).** Keeping franchise terms out of prompts does
  not stop generators from converging on famous archetypes (armored gryphon + knight rider =
  WoW's gryphon rider; orc on wyvern = wind rider). At the concept gate ask: "would a player
  screenshot this and name a specific franchise unit?" If yes → redesign the creature/silhouette,
  not the palette. Developer-caught on a mobile strategy game.
- **Inspect on a flat loud background before accepting any mesh.** Isolated render (dedicated
  layer, solid magenta clear, flat ambient, 6 views): enclosed background pixels inside the
  silhouette = see-through holes (flood-fill from image borders finds them mechanically); a
  front-culled unlit duplicate shows exposed-interior regions. Both are ASSISTS to locate what an
  eye reported — the developer's eye is the accept gate, because on island-soup a detector strong
  enough to catch everything false-positives too much to act on automatically.
- **Download outputs immediately** — non-Enterprise Meshy task assets expire after ~3 days, and
  any later re-export (mobile-tier remesh at a lower polycount, format change) needs the task id
  alive. Decide quality tiers and fire the cheap remesh in the SAME session as the generation.
- **FREE-RETRY-FIRST — a same-input reroll rides the provider's free-retry entitlement, never a
  fresh paid task.** Meshy plans bundle free retries on COMPLETED generations ("re-run the
  generation for free — no credits deducted"; Pro 4 / Premium 12 / Ultra 40 / Studio 24 /
  Enterprise 100 at) — but only as a WEB-WORKSPACE button: the API has no retry
  endpoint on non-Enterprise plans, so an API-only pipeline silently pays full price for every
  hallucination-class reroll (invented floaters, duplicated creature, deformed limb from a clean
  concept) the plan would have covered. Route: defect with unchanged inputs → workspace Retry
  (free) → download; a paid task only when the INPUTS change (view set, crops, regenerated
  concept). Generalize: before wiring any generation provider, audit its retry/refund surface —
  entitlements often live only in the web app and never appear in the API reference. The
  two-roll stop binds on free retries too: it exists for recurrence, not just cost. (a mobile strategy game: ~250 cr of demon/air same-input rerolls were paid at full API price while the
  plan's free retries sat unused.)
- **MIRROR-AXIS-PROOF — before mirroring geometry to rebuild a missing symmetric part, PROVE the
  mirror axis, and expect the defect to be SMALL.** AI meshes import with arbitrary axis
  conventions (one Tripo GLB: X = body, Y = left-right, Z = up), and a voxel/vertex
  mirror-asymmetry scan across the WRONG axis reads head-vs-tail as "missing half" — acting on
  that builds a two-headed Janus model. Two sanity gates before any mirror edit: (1) a missing
  wing/limb is a small unmatched cluster (~a few hundred verts) on the intact side — if ~half the
  mesh reads unmatched, the axis is wrong, re-scan across the other axis; (2) verify axes
  visually from a top-view render, never from island bbox guesswork. When copying the intact
  part: hand-pick its components (print candidates, eyeball before committing — per-mesh manual
  pick, not a banned island heuristic), and exclude anything that already HAS a twin — mirroring
  a twinned panel lands a coincident z-fighting double. The hole detector cannot see
  missing-silhouette defects: the accept evidence is a before/after render of the VIEW where the
  defect was reported. Always run the flat-background inspection gate on the mirrored result —
  it is what caught the Janus attempt (original file untouched, so the wrong mirror cost
  nothing). **Corollary (same model, next day): per-component surgery LOSES on wing stacks —
  half-mirror is the default repair.** Wings on AI models are layered multi-sheet fans, and a
  nearest-neighbor twin test false-positives on them (an absent panel's mirrored position is
  occupied by the OTHER side's forewing, so it reads "twin present") — the surgical mirror
  shipped a half-wing twice. Panel-level completeness is judged by EYE on ~1500² closeup renders
  (a rear view showed 2 panels vs 1 instantly). Once the left-right axis is visually proven,
  bisect at the midline, keep the complete half, and Mirror-modifier the whole model
  (clip+merge); centered riders/heads symmetrize imperceptibly. Reserve per-component copies for
  models whose authored asymmetry must survive. (a mobile strategy game: Tripo moth right
  hindwing, fixed free in Blender on the third attempt — half-mirror across the proven axis.)
- **LOCALIZED-REPAIR-PROJECTION — one bad region on an accepted model (the classic: a small-in-
  frame face turned to mush) is repaired in TEXTURE SPACE for ~0-9 cr, never by re-rolling the
  whole model.** Recipe: (1) diagnose with closeup renders — geometry mush vs texture mush;
  (2) the concept's own face is usually <40 px, too soft as a repair source — i2i a "closeup
  portrait of this EXACT character, same colors, plain white background" from the upscaled head
  crop (lands 1:1); (3) project-bake it onto the head in Blender headless: render a PREVIEW from
  the projector camera first (that frame IS the projection space — align the portrait to mesh
  features in 2D, never by tuning bboxes blind), alpha-key the background and keep only a
  feathered face band, then bake `mix(original, canvas, alpha)` as EMIT into the basecolor via
  the original UVs. Two passes are mandatory on faceted low-poly heads: a front cone
  (`normal·front < -0.3`) for features, plus the SAME canvas Gaussian-blurred onto side-dominant
  faces BEHIND the face plane — a single cone either ghosts the face onto the head's side
  (grazing rays paint every surface in the column) or leaves the old mush at cheek edges, and a
  normal-only rim band eats the face's own 45° facets. Texture-only output: mesh untouched, every
  iteration re-bakes from the original texture, material swaps to the new PNG. **STYLE-TIER GATE
  (developer-caught): the re-rendered portrait comes back at a HIGHER detail tier than the unit —
  realistic shading and micro-detail project as GRAFTED SKIN on a flat-painted low-poly body,
  and the mismatch is frequency/shading, not hue (palette probes matched).** Before projecting:
  downscale the portrait to the unit's texel frequency (head ≈ 90 px) and re-upscale, then
  flatten mid-tones ~30% toward the unit's flat skin color (protect darks and lights so
  features/tusks survive). The source's OWN face at native res is not the alternative — a <30 px
  concept face upscaled projects as camo-blob mush; re-render then downscale is the only shape
  with both correct features AND correct tier — **BUT for UNIT-TIER subjects this whole portrait path is superseded (developer-
  mandated): source the repair from the race's own VENDOR PACK REF captures.** A generated
  portrait lands at hero/god detail tier and reads as grafted skin no matter how it's chunked;
  the pack ref IS the target style by definition. Pack-ref caveats: the ref render carries baked
  lighting (helmet shadow on the face) — lift to body albedo with per-channel GAIN measured in
  file space; pack faces are often minimal (nose shading + soft mouth) — extract just the
  missing feature lines via darkness mask in subject-skin ink (a full-skin patch paste reads as
  a goatee). Two refinements from the second day: (1)
  **FEATURES-ONLY MASK** — when the mesh face has correct skin and only missing DEFINITION
  (blank eyes), don't project skin at all: mask the canvas to the portrait's dark feature lines
  (lum threshold + MaxFilter dilate + small blur feather), clipped to the feature columns —
  original skin stays byte-identical, so there is no boundary and no color problem; (2)
  **COLOR-MATCH IN ONE SPACE** — flat renders dim/grey relative to the texture file, so never
  match file-space portrait colors to render-space mesh samples (overshoots grey); derive the
  correction as a differential between projected patch and adjacent original skin in the SAME
  render, apply additively, re-measure — converges in one iteration. (a mobile strategy game
  war-bat rider + Aelmira god: 9 cr each vs 30 cr rerolls; graft + wrong-color both caught by
  developer in-editor.)
- **TEXEL-STARVATION-FACE-SLOT — when a small region (rider/hooded face) stays soft through
  every texture repair AND through rerolls, the cause is ATLAS TEXEL ALLOCATION, and the fix is
  a dedicated material slot, not more bakes.** A rider face owns ~40 texels of a 2k atlas;
  nothing baked into those UVs can be crisp, and a regenerated roll inherits the same
  allocation (a face-crop reroll came back mush-faced — that's the proof to stop). Fix in the
  SAME one-session normalize script (never round-trip the exported FBX through Blender — import
  re-bases axes and flips facing): move the head's front-cone faces to a second material slot,
  rewrite their uv0 to the projector-frame ortho mapping, and drive them with a dedicated 1024²
  face texture (projector-frame render of the current look composited with the aligned repair
  canvas — the boundary matches the body by construction). Post: inpaint the render's
  background color out of the texture (grazing faces sample it) and import with wrap=Clamp
  (Repeat wraps out-of-frustum UVs into the border). Cost: one extra draw call. Diagnose-first
  rule: measure/suspect texel density BEFORE iterating projection constants — five bakes were
  burned on a region that could never hold the detail. (a mobile strategy game war-bat rider:
  crisp in-engine closeup after the slot; verified with editor-camera renders.)
- **Pick the generation provider by BILLING MODEL and capability, not habit — subscription-plan
  credits are sunk cost, pay-as-you-go credits are new cash.** When a project holds both a
  subscription provider (Meshy: monthly plan credits already paid) and a wallet provider (Tripo:
  prepaid, 1 credit = $0.01, Stripe recharge, credits never expire), route routine generation
  through the sunk-cost plan and spend cash only on capabilities the plan provider lacks. Tripo
  (V3 API, base `openapi.tripo3d.ai/v3` — V2 dies, never build on a deprecating API
  version when the retirement date is already announced) uniquely offers: free rig-check +
  auto-rig 25cr + animation retarget 10cr/anim; Import Model (run rig/texture/retopo on an
  EXISTING mesh, including another provider's output — cross-provider chains are legal); mesh/part
  completion (server-side hole repair — test on a copy, inspection gate still applies);
  deterministic `model_seed`/`texture_seed`; quad topology and `smart_low_poly`. All generation
  hygiene rules (one subject per input image, two-roll stop, IP gate, flat-background inspection)
  are provider-independent — apply them to every provider. Wallet balance is a precondition:
  check the balance endpoint BEFORE promising a task (a freshly-created key can sit at 0 credits
  with no free grant). (Wired a mobile strategy game: Tripo key added as second provider next to a
  Meshy subscription.)
- **CAVITY-MUSH-REGEN-NOT-PLUG — an open cavity full of AI interior mush that the camera
  looks into (open cockpit, basket, bay) is a DESIGN defect: seal it at the CONCEPT and re-roll
  the 3D; do not build occluder geometry over it.** (Replaces v58 CAVITY-OCCLUDER-TWO-TIER,
  which failed developer review the same day it was promoted.) Three escalating Blender plug
  attempts on one gondola all lost: (1) a rim-level plug poked through the hull — AI hulls are
  narrower than they look, and per-bin radius fits are polluted by wing roots/straps outside
  and shreds inside; (2) volume-bounded shred deletion silently DELETED THE PILOT — on
  island-soup the occupant's body is itself loose sheets, indistinguishable from mush by any
  size/volume test (the no-heuristics rule wins again); (3) even the contained version (dark
  cap + floor, occupant intact) still read as a black void at product bar. The working fix cost
  39 cr and one prompt: white-bg renders of the ACCEPTED model itself as i2i refs + "recreate
  this EXACT machine, UNMANNED, opening fully SEALED by a riveted dome hatch, no interior
  visible" → identity preserved 1:1, and a sealed volume gives reconstruction nothing to
  hallucinate inside — first-roll clean. Occluder plugs remain legitimate ONLY for holes
  glimpsed through small gaps (the backed-by-geometry case above), never as the visible floor
  of an open cavity. Two trailing traps: review the FULL 6-view set before claiming any mesh
  edit done (the poke-through was visible only in side views nobody re-checked), and when
  REPLACING a model in-place, re-point the importer material remap at the NEW model's own
  extracted texture — the old remap silently paints the old skin onto the new UVs as scrambled
  camo noise. (a mobile strategy game: dwarf war-balloon bay saga, plugs 0 cr wasted, regen 39 cr.)

- **GEOMETRY-VS-ALBEDO-FLATGREY — before "repairing" an apparent hole/dent/tear on an AI mesh,
  re-render it with a FLAT UNTEXTURED material and fit the primitive it should be. The defect is
  very often painted into the ALBEDO, and the geometry underneath is perfect.** Reconstruction
  bakes the concept image's dark regions (a shadowed leather flap, a recessed patch, a soot
  smear) straight into the basecolor; under the flat-ambient inspection lighting those texels
  read as an OPENING — jagged rim, visible "inner surface", the lot. Every mesh-side repair then
  fails for the same reason: there is nothing to fill. The 2-minute test that settles it: (1)
  swap every renderer to a plain grey lit material and re-render the offending view — if the
  silhouette and shading are clean, the mesh is fine; (2) confirm numerically by fitting the
  primitive the part should be (algebraic sphere fit over the shell verts: `A = [2p | 1]`,
  `solve A x = |p|²`, `r = sqrt(x₃ + |c|²)`) and reporting the radial deficit distribution — a
  real dent shows deficits of tens of percent, a clean shell stays inside a few. Do this BEFORE
  the boundary-loop census, before any fill-threshold sweep, before any plug. Corollary: an
  albedo-painted hole is a TEXTURE-tier fix (localized repair → §6f, or a 10 cr retexture that
  re-rolls the whole look and therefore needs the developer's call), never a geometry-tier one — and
  partial texel lifts are worse than the original defect, because a half-lifted flap reads as a
  bright rectangle. Also note the hole DETECTOR cannot see this class at all: a double-sided
  material paints the far inner surface, so no enclosed background pixels are ever produced and
  the count comes back clean while the model plainly looks torn. (a mobile strategy game: the
  dwarf war-balloon's "rip" survived a fill-threshold raise from 0.6 to 3.0 m — 1,025 of 1,033
  loops filled, no change — because the envelope was already a closed sphere at 5% max deficit;
  three cosmetic passes were spent before anyone rendered it grey.)

- **GEN-ANIM-IS-LIBRARY-ONLY — no generative-3D service GENERATES animation; they retarget a fixed
  mocap library keyed by an action id, so "it moves like a human" is a CURATION defect you fix for
  3 credits, not a platform limit you accept.** Verify capability against the LIVE API, never docs
  or assumption: Meshy's `POST /openapi/v1/animations` rejects `prompt` / `text_prompt` /
  `video_url` with *"ActionID is a required field"*, `/openapi/v1/text-to-animation` returns
  NoMatchingRoute, and there is no action-list endpoint (the ~400-entry list is a docs PAGE —
  scrape it, cache it, and pick from it). Tripo is the same shape. The failure this rule prevents:
  buying the DEFAULT-looking ids (`0 Idle`, `4 Attack`, `97 Left_Slash`, `183
  Shot_and_Fall_Backward`) for a fantasy monster — that death is a **gunshot** fall and that idle
  is a neutral human stand, which is exactly what "stupid human animations" means when a developer
  rejects a set. Every library has beast/heavy/rage entries hiding in it (Meshy: `112
  Monster_Walk`, `119 Slow_Orc_Walk`, `113 Mummy_Stagger`, `386 Zombie_Scream`, `88
  Chest_Pound_Taunt`, `255-257 Angry_Ground_Stomp`, `99 Reaping_Swing`, `242 Charged_Slash`, `237
  Charged_Axe_Chop`, `127 Charged_Ground_Slam`, `335 Axe_Breathe_and_Look_Around` = a
  weapon-in-hand idle). **Read every candidate action's NAME against the unit's fiction before
  buying** — no firearm/reload/phone/dance ids on a fantasy unit, no neutral `Idle` where a
  combat-stance or breathing-with-weapon idle exists. Structural point that closes the argument:
  an auto-rigged character IS a Mixamo-spec biped, so every source (library, video mocap,
  text-to-motion) yields humanoid motion — "creature-like" is a PERFORMANCE STYLE (hunch, wide
  stance, weight, aggression), selectable at buy time. If a truly bespoke performance is ever
  required, the only real option is video-to-mocap (DeepMotion/Move/Rokoko) retargeted onto the
  same rig — not hand-keying. (a mobile strategy game: a demon brute's rejected set became a
  hunched orc-walk + weapon idle + reaping swing + backward death for 24 credits.)

- **WEAPON-FUSED-REGENERATE-NEVER-CUT — when an image-to-3D character was generated holding a
  weapon, the weapon is REMESHED INTO the body and no Blender surgery recovers it; regenerate the
  body weapon-free and the weapon as its own asset.** The remesher welds blade to hip/thigh/tabard
  and shatters it into fragments that share vertices with the body, so connectivity-island capture
  cannot describe the split: taking the islands leaves a blade SLAB fused across the hip while
  removing the closed fist (a fist is its OWN shell — it leaves with the weapon and reads as "his
  hand is all broken"), and hole-patching the body afterwards webs FBX UV-seam loops into
  membranes that stretch open mid-stride. Three rig+clip rolls were burned proving this. Correct
  flow: (1) image-to-image the SAME character from its approved views with *"he holds NOTHING,
  both hands are empty relaxed open hands"* → the identity holds, the hands come out clean; (2) a
  second generation of the weapon ALONE from a weapon-only asset sheet in the same style; (3) rig
  the clean body, socket the weapon to the hand bone with its origin at the grip. Also: a clean
  single-character or single-prop generation needs NO floater cull — keep-largest-island deletes
  separate armor shells (chest plate) and bbox-overlap deletes boot shells that gap past the pad;
  cull only what a render actually shows as debris. (a mobile strategy game demon melee: 51 credits
  of surgery discarded, 134 to regenerate correctly.)

- **VFX-SOLID-FORM-GEN — generative meshes are solid cores for VFX stacks, not whole effects.**
  Projectiles / spell props that need a physical silhouette (crystal bolt, spore pod, rune disc)
  are valid single-subject gens (same multi-object + provenance rules), but ship them as **one
  layer under stacked additive energy materials** (AllIn1 or project equivalent). A bare
  textured mesh at gameplay camera distance fails the magic look bar. Low poly (≈1k–4k for
  fliers); pivot at flight center. Full path table: the hybrid section of the [VFX with AllIn1](/workflows/vfx-allin1/) page.
  (a mobile strategy game — developer required Meshy in the VFX pipeline alongside AllIn1 / Polygon.)

- **STYLE-ANCHOR-LEAKS-ITS-SILHOUETTE — an image passed only as a "style" anchor also donates its
  shape.** An icon set repainted with another item's icon as the style reference came back with a plant
  fibre twisted into the bone anchor's exact outline and pose (outline overlap 0.68); the item still pointed
  at its own file, so no reference or GUID check could catch it. Anchor a batch on an item whose silhouette
  differs from every target, never on a sibling in the set, and gate each roll on outline overlap with its
  anchor (reject above ~0.65) plus an identical-pixels/shared-file uniqueness test across the whole set.

## Free multi-view identity (from a mobile strategy game)

- Free/Imagine multi-view: view_0 only new identity; side/back = image-edit of view_0. Never three independent gens as turnaround.
- Concept bodies: T/A-pose, empty fists for weapon classes, no weapons on body (R11a). >=3 views including air.


## IMAGE-GEN-CLI-AND-API-TRAPS

- Codex CLI image runs could not read attachments: its sandbox denies the worktree, user temp and Python
  `mkdtemp` folders, and the elevated sandbox fails its ACLs. Stage inputs in a public folder
  (`<user-home>`), run unelevated and close stdin (`codex exec` otherwise waits forever).
- A prompt saying "using the attached images" with nothing attached failed 6 of 8 runs ("requested the last N
  conversation images, but only 0 were available"). Unanchored runs use text-only wording.
- Grok CLI: `grok models` reports "not authenticated" while `-p` works, and images come from its agent tools,
  not flags. Test a route with one real still, never `--help`.
- Name the transparent-background option on every icon request; rolls came back opaque without it.
- Generated fictional brands can match real companies; check every generated mark before modelling it.
- A reference image in a sparse worktree can be an LFS pointer ("unsupported image image/jpeg"): `git lfs pull`.
- Meshy: task asset URLs expire after 3 days, so download on success. `DELETE` on an `IN_PROGRESS` task returns
  409 and it keeps spending; cancel only while `PENDING`. API Remesh/Rigging/Animate cost 5/5/3 credits where
  the web app shows 0. `remove_lighting` is honoured by `meshy-6` only. 4k geometry failed on thin slabs
  ("repair did not close the mesh", refunded, cause unknown) where 2k worked. Smart Topology output comes in
  separate parts, so it breaks a one-closed-piece rule.
- A Tripo multiview from a straight front/back pair returned a 0.47 m column: two opposite views carry no
  depth. Mirror an approved front, or use a multi-image route that includes a side view.
- Patching an image by sampling at a fixed offset lands on the wrong material where a band narrows in
  perspective; sample by relative position across the band.
