# `tecs.gfx.models`
Resident glTF models and allocation-stable node animation.
`tecs.gfx.models.load` decodes a glTF or GLB file and returns a shared model. Each `newInstance` owns its joint palettes, morph weights, and
primitive templates, so copies of one model can play different poses without
duplicating geometry, materials, textures, morph deltas, or clips.
```nupp
local model = assert(tecs.gfx.models.load("models/hero.gltf"))
local instance = model:newInstance()
local entities: {integer} = {}
for index, primitive in ipairs(instance.primitives) do
entities[index] = world:spawn(
primitive.transform,
primitive.mesh,
primitive.bounds,
primitive.material,
primitive.skin,
primitive.morph,
tecs.gfx.Tint(1, 1, 1, 1),
tecs.gfx.Renderable3D
)
end
world:commit()
for index, entity in ipairs(entities) do
instance:bind(world, index, entity)
end
instance:play("Walk")
world:addSystem({
name = "game.AnimateModel",
phase = tecs.ecs.phases.Update,
run = function(dt: number): nil
instance:update(dt)
end,
})
```
Sampling resets the reusable pose to the file's base TRS, applies one clip,
builds node world matrices in parent order, updates bound `Transform3D`
components, and stages each instance-owned skin palette and morph vector. No
table or cdata is allocated per sample. Bounds remain caller-owned and must
enclose every pose.
## Constructors
### `newMaterial` _constructor_
```nupp
function newMaterial(options: MaterialOptions): MaterialInput
```
Registers an independently authored material for any mesh.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `options` | `MaterialOptions` | The caller supplies an identity, shading model, factors and optional maps. |
#### Returns
| Type | Description |
| --- | --- |
| `MaterialInput` | Returns a shared MeshMaterial component ready to spawn. |
#### Raises
- When a map cannot load or a material factor is invalid.
### `newMesh` _constructor_
```nupp
function newMesh(options: MeshOptions): MeshInput, BoundsInput
```
Builds resident indexed geometry from the original twelve-float mesh layout.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `options` | `MeshOptions` | The caller supplies a stable name, position/normal/tangent/UV vertices and zero-based triangle indices. |
#### Returns
| Type | Description |
| --- | --- |
| `MeshInput` | The mesh component and its computed local bounding sphere. |
| `BoundsInput` | |
#### Raises
- When streams are empty, malformed or contain nonfinite values or invalid indices.
## Types
### `Animation` _record_
```nupp
record Animation
name: string
duration: number
channels: {Channel}
end
```
Holds one authored clip and its reusable channel data.
#### Fields
##### `name`
```nupp
name: string
```
Read-only. Names the authored clip.
##### `duration`
```nupp
duration: number
```
Read-only. Reports the clip duration in seconds.
##### `channels`
```nupp
channels: {Channel}
```
Engine-owned. Retains decoded channels for sampling; game code uses Instance.
### `BoundsInput` _record_
```nupp
record BoundsInput
componentType: C
value: T
end
```
Carries a native value across a mutation boundary without losing its definition.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
| `C` | |
#### Fields
##### `componentType`
```nupp
componentType: C
```
Read-only. Preserves the exact component or relationship definition.
##### `value`
```nupp
value: T
```
Caller-writable. Holds the struct supplied at the mutation boundary.
### `Instance` _record_
```nupp
record Instance
primitives: {Primitive}
transform: mesh.Transform3D?
animation: integer
time: number
speed: number
loop: boolean
playing: boolean
bind: function(exclusive self: Instance, world: World, primitive: integer, entity: integer): nil
unbind: function(exclusive self: Instance, primitive: integer): nil
play: function(exclusive self: Instance, animation: string | integer, options: PlayOptions?): nil
sample: function(exclusive self: Instance, animation: string | integer, time: number, loop: boolean?): nil
update: function(exclusive self: Instance, dt: number): nil
end
```
Plays one independently posed copy of a resident model.
#### Methods
##### `bind`
```nupp
bind: function(exclusive self: Instance, world: World, primitive: integer, entity: integer): nil
```
Binds one primitive to an existing entity's `Transform3D`.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Instance` | |
| `world` | `World` | The caller supplies the entity's world. Every binding on one instance must use the same world. |
| `primitive` | `integer` | The caller supplies a one-based primitive index. |
| `entity` | `integer` | The caller supplies a live entity carrying `Transform3D`. |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | The operation returns no value. |
##### `unbind`
```nupp
unbind: function(exclusive self: Instance, primitive: integer): nil
```
Removes one primitive's entity binding.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Instance` | |
| `primitive` | `integer` | The caller supplies a one-based primitive index. |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | The operation returns no value. |
##### `play`
```nupp
play: function(exclusive self: Instance, animation: string | integer, options: PlayOptions?): nil
```
Selects and restarts a clip.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Instance` | |
| `animation` | `string | integer` | The caller supplies a one-based index or clip name. |
| `options` | `PlayOptions?` | Omitted fields default to speed one, looping, and immediate playback. |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | The operation returns no value. |
##### `sample`
```nupp
sample: function(exclusive self: Instance, animation: string | integer, time: number, loop: boolean?): nil
```
Samples a clip at an explicit time without allocating.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Instance` | |
| `animation` | `string | integer` | The caller supplies a one-based index or clip name. |
| `time` | `number` | The caller supplies seconds. Negative values clamp to zero. |
| `loop` | `boolean?` | Whether time wraps at the duration. Defaults to false. |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | The operation returns no value. |
##### `update`
```nupp
update: function(exclusive self: Instance, dt: number): nil
```
Advances and samples the selected clip.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Instance` | |
| `dt` | `number` | The caller supplies non-negative elapsed seconds. |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | The operation returns no value. |
#### Fields
##### `primitives`
```nupp
primitives: {Primitive}
```
Read-only. Contains this instance's spawnable primitive bundles.
##### `transform`
```nupp
transform: mesh.Transform3D?
```
Caller-writable. Places the complete sampled model in world space, or
nil to preserve the file's authored placement without placement work.
Sampling composes this transform after the authored node hierarchy.
##### `animation`
```nupp
animation: integer
```
Read-only. Reports the selected one-based clip, or zero before `play`.
##### `time`
```nupp
time: number
```
Read-only. Reports the current clip time in seconds.
##### `speed`
```nupp
speed: number
```
Caller-writable. Multiplies elapsed time. Negative values raise on
`update`.
##### `loop`
```nupp
loop: boolean
```
Caller-writable. Controls whether playback wraps at the duration.
##### `playing`
```nupp
playing: boolean
```
Caller-writable. Controls whether `update` advances playback.
### `LoadOptions` _type_
```nupp
type LoadOptions = {
--- Caller-writable. Tunes each decoded material before its upload is queued.
--- @param value The callback receives the material's authored values in place.
--- @return The callback returns no value.
material: function(exclusive value: MaterialInfo): nil
}
```
Runs once per imported material before the resident GPU upload is queued.
### `MaterialInfo` _type_
```nupp
type MaterialInfo = {
--- Caller-writable. Names the authored material.
name: string,
--- Caller-writable. Selects metallic-roughness (0), unlit (1), or Lambert (2).
model: integer,
--- Caller-writable. Sets the metallic factor from zero to one.
metallic: number,
--- Caller-writable. Sets the perceptual roughness from zero to one.
roughness: number
}
```
Allows import-time material tuning without duplicating geometry or images.
### `MaterialInput` _record_
```nupp
record MaterialInput
componentType: C
value: T
end
```
Carries a native value across a mutation boundary without losing its definition.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
| `C` | |
#### Fields
##### `componentType`
```nupp
componentType: C
```
Read-only. Preserves the exact component or relationship definition.
##### `value`
```nupp
value: T
```
Caller-writable. Holds the struct supplied at the mutation boundary.
### `MaterialOptions` _type_
```nupp
type MaterialOptions = {
--- Caller-writable. Names the shared material with a stable, non-empty identity.
name: string,
--- Caller-writable. Selects a MATERIAL_* model; metallic-roughness is the default.
model: integer?,
--- Caller-writable. Sets linear red reflectance and defaults to one.
baseR: number?,
--- Caller-writable. Sets linear green reflectance and defaults to one.
baseG: number?,
--- Caller-writable. Sets linear blue reflectance and defaults to one.
baseB: number?,
--- Caller-writable. Sets opacity from zero to one and defaults to one.
baseA: number?,
--- Caller-writable. Sets linear red emission and defaults to zero.
emissiveR: number?,
--- Caller-writable. Sets linear green emission and defaults to zero.
emissiveG: number?,
--- Caller-writable. Sets linear blue emission and defaults to zero.
emissiveB: number?,
--- Caller-writable. Sets the metallic factor from zero to one and defaults to zero.
metallic: number?,
--- Caller-writable. Sets perceptual roughness from zero to one and defaults to one.
roughness: number?,
--- Caller-writable. Scales the normal map XY channels and defaults to one.
normalScale: number?,
--- Caller-writable. Sets occlusion strength from zero to one and defaults to one.
occlusionStrength: number?,
--- Caller-writable. Selects opaque, mask, or blend and defaults to opaque.
alphaMode: string?,
--- Caller-writable. Sets the mask threshold from zero to one and defaults to 0.5.
alphaCutoff: number?,
--- Caller-writable. Disables back-face culling when true and defaults to false.
doubleSided: boolean?,
--- Caller-writable. Names the optional base-color texture, sampled as sRGB.
baseMap: string?,
--- Caller-writable. Names the optional tangent-space normal texture, sampled as
--- linear data.
normalMap: string?,
--- Caller-writable. Names the optional linear texture with roughness in green and
--- metallic in blue.
metallicRoughnessMap: string?,
--- Caller-writable. Names the optional linear texture with occlusion in red.
occlusionMap: string?,
--- Caller-writable. Names the optional emission texture, sampled as sRGB.
emissionMap: string?
}
```
Configures a shared mesh material. Colors and emission are linear RGB
texture paths load relative to the caller's content directory.
### `MeshInput` _record_
```nupp
record MeshInput
componentType: C
value: T
end
```
Carries a native value across a mutation boundary without losing its definition.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
| `C` | |
#### Fields
##### `componentType`
```nupp
componentType: C
```
Read-only. Preserves the exact component or relationship definition.
##### `value`
```nupp
value: T
```
Caller-writable. Holds the struct supplied at the mutation boundary.
### `MeshOptions` _type_
```nupp
type MeshOptions = {
--- Caller-writable. Names shared geometry with a stable, non-empty identity.
name: string,
--- Caller-writable. Supplies twelve floats per vertex: position XYZ, normal XYZ,
--- tangent XYZW and texture UV.
vertices: {number},
--- Caller-writable. Supplies zero-based triangle indices, three per triangle.
indices: {integer},
--- Caller-writable. Optionally supplies four linear RGBA values per vertex.
colors: {number}?,
--- Caller-writable. Optionally supplies four zero-based joint indices per vertex.
joints: {integer}?,
--- Caller-writable. Supplies four nonnegative weights with each joint quartet.
--- Each quartet must have positive total; the constructor normalizes it.
weights: {number}?
}
```
Describes one indexed triangle mesh in local coordinates.
### `Model` _record_
```nupp
record Model
path: string
id: integer
animations: {Animation}
animationCount: integer
newInstance: function(self: Model): Instance
animationIndex: function(borrows self: Model, name: string): integer?
end
```
Retains shared geometry, materials and authored clips.
#### Methods
##### `newInstance`
```nupp
newInstance: function(self: Model): Instance
```
Creates an independently animated instance.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Model` | |
###### Returns
| Type | Description |
| --- | --- |
| `Instance` | Returns reusable primitive templates plus private joint palettes and morph-weight vectors. |
##### `animationIndex`
```nupp
animationIndex: function(borrows self: Model, name: string): integer?
```
Returns a clip's one-based index.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `borrows self` | `Model` | |
| `name` | `string` | The caller supplies an authored or generated clip name. |
###### Returns
| Type | Description |
| --- | --- |
| `integer?` | Returns its index, or nil when absent. Duplicate authored names raise because selecting either by that name would be ambiguous. |
#### Fields
##### `path`
```nupp
path: string
```
Read-only. Contains the source asset path.
##### `id`
```nupp
id: integer
```
Engine-owned. Identifies the resident upload; game code selects primitives
instead.
##### `animations`
```nupp
animations: {Animation}
```
Read-only. Contains decoded clips in file order.
##### `animationCount`
```nupp
animationCount: integer
```
Read-only. Reports the number of decoded clips.
### `PlayOptions` _type_
```nupp
type PlayOptions = {
--- Caller-writable. Multiplies elapsed time. Negative values raise on
--- `update`.
speed: number?,
--- Caller-writable. Controls whether playback wraps at the duration.
loop: boolean?,
--- Caller-writable. Controls whether `update` advances playback.
playing: boolean?
}
```
Controls clip playback, with speed one, looping and immediate playback by default.
### `Primitive` _record_
```nupp
record Primitive
transform: TransformInput
mesh: MeshInput
bounds: BoundsInput
material: MaterialInput
skin: SkinInput
morph: MorphInput
materialName: string = ""
end
```
Holds the spawnable components for one independently posed primitive.
#### Fields
##### `transform`
```nupp
transform: TransformInput
```
Caller-writable. Contains this primitive's sampled world transform.
##### `mesh`
```nupp
mesh: MeshInput
```
Caller-writable. Selects resident geometry.
##### `bounds`
```nupp
bounds: BoundsInput
```
Caller-writable. Supplies the local bound, which must enclose every
animated pose.
##### `material`
```nupp
material: MaterialInput
```
Caller-writable. Selects resident PBR material data.
##### `skin`
```nupp
skin: SkinInput
```
Caller-writable. Selects this instance's joint palette; its empty palette
denotes rigid geometry.
##### `morph`
```nupp
morph: MorphInput
```
Caller-writable. Selects this instance's morph weights; its empty palette
denotes geometry without morph targets.
##### `materialName`
```nupp
materialName: string
```
Read-only. Reports the authored material name for fixture and game metadata.
## Functions
### `load` _function_
```nupp
function load(path: string, options: LoadOptions?): Model?, string?
```
Loads and registers a glTF or GLB scene for independent instances.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | The caller supplies a model path; external buffers and images resolve beside it. |
| `options` | `LoadOptions?` | The caller may tune imported materials before upload. |
#### Returns
| Type | Description |
| --- | --- |
| `Model?` | The shared model, or nil and the decoder's reason when loading fails. |
| `string?` | |
#### Raises
- When the supplied path is empty.
### `loadEnvironment` _function_
```nupp
function loadEnvironment(name: string, faces: {string}): string?, string?
```
Loads the six original environment faces and registers their shared GPU mip chain.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | The stable, non-empty environment identity used by View lighting and snapshots. |
| `faces` | `{string}` | File paths in positive X, negative X, positive Y, negative Y, positive Z, negative Z order. |
#### Returns
| Type | Description |
| --- | --- |
| `string?` | The environment name, or nil and a decoding failure. All faces must be equally sized squares. |
| `string?` | |
### `nextUpload` _function_
```nupp
function nextUpload(): integer, string
```
Drains one model upload for the native host; zero means the queue is empty.
#### Returns
| Type | Description |
| --- | --- |
| `integer` | The model ID and packed geometry, or zero and an empty string. |
| `string` | |
## Values
### `MATERIAL_LAMBERT` _variable_
```nupp
const MATERIAL_LAMBERT: integer
```
Selects diffuse Lambert shading.
### `MATERIAL_METALLIC_ROUGHNESS` _variable_
```nupp
const MATERIAL_METALLIC_ROUGHNESS: integer
```
Selects Cook-Torrance metallic-roughness shading.
### `MATERIAL_UNLIT` _variable_
```nupp
const MATERIAL_UNLIT: integer
```
Selects unlit base color and emission.