the symptom, in your words

"s&box won't load my OBJ/GLB"

✓ verified on 26.07.08e
lane Getting art inposted

▸ SYMPTOM

  • Dropping an .obj / .glb into Assets/ does nothing useful in a scene.
  • Model.Load("models/foo.obj") fails or never resolves as a usable model.
  • The .vmdl compiles but materials error with something like: unable to resolve X.vmat.
  • Model shows untextured / ERROR after a scripted re-export.

▸ CAUSE

s&box does not load raw OBJ/GLB in scenes. The engine compiles a .vmdl wrapper on load. The mesh file is only a RenderMeshFile (and optionally physics) input referenced by that wrapper.

Material remaps must map both names ("MatName" and "MatName.vmat") in the DefaultMaterialGroup remaps, or the compiler errors with unable to resolve X.vmat. A MaterialOverride on the renderer is not a substitute.

Related trap: Blender's OBJ exporter with export_materials=False silently drops the usemtl line too (not just the .mtl file). Remaps key off that material-group name, so the mesh compiles untextured.

▸ FIX

  1. Put the mesh under Assets/models/<your-project>/foo.obj (and .mtl if needed).
  2. Emit a sibling foo.vmdl that references it.

Minimal wrapper shape (KV3 text, copy structure from any working hand-authored vmdl in your install):

snippet
<!-- kv3 encoding:text:version{...} format:modeldoc29:version{...} -->
{
  rootNode =
  {
    _class = "RootNode"
    children =
    [
      {
        _class = "MaterialGroupList"
        children =
        [
          {
            _class = "DefaultMaterialGroup"
            remaps =
            [
              { from = "MatName"      to = "materials/your/mat.vmat" },
              { from = "MatName.vmat" to = "materials/your/mat.vmat" },
            ]
          },
        ]
      },
      {
        _class = "RenderMeshList"
        children =
        [
          {
            _class = "RenderMeshFile"
            filename = "models/your-project/foo.obj"
            import_scale = 39.37
            import_rotation = [ 0, 0, 0 ]
            // translation origin as needed
          },
        ]
      },
    ]
  }
}

Exact header GUIDs: copy line 1 from a known-good hand-authored .vmdl (citizen addon or your own pipeline output). Do not invent format versions. See also the modeldoc32 trap in ai-generated-models.

After any scripted OBJ export, grep the output for usemtl and vt . If usemtl is missing, re-inject usemtl <name> before the first f line rather than re-enabling material export (which can write a stray .mtl into a delivery folder you don't want).

Load in code with a root-relative vmdl path, forward slashes, no Assets/ prefix:

snippet
var model = Model.Load("models/your-project/foo.vmdl");

Kick the editor if a new vmdl doesn't show (hot-compile on focus).

▸ WHY IT WORKS

ModelDoc is the compile unit. OBJ/GLB are mesh sources; without a vmdl there is nothing for the resource system to compile into a Model. Remap keys must match whatever string the mesh importer emits for the material slot (some paths emit the bare name, some append .vmat), so mapping both closes the gap. MaterialOverride only swaps at runtime on an already-resolved material group; it cannot fix a failed compile-time remap.

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

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord