Import: FBX node hierarchy + dynamic scene/mesh limits (completes #133)

Current State

#133 (closed) ("Import: FBX mesh & scene hierarchy") is closed, but its acceptance criterion — "…with all submeshes, materials, and hierarchy intact" — was only partially met. FBX import currently produces:

  • the whole-file flattened .model mesh, and
  • the 132 material:{d} sub-assets,

but no node hierarchy: editor/assets/ModelHierarchy.zig is glTF-only, so FBX emits no mesh:{d} sub-assets and no hierarchy prefab. There is no per-node structure — the whole city is one 8.5 M-vertex draw target. (This is tolerable once #140 (closed) lands, but blocks per-object culling, movable props, and clean interior scenes.)

Goal

Bring FBX to parity with the glTF hierarchy import (#8 (closed)): a mesh:{d} sub-asset per FBX mesh + a hierarchy prefab tree with a mesh_renderer per mesh-bearing node, reusing the existing prefab-instantiate path.

Scope

  • Mirror #8 (closed)'s ModelHierarchy.zig path for FBX: walk ufbx nodes, decompose transforms, topo-sort parents-before-children, build SceneNodes, generate mesh:{d} + hierarchy sub-assets (fbx_wrap.c needs a hierarchy walk alongside the existing fbx_wrap_load_all).
  • Lift the fixed live-scene limits — Bistro exterior is thousands of FBX nodes:
    • engine/scene/SceneNode.zig:6 MAX_OBJECTS = 128 → dynamic / vastly larger.
    • subsystems/render/state.zig:104 MAX_MESHES = 64 → dynamic / larger. These are pervasive fixed caps; making the scene-object and GPU-mesh stores growable is the bulk of this work.
  • Instance de-duplication: many FBX nodes reference the same geometry — the mesh:{d} cook and GPU mesh cache must dedupe so memory doesn't explode.
  • Skin/joint handling remains out of scope (a known gap from #8 (closed)).

Acceptance

  • A multi-thousand-node FBX (Bistro) instantiates as a prefab hierarchy with correct per-node transforms, without hitting MAX_OBJECTS/MAX_MESHES.
  • Instanced geometry is uploaded once, not per node.
Edited by Bruno Massa