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

"My model imported upside down / sideways / facing the wrong way"

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

Your mesh loads, but:

  • The "front" of the model faces the wrong cardinal direction in-world.
  • Roofs, ramps, or angled slabs tip the wrong way (valley instead of peak — upside-down geometry).
  • Facing looks fine in Blender and wrong after the .vmdl compiles.
▸ CAUSE

Three separate axis traps stack:

  1. OBJ compile remaps Y-up → Z-up. Export with forward_axis='NEGATIVE_Z', up_axis='Y' and keep import_rotation [0,0,0] in the vmdl. The compiled world frame is:

    world = (-objZ, -objX, objY) * scale

  2. Facing convention. Geometry built along Blender +X faces world −Y after import. Yaw +90° turns it to face world +X.

  3. Blender rotation sign. Rotating a slab about +Y by a positive angle tips its +X edge down. Guessing the sign ships every roof upside-down (valley instead of peak).

Colliders live in the model's own frame and rotate with the GameObject — author collider specs in the yaw-0 frame, never pre-rotate them to "fix" facing.

▸ FIX

Export (Blender):

snippet
bpy.ops.wm.obj_export(
    filepath=path,
    export_materials=True,
    forward_axis='NEGATIVE_Z',
    up_axis='Y',
)

vmdl: leave mesh import rotation at zero:

snippet
import_rotation = [ 0, 0, 0 ]

Facing at placement — codify once, reuse everywhere:

snippet
// Blender +X → world −Y; +90° yaw → face world +X
const float FacingYawOffset = 90f;

float yaw = MathF.Atan2(dir.y, dir.x) * (180f / MathF.PI) + FacingYawOffset;
go.WorldRotation = Rotation.FromYaw(yaw);

Angled geometry: before batching roofs/ramps, place one instance and verify the peak/valley visually. If it's inverted, negate the Blender rotation angle — do not "fix" it with import_rotation.

AI-generated meshes often face the opposite way from the Blender-pipeline convention — use atan2(...) − 90 instead of + 90. Verify one placed instance per source pipeline. See ai-generated-models.

▸ WHY IT WORKS

s&box's OBJ importer owns the Y-up → Z-up conversion at compile time. Fighting that with import_rotation double-applies a frame change and breaks the facing constants you already tuned. Keeping export axes + zero import rotation means one predictable map (world = (−objZ, −objX, objY)), so a single yaw offset corrects "front" for every model from that pipeline. The roof-valley bug is pure Blender local-axis semantics — positive +Y rotation lowers the +X edge — so the fix is authoring discipline, not an engine remapping.

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