s&box/field-guide
the symptom, in your words

"Using AI-generated 3D models in a real s&box game"

✓ verified on engine 26.07lane: Getting art inposted
▸ SYMPTOM

You pull an AI-generated (Tripo / Meshy / Rodin-style) asset into Assets/ and get one or more of:

  • Floating ERROR mesh text in-editor.
  • Console: No valid format conversion from 'modeldoc32' to 'modeldoc30'.
  • Material compile FAIL / Unable to read file .../assets/<slug>_color.png.
  • Model loads but is knee-high (everything normalized to ~1 m).
  • Players walk through it (no collider shipped).
▸ CAUSE

Texture-native generators (Tripo, Meshy, Rodin, and similar) can emit mesh + albedo together, wrapped as OBJ + .vmdl + .vmat. That path is real and shippable — but several delivery bugs break the happy path:

  1. format:modeldoc32 header — current engine can't convert to modeldoc30.
  2. Meshes arrive normalized to ~1 m max dimension — no real-world scale.
  3. Corrupt white.png — bad IDAT checksum → texture compile FAIL.
  4. Bare <slug>_color.png in the vmat — material compiler resolves against the Assets root, not the vmat's folder.
  5. No ModelCollider / physics — render-only delivery.
▸ FIX

What you should get (happy path)

snippet
Assets/
  models/ai/<slug>.obj
  models/ai/<slug>.vmdl
  materials/ai/<slug>_mat.vmat
  materials/ai/<slug>_color.png

Reference root-relative, no Assets/ prefix:

snippet
var model = Model.Load("models/ai/<slug>.vmdl");

Conventions when healthy: import_scale 39.37, import_rotation [0,0,0], material remaps for both "name" and "name.vmat", grounded at z=0, triangulated. Geometry face budget often ~8–12k tris.

Bug fixes (per asset until your generator's output is fixed)

1. modeldoc32 header — replace line 1 of the vmdl with the modeldoc29 header from any working hand-authored vmdl (copy line 1 verbatim). Node classes inside are compatible as-is. Error string:

snippet
No valid format conversion from 'modeldoc32' to 'modeldoc30'

2. Scale — until intended sizes are baked:

snippet
float M = 39.37f;
go.WorldScale = desiredHeightMeters * M / model.Bounds.Size.z;

Prefer baking into a wrapper import_scale at unit WorldScale when you need ModelCollider to match — see scaled-collider-didnt-scale. Remember "normalized ~1 m" ≠ exactly 1 m.

3. Corrupt white.png — re-encode or replace with any clean white PNG.

4. Bare texture path — edit the vmat TextureColor to the full root-relative path:

snippet
materials/ai/<slug>_color.png

5. Colliders — add a bounds-derived BoxCollider (standard practice). Do not assume ModelCollider works on render-only vmdls (model-no-collision).

Facing

Verified: AI-generated meshes often face the opposite way from the Blender-pipeline convention — use facing yaw = atan2(dir.y, dir.x)° − 90 (not + 90). Verify one instance.

Characters

Delivered as static meshes (no skeleton). Rig from a copy outside the delivery folder — re-runs overwrite in place. See rigging-ai-generated-mesh.

Hygiene

Don't hand-edit files under a regenerating delivery tree long-term — fork/copy out. Each generation costs real money; batch thoughtfully. (needs verification): exact generator CLI / token layout varies by your tooling — keep secrets out of public repos.

▸ WHY IT WORKS

The engine path is the same as hand-authored art: ModelDoc compiles OBJ + materials into a Model. The AI farm just has to emit compatible headers, root-relative texture paths, and honest scale. Each listed bug is a packaging mismatch (format version, path resolution, missing physics graph), not a fundamental "AI meshes can't work in s&box" limitation.

Verified on engine 26.07 — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?