# `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.