Log inGet started

modelImport

Updated October 16, 2026

The model-import pipeline: decompose any assimp-supported 3D model (fbx, obj, dae, gltf, glb, stl, ply, 3ds, …) into meshes, materials, textures, animation clips and a node graph; extract a single clip to its .zanim payload; and derive a .rig from a skinned mesh or from an animation-only file's driven skeleton.

require("@builtin::modules.api.engine.modelImport") -- modelImport (also available as global 'modelImport')

The modelImport namespace: 25 functions.

globals/modelImport/decompose

modelImport.decompose(bytes: buffer | string, format: string) -> PromiseHandle

Parse raw model bytes on a background thread. format is the real source extension ("fbx", "obj", "dae", "gltf", "glb", "stl", "ply", "3ds", …), forwarded to assimp as the format hint. Returns a promise handle: task.await it, then read the data with result(handle).

Parameters

  • bytes buffer | string — Raw model file bytes (from vfs.readAsync).
  • format string — The source file extension (lowercase, no dot).

Returns PromiseHandle — Promise handle for task.await.

local h = modelImport.decompose(bytes, "obj"); task.await(h)

globals/modelImport/decomposeFiles

modelImport.decomposeFiles(files: { ModelFile }, mainName: string) -> PromiseHandle

Parse a model plus its companion files on a background thread, so assimp resolves the model's external references (a .gltf's external .bin and image files, an .obj's .mtl colors/textures, MD5's .md5anim, …). files is an array of { name = basename, bytes = <bytes> } that MUST include the model file itself; mainName is that file's basename. Returns a promise handle: task.await it, then read the data with result(handle) — the same shape decompose produces.

Parameters

  • files { ModelFile } — Array of { name, bytes }: the model file plus its companions.
  • mainName string — Basename of the model file to import (one of files' names).

Returns PromiseHandle — Promise handle for task.await.

local h = modelImport.decomposeFiles(files, "CesiumMilkTruck.gltf"); task.await(h)

globals/modelImport/extractAnimation

modelImport.extractAnimation(sourcePath: string, clipName: string) -> PromiseHandle

Read a model source file and extract one animation clip to its .zanim payload, stashed for retrieval. The source extension decides the parser, so this is format-agnostic. Returns a promise handle: task.await it, then extractAnimationResult(handle) returns the bytes.

Parameters

  • sourcePath string — VFS path to the source model file.
  • clipName string — Clip name as returned by result(handle).animations[i].name.

Returns PromiseHandle — Promise handle for task.await.

globals/modelImport/extractAnimationResult

modelImport.extractAnimationResult(handle: PromiseHandle | string) -> string?

After awaiting an extractAnimation handle, return the extracted .zanim bytes (binary-safe), consuming them. The bytes to hand to asset.create("animation", name, { bytes }). Returns nil on failure or if already taken.

Parameters

  • handle PromiseHandle | string — Promise handle from extractAnimation.

Returns string? — The clip's .zanim payload bytes, or nil.

globals/modelImport/result

modelImport.result(handle: PromiseHandle | string) -> any?

Read the decomposed model after decompose's handle has been awaited. Every format decomposes into the same shape, so this is format-agnostic. Runs on the main thread; consumes the stored result.

Parameters

  • handle PromiseHandle | string — Promise handle from decompose.

Returns any? — { nodes, meshes, materials, textures, animations, hasSkeleton, skeletonRootNode?, skeleton? }, or nil.

globals/modelImport/retryHandle

modelImport.retryHandle(makeHandle: () -> any, retries: number?, yield: (() -> ())?) -> (PromiseHandle | string)?

Parameters

  • makeHandle () -> any
  • retries number (optional)
  • yield (() -> ()) (optional)

Returns (PromiseHandle | string)?

globals/modelImport/rigFromMeshSkin

modelImport.rigFromMeshSkin(meshBytes: buffer | string) -> string?

Lift the skeleton out of a skinned .mesh (ZMSH) payload and return it as a .rig JSON document: bones (hierarchy, rest pose, inverse-bind). The source rig a skinned mesh's clips retarget through. Returns nil when the bytes are not a mesh or carry no skin.

Parameters

  • meshBytes buffer | string — Raw ZMSH mesh bytes carrying a skin.

Returns string? — .rig JSON document, or nil.

globals/modelImport/rigFromSkeleton

modelImport.rigFromSkeleton(skeleton: AnimationSkeleton) -> string?

Build a .rig JSON document from the skeleton an animation-only file was authored on — result(handle).skeleton, the bones its clips drive with their local rest transforms. Forward kinematics over the locals resolves globals + inverse-bind. The source rig a standalone clip retargets through. Returns nil on malformed input.

Parameters

  • skeleton AnimationSkeleton — { names, parents, locals } (a decompose result's skeleton).

Returns string? — .rig JSON document, or nil.

local rigJson = modelImport.rigFromSkeleton(data.skeleton)

modules/modelImport/decompose

decompose(bytes: buffer | string, format: string): PromiseHandle

Parse raw model bytes on a background thread. format is the real source extension ("fbx", "obj", "dae", "gltf", "glb", "stl", "ply", "3ds", …), forwarded to assimp as the format hint. Returns a promise handle: task.await it, then read the data with result(handle).

Parameters

  • bytes buffer | string — Raw model file bytes (from vfs.readAsync).
  • format string — The source file extension (lowercase, no dot).
local h = modelImport.decompose(bytes, "obj"); task.await(h)

modules/modelImport/decomposeFiles

decomposeFiles(files: { ModelFile }, mainName: string): PromiseHandle

Parse a model plus its companion files on a background thread, so assimp resolves the model's external references (a .gltf's external .bin and image files, an .obj's .mtl colors/textures, MD5's .md5anim, …). files is an array of { name = basename, bytes = <bytes> } that MUST include the model file itself; mainName is that file's basename. Returns a promise handle: task.await it, then read the data with result(handle) — the same shape decompose produces.

Parameters

  • files { ModelFile } — Array of { name, bytes }: the model file plus its companions.
  • mainName string — Basename of the model file to import (one of files' names).
local h = modelImport.decomposeFiles(files, "CesiumMilkTruck.gltf"); task.await(h)

modules/modelImport/extractAnimation

extractAnimation(sourcePath: string, clipName: string): PromiseHandle

Read a model source file and extract one animation clip to its .zanim payload, stashed for retrieval. The source extension decides the parser, so this is format-agnostic. Returns a promise handle: task.await it, then extractAnimationResult(handle) returns the bytes.

Parameters

  • sourcePath string — VFS path to the source model file.
  • clipName string — Clip name as returned by result(handle).animations[i].name.

modules/modelImport/extractAnimationResult

extractAnimationResult(handle: PromiseHandle | string): string?

After awaiting an extractAnimation handle, return the extracted .zanim bytes (binary-safe), consuming them. The bytes to hand to asset.create("animation", name, { bytes }). Returns nil on failure or if already taken.

Parameters

  • handle PromiseHandle | string — Promise handle from extractAnimation.

modules/modelImport/isHandle

isHandle(handle: any): boolean

Call makeHandle, which returns a promise handle (or its id string), or a falsy value on a transient failure (e.g. a source read that raced a pending write during a parallel import), up to retries + 1 times, yielding via yield between attempts so a pending write can land before the next try. Returns the handle once one is produced, or nil when every attempt failed. Callers task.await the result only when it is non-nil, so a transient miss never reaches task.await as something other than a handle.

Parameters

  • handle any (optional)

modules/modelImport/result

result(handle: PromiseHandle | string): any?

Read the decomposed model after decompose's handle has been awaited. Every format decomposes into the same shape, so this is format-agnostic. Runs on the main thread; consumes the stored result.

Parameters

  • handle PromiseHandle | string — Promise handle from decompose.

modules/modelImport/retryHandle

retryHandle(makeHandle: () -> any, retries: number?, yield: (() -> ())?): (PromiseHandle | string)?

Parameters

  • makeHandle () -> any
  • retries number? (optional)
  • yield (() -> ())? (optional)

modules/modelImport/rigFromMeshSkin

rigFromMeshSkin(meshBytes: buffer | string): string?

Lift the skeleton out of a skinned .mesh (ZMSH) payload and return it as a .rig JSON document: bones (hierarchy, rest pose, inverse-bind). The source rig a skinned mesh's clips retarget through. Returns nil when the bytes are not a mesh or carry no skin.

Parameters

  • meshBytes buffer | string — Raw ZMSH mesh bytes carrying a skin.

modules/modelImport/rigFromSkeleton

rigFromSkeleton(skeleton: AnimationSkeleton): string?

Build a .rig JSON document from the skeleton an animation-only file was authored on — result(handle).skeleton, the bones its clips drive with their local rest transforms. Forward kinematics over the locals resolves globals + inverse-bind. The source rig a standalone clip retargets through. Returns nil on malformed input.

Parameters

  • skeleton AnimationSkeleton — { names, parents, locals } (a decompose result's skeleton).
local rigJson = modelImport.rigFromSkeleton(data.skeleton)

typed/builtin//modules/api/engine/modelImport/modelImport/decompose

modelImport.decompose(bytes: buffer | string, format: string) -> PromiseHandle

Parse raw model bytes on a background thread. format is the real source extension ("fbx", "obj", "dae", "gltf", "glb", "stl", "ply", "3ds", …), forwarded to assimp as the format hint. Returns a promise handle: task.await it, then read the data with result(handle).

Parameters

  • bytes buffer | string — Raw model file bytes (from vfs.readAsync).
  • format string — The source file extension (lowercase, no dot).

Returns PromiseHandle — Promise handle for task.await.

local h = modelImport.decompose(bytes, "obj"); task.await(h)

typed/builtin//modules/api/engine/modelImport/modelImport/decomposeFiles

modelImport.decomposeFiles(files: { ModelFile }, mainName: string) -> PromiseHandle

Parse a model plus its companion files on a background thread, so assimp resolves the model's external references (a .gltf's external .bin and image files, an .obj's .mtl colors/textures, MD5's .md5anim, …). files is an array of { name = basename, bytes = <bytes> } that MUST include the model file itself; mainName is that file's basename. Returns a promise handle: task.await it, then read the data with result(handle) — the same shape decompose produces.

Parameters

  • files { ModelFile } — Array of { name, bytes }: the model file plus its companions.
  • mainName string — Basename of the model file to import (one of files' names).

Returns PromiseHandle — Promise handle for task.await.

local h = modelImport.decomposeFiles(files, "CesiumMilkTruck.gltf"); task.await(h)

typed/builtin//modules/api/engine/modelImport/modelImport/extractAnimation

modelImport.extractAnimation(sourcePath: string, clipName: string) -> PromiseHandle

Read a model source file and extract one animation clip to its .zanim payload, stashed for retrieval. The source extension decides the parser, so this is format-agnostic. Returns a promise handle: task.await it, then extractAnimationResult(handle) returns the bytes.

Parameters

  • sourcePath string — VFS path to the source model file.
  • clipName string — Clip name as returned by result(handle).animations[i].name.

Returns PromiseHandle — Promise handle for task.await.

typed/builtin//modules/api/engine/modelImport/modelImport/extractAnimationResult

modelImport.extractAnimationResult(handle: PromiseHandle | string) -> string?

After awaiting an extractAnimation handle, return the extracted .zanim bytes (binary-safe), consuming them. The bytes to hand to asset.create("animation", name, { bytes }). Returns nil on failure or if already taken.

Parameters

  • handle PromiseHandle | string — Promise handle from extractAnimation.

Returns string? — The clip's .zanim payload bytes, or nil.

typed/builtin//modules/api/engine/modelImport/modelImport/result

modelImport.result(handle: PromiseHandle | string) -> any?

Read the decomposed model after decompose's handle has been awaited. Every format decomposes into the same shape, so this is format-agnostic. Runs on the main thread; consumes the stored result.

Parameters

  • handle PromiseHandle | string — Promise handle from decompose.

Returns any? — { nodes, meshes, materials, textures, animations, hasSkeleton, skeletonRootNode?, skeleton? }, or nil.

typed/builtin//modules/api/engine/modelImport/modelImport/retryHandle

modelImport.retryHandle(makeHandle: () -> any, retries: number?, yield: (() -> ())?) -> (PromiseHandle | string)?

Parameters

  • makeHandle () -> any
  • retries number (optional)
  • yield (() -> ()) (optional)

Returns (PromiseHandle | string)?

typed/builtin//modules/api/engine/modelImport/modelImport/rigFromMeshSkin

modelImport.rigFromMeshSkin(meshBytes: buffer | string) -> string?

Lift the skeleton out of a skinned .mesh (ZMSH) payload and return it as a .rig JSON document: bones (hierarchy, rest pose, inverse-bind). The source rig a skinned mesh's clips retarget through. Returns nil when the bytes are not a mesh or carry no skin.

Parameters

  • meshBytes buffer | string — Raw ZMSH mesh bytes carrying a skin.

Returns string? — .rig JSON document, or nil.

typed/builtin//modules/api/engine/modelImport/modelImport/rigFromSkeleton

modelImport.rigFromSkeleton(skeleton: AnimationSkeleton) -> string?

Build a .rig JSON document from the skeleton an animation-only file was authored on — result(handle).skeleton, the bones its clips drive with their local rest transforms. Forward kinematics over the locals resolves globals + inverse-bind. The source rig a standalone clip retargets through. Returns nil on malformed input.

Parameters

  • skeleton AnimationSkeleton — { names, parents, locals } (a decompose result's skeleton).

Returns string? — .rig JSON document, or nil.

local rigJson = modelImport.rigFromSkeleton(data.skeleton)
  • api
  • reference