Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

Model Importer

Claims a mesh-bearing 3D model file in any assimp-supported format (.fbx, .obj, .dae, .gltf, .glb, .stl, .ply, .3ds, .blend, .x, and more) and decomposes it into a single <name>.bundle/ directory. The source file is moved into the bundle (<name>.bundle/<name>.<ext>) so the link from the derived engine-native assets back to their origin is never lost, and a settings change can re-import from it. The keepSource setting decides whether it stays there once the import is done.

The real source extension flows through to assimp as the format hint, so every supported format parses correctly — the importer is backed by assimp for all 3D formats.

Output layout

<name>.bundle/
  <name>.<ext>                     the original source (moved in; kept while keepSource)
  entity_template                  JSON list of template-entities
  mesh/<name>.mesh                 engine-native mesh bytes
  material/<name>.material/mat.yaml  one per material something binds
  texture/<name>.texture/          one per distinct image: data.ztex, and
                                   source.<ext> in the format the model holds it
  animation/<name>.anim            AnimAsset-format animation clips

How it fits the importer system

This importer implements only canImport(path, bytes) and import(ctx). Everything that makes importing correct in a multiplayer, content-addressed engine lives once in the importer type's shared module (importer.assetType/shared.module):

  • Origin gate — runs on the originating client only; peer-synced (remote) writes never re-trigger.
  • Content gate — re-runs only when the source is new (no .meta), its checksum changed, or the importer's own settings changed.
  • Containment skip — once the source lives at <name>.bundle/<name>.<ext>, asset.containing resolves it to its own bundle and the write is skipped.
  • Re-entrancy guard — the derived writes (and the source move) don't re-enter dispatch.

ctx.relocateIntoBundle(path) is the shared helper that performs the move-into-bundle; this importer calls it, then derives mesh/material/ texture/animation assets and the entity_template alongside the moved source.

Settings

keepSource (default true) keeps the imported model file inside the bundle. Off, the file is removed once everything the bundle renders from has been derived from it: the bundle is smaller, and a later settings change has no original to re-import from. A generation run (the services toolbox) imports with it off. One import can name it without changing the importer's settings: asset.import(path, { importerSettings = { model = { keepSource = false } } }).

The importer's own settings.luau also declares textureSettings — the texture type's settings, embedded rather than restated. Every texture the importer extracts from a model is created with it as its importer layer, so a world can import every model's textures crisp, or block-compressed, by setting it once. A texture the model's own materials bind is created as the type its slot names — a normal slot as normalMap, a metallic-roughness or occlusion slot as mask, an albedo / base-colour or emissive slot as color — under whatever textureSettings names, so its colour space follows what the image is for rather than what its asset ends up called.

A mesh-free model (animation-only) is ignored here — the model_animation importer claims it.