modelImport
The modelImport namespace — 25 functions.
globals/modelImport/decompose
modelImport.decompose(bytes: buffer | string, format: string) -> string
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 string — Promise handle for task.await.
local h = modelImport.decompose(bytes, "obj"); task.await(h)
globals/modelImport/decomposeFiles
modelImport.decomposeFiles(files: { ModelFile }, mainName: string) -> string
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 string — Promise handle for task.await.
local h = modelImport.decomposeFiles(files, "CesiumMilkTruck.gltf"); task.await(h)
globals/modelImport/extractAnimation
modelImport.extractAnimation(sourcePath: string, clipName: string) -> string
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 string — Promise handle for task.await.
globals/modelImport/extractAnimationResult
modelImport.extractAnimationResult(handle: 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
handlestring— Promise handle fromextractAnimation.
Returns string? — The clip's .zanim payload bytes, or nil.
globals/modelImport/result
modelImport.result(handle: 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
handlestring— 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: (() -> ())?) -> string?
Call makeHandle — which returns a promise-handle 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 string 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 a non-string.
Parameters
makeHandle() -> any— Returns a promise-handle string, or a falsy value on failure.retriesnumber(optional) — Extra attempts after the first (default 3).yield(() -> ())(optional) — Called between attempts (defaulttask.wait).
Returns string? — The handle string, or nil when every attempt failed.
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
auto-derived humanoid profile, and the humanoid classification. 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 humanoid profile + classification are derived as
for rigFromMeshSkin. 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/README
require("@builtin/modules/api/engine/modelImport") -- modelImport (also available as global '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. Public Luau surface over the __model and __rig Internal FFI namespaces.
Usage: local modelImport = require("@builtin/modules/api/engine/modelImport") Also available as global: modelImport
modules/modelImport/decompose
decompose(bytes: buffer | string, format: string): string
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): string
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): string
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: 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
handlestring— Promise handle fromextractAnimation.
modules/modelImport/result
result(handle: 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
handlestring— Promise handle fromdecompose.
modules/modelImport/retryHandle
retryHandle(makeHandle: () -> any, retries: number?, yield: (() -> ())?): string?
Call makeHandle — which returns a promise-handle 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 string 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 a non-string.
Parameters
makeHandle() -> any— Returns a promise-handle string, or a falsy value on failure.retriesnumber?(optional) — Extra attempts after the first (default 3).yield(() -> ())?(optional) — Called between attempts (defaulttask.wait).
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
auto-derived humanoid profile, and the humanoid classification. 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 humanoid profile + classification are derived as
for rigFromMeshSkin. 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) -> string
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 string — 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) -> string
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 string — 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) -> string
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 string — Promise handle for task.await.
typed/builtin//modules/api/engine/modelImport/modelImport/extractAnimationResult
modelImport.extractAnimationResult(handle: 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
handlestring— Promise handle fromextractAnimation.
Returns string? — The clip's .zanim payload bytes, or nil.
typed/builtin//modules/api/engine/modelImport/modelImport/result
modelImport.result(handle: 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
handlestring— 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: (() -> ())?) -> string?
Call makeHandle — which returns a promise-handle 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 string 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 a non-string.
Parameters
makeHandle() -> any— Returns a promise-handle string, or a falsy value on failure.retriesnumber(optional) — Extra attempts after the first (default 3).yield(() -> ())(optional) — Called between attempts (defaulttask.wait).
Returns string? — The handle string, or nil when every attempt failed.
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
auto-derived humanoid profile, and the humanoid classification. 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 humanoid profile + classification are derived as
for rigFromMeshSkin. 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)