modelImport
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
bytesbuffer | string— Raw model file bytes (fromvfs.readAsync).formatstring— 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.mainNamestring— Basename of the model file to import (one offiles' 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
sourcePathstring— VFS path to the source model file.clipNamestring— Clip name as returned byresult(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
handlePromiseHandle | string— Promise handle fromextractAnimation.
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
handlePromiseHandle | string— Promise handle fromdecompose.
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() -> anyretriesnumber(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
meshBytesbuffer | 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
skeletonAnimationSkeleton—{ names, parents, locals }(adecomposeresult'sskeleton).
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
bytesbuffer | string— Raw model file bytes (fromvfs.readAsync).formatstring— 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.mainNamestring— Basename of the model file to import (one offiles' 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
sourcePathstring— VFS path to the source model file.clipNamestring— Clip name as returned byresult(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
handlePromiseHandle | string— Promise handle fromextractAnimation.
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
handleany(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
handlePromiseHandle | string— Promise handle fromdecompose.
modules/modelImport/retryHandle
retryHandle(makeHandle: () -> any, retries: number?, yield: (() -> ())?): (PromiseHandle | string)?
Parameters
makeHandle() -> anyretriesnumber?(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
meshBytesbuffer | 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
skeletonAnimationSkeleton—{ names, parents, locals }(adecomposeresult'sskeleton).
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
bytesbuffer | string— Raw model file bytes (fromvfs.readAsync).formatstring— 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.mainNamestring— Basename of the model file to import (one offiles' 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
sourcePathstring— VFS path to the source model file.clipNamestring— Clip name as returned byresult(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
handlePromiseHandle | string— Promise handle fromextractAnimation.
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
handlePromiseHandle | string— Promise handle fromdecompose.
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() -> anyretriesnumber(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
meshBytesbuffer | 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
skeletonAnimationSkeleton—{ names, parents, locals }(adecomposeresult'sskeleton).
Returns string? — .rig JSON document, or nil.
local rigJson = modelImport.rigFromSkeleton(data.skeleton)