
# tecs.assets


Reads bytes and decodes images and sounds on a worker.

Each load returns a [`Future`](/modules/Future/) immediately:

```teal
return tecs.newApplication({
    plugin = function(world: tecs.World, app: tecs.Application)
        tecs.assets.loadImage(
            tecs.io.files.assetPath("sprites/hero.png")
        )
            :map(function(image: tecs.assets.Image): tecs.gfx.Sprite
                return app.renderer.sprites:registerImage(image)
            end)
            :onSettle(function(sprite: tecs.Future<tecs.gfx.Sprite>)
                if sprite.status == "ready" then
                    world:spawn(
                        tecs.Transform2D(100, 100),
                        sprite.value,
                        tecs.gfx.Renderable2D()
                    )
                end
            end)
    end,
})
```

The loader decodes pixels but creates no GPU resource. The renderer decides
texture residency. Audio follows the same split between decoded clips and
voices.

`newMesh` constructs immutable procedural geometry synchronously. `loadGLTF`
decodes static glTF 2.0 and GLB scenes on the worker, including their images,
metallic-roughness materials, alpha masks, and `BLEND` modes. Both produce
CPU-owned data that the mesh domain consumes when it becomes resident. A model
with a `BLEND` material requires a mesh domain created with
`transparency = true`.

## Lifetime

[`Application`](/modules/Application/) installs and shuts down the process-wide worker, and
[`runtime.poll`](/modules/runtime/#tecs.runtime.poll) updates it. Headless code
must call `install`, pump with `tecs.runtime.poll` or wait with `waitAll`, then
call `shutdown`. Drain wanted results before shutdown because in-flight
futures otherwise remain pending.

Cancel a pending future to release one caller's interest. Release a ready
payload when its consumer takes ownership. The final release frees the decoded
memory.

Overlapping image loads for one path share a decode. Each caller receives its
own future and a hold on the same image. A later load after settlement decodes
again. Sound loads never share.

`loadString` reads a complete file into a binary-safe string without
interpreting it. Image loads accept PNG, JPEG and static SVG. SVG renders at
its intrinsic dimensions. It uses the bundled JetBrains Mono for all text and
ignores external image references, so installed fonts and the worker's current
directory cannot change its pixels. `tecs.audio.decoders()` reports the sound
formats linked into the current build. Sound mode `"resident"` decodes the
complete clip, `"stream"` opens a source for each voice, and `"auto"` chooses
from the duration threshold.

Use `Future.all` to join independent loads. Release each ready payload after
its consumer takes ownership.

## Module contents

### Constructors

| Constructor | Description |
| --- | --- |
| [`newMesh`](/modules/assets/#tecs.assets.newMesh) | Builds procedural mesh data in the renderer's fixed vertex layout. |

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`Image`](/modules/assets/#tecs.assets.Image) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Represents decoded pixels and the caller's hold on them. |
| [`Mesh`](/modules/assets/#tecs.assets.Mesh) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Describes one immutable indexed triangle mesh before GPU registration. |
| [`MeshOptions`](/modules/assets/#tecs.assets.MeshOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Supplies procedural geometry to newMesh. |
| [`Model`](/modules/assets/#tecs.assets.Model) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Represents one decoded static glTF 2.0 scene. |
| [`ModelDraw`](/modules/assets/#tecs.assets.ModelDraw) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Describes one static primitive instance in a decoded glTF scene. |
| [`ModelMaterial`](/modules/assets/#tecs.assets.ModelMaterial) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Describes one decoded glTF material before GPU registration. |
| [`Sound`](/modules/assets/#tecs.assets.Sound) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Represents a loaded clip and the caller's hold on it. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`install`](/modules/assets/#tecs.assets.install) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Starts the loading worker. |
| [`installed`](/modules/assets/#tecs.assets.installed) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Reports whether the loading worker is running. |
| [`loadGLTF`](/modules/assets/#tecs.assets.loadGLTF) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Queues static glTF 2.0 or GLB decoding on the asset worker. |
| [`loadImage`](/modules/assets/#tecs.assets.loadImage) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Queues an image for loading and answers a future for it immediately. |
| [`loadSound`](/modules/assets/#tecs.assets.loadSound) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Queues a sound for loading and answers a future for it immediately. |
| [`loadString`](/modules/assets/#tecs.assets.loadString) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Queues a complete file read and answers a future for its bytes immediately. |
| [`pending`](/modules/assets/#tecs.assets.pending) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the number of loads still waiting on the worker. |
| [`shutdown`](/modules/assets/#tecs.assets.shutdown) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Stops the loading worker. |
| [`update`](/modules/assets/#tecs.assets.update) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Takes finished loads and settles their futures. |
| [`waitAll`](/modules/assets/#tecs.assets.waitAll) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Blocks until every queued load has finished. |

## Constructors

<a id="tecs.assets.newMesh"></a>
### tecs.assets.newMesh <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Builds procedural mesh data in the renderer's fixed vertex layout.


```teal
function tecs.assets.newMesh(options: MeshOptions): Mesh
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `options` | [`MeshOptions`](/modules/assets/#tecs.assets.MeshOptions) | The caller supplies a name, interleaved vertices, and zero-based triangle indices. |

#### Returns

| Type | Description |
| --- | --- |
| [`Mesh`](/modules/assets/#tecs.assets.Mesh) | Returns CPU geometry that `renderer.meshes:registerMesh` consumes or the caller releases. |

## Types

<a id="tecs.assets.Image"></a>
### tecs.assets.Image <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Represents decoded pixels and the caller's hold on them.

What a `loadImage` future settles to. Reference counts govern the pixels rather than
owned outright, because two loads of one path that overlap share a decode:
each caller holds the same `Image`, and the last caller to release it
one that frees. Reading `pixels` after that is reading freed memory, which
is why a released image reads nil there rather than looking uploadable.
Read-only. Exposes the decoded image type.


```teal
record tecs.assets.Image
    path: string
    pixels: loader.CValue
    width: integer
    height: integer
    pitch: integer

    release: function(self)
end
```

<a id="tecs.assets.Image.path"></a>
#### tecs.assets.Image.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the requested path unchanged. The loader does not resolve it, so it is
whatever the caller passed.


```teal
tecs.assets.Image.path: string
```

<a id="tecs.assets.Image.pixels"></a>
#### tecs.assets.Image.pixels <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains decoded RGBA pixels until the last `release`, then
becomes nil.


```teal
tecs.assets.Image.pixels: loader.CValue
```

<a id="tecs.assets.Image.width"></a>
#### tecs.assets.Image.width <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the width in pixels.


```teal
tecs.assets.Image.width: integer
```

<a id="tecs.assets.Image.height"></a>
#### tecs.assets.Image.height <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the height in pixel rows.


```teal
tecs.assets.Image.height: integer
```

<a id="tecs.assets.Image.pitch"></a>
#### tecs.assets.Image.pitch <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the row stride in bytes, which a decoder may pad
beyond `width * 4`.


```teal
tecs.assets.Image.pitch: integer
```

<a id="tecs.assets.Image.release"></a>
#### tecs.assets.Image:release <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Gives up this caller's hold on the pixels, and frees at the last.

Called once whatever needed them has taken a copy, which for an image
means after the caller uploads it. Where several loads shared one decode,
this releases one of them.

Releasing an image already down to nothing does nothing, so a shutdown
path need not know whether something else got there first.


```teal
function tecs.assets.Image.release(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Image` |  |

##### Returns

None.

<a id="tecs.assets.Mesh"></a>
### tecs.assets.Mesh <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Describes one immutable indexed triangle mesh before GPU registration.

Vertices use one fixed interleaved layout: position xyz, normal xyz,
tangent xyzw, and texture uv. Indices are zero-based unsigned 32-bit
values. A mesh may therefore contain far more than 65,535 vertices, and a
primitive is one indexed range rather than one triangle.

`renderer.meshes:registerMesh` copies both arrays into device-local
storage and releases this object. The Lua-number constructor is intended
for procedural and example geometry. A file decoder can build the same
record directly without expanding a large mesh into Lua tables.
Read-only. Exposes the immutable CPU mesh type.


```teal
record tecs.assets.Mesh
    name: string
    vertices: loader.CArray
    indices: loader.CArray
    vertexCount: integer
    indexCount: integer
    centerX: number
    centerY: number
    centerZ: number
    radius: number

    release: function(self)
end
```

<a id="tecs.assets.Mesh.name"></a>
#### tecs.assets.Mesh.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the stable name used by `meshId` and snapshots.


```teal
tecs.assets.Mesh.name: string
```

<a id="tecs.assets.Mesh.vertices"></a>
#### tecs.assets.Mesh.vertices <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains interleaved vertex floats until `release` runs.


```teal
tecs.assets.Mesh.vertices: loader.CArray
```

<a id="tecs.assets.Mesh.indices"></a>
#### tecs.assets.Mesh.indices <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains zero-based unsigned 32-bit indices until `release`
runs.


```teal
tecs.assets.Mesh.indices: loader.CArray
```

<a id="tecs.assets.Mesh.vertexCount"></a>
#### tecs.assets.Mesh.vertexCount <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the number of vertices, not floats.


```teal
tecs.assets.Mesh.vertexCount: integer
```

<a id="tecs.assets.Mesh.indexCount"></a>
#### tecs.assets.Mesh.indexCount <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the number of indices. It is always a multiple of
three.


```teal
tecs.assets.Mesh.indexCount: integer
```

<a id="tecs.assets.Mesh.centerX"></a>
#### tecs.assets.Mesh.centerX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the local-space bounding-sphere center x.


```teal
tecs.assets.Mesh.centerX: number
```

<a id="tecs.assets.Mesh.centerY"></a>
#### tecs.assets.Mesh.centerY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the local-space bounding-sphere center y.


```teal
tecs.assets.Mesh.centerY: number
```

<a id="tecs.assets.Mesh.centerZ"></a>
#### tecs.assets.Mesh.centerZ <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the local-space bounding-sphere center z.


```teal
tecs.assets.Mesh.centerZ: number
```

<a id="tecs.assets.Mesh.radius"></a>
#### tecs.assets.Mesh.radius <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the non-negative local-space bounding-sphere radius.


```teal
tecs.assets.Mesh.radius: number
```

<a id="tecs.assets.Mesh.release"></a>
#### tecs.assets.Mesh:release <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Gives up the CPU geometry. Calling it again does nothing.


```teal
function tecs.assets.Mesh.release(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Mesh` |  |

##### Returns

None.

<a id="tecs.assets.MeshOptions"></a>
### tecs.assets.MeshOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Supplies procedural geometry to `newMesh`.


```teal
global record tecs.assets.MeshOptions
    name: string
    vertices: {number}
    indices: {integer}
end
```

<a id="tecs.assets.MeshOptions.name"></a>
#### tecs.assets.MeshOptions.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Supplies the stable non-empty mesh name.


```teal
tecs.assets.MeshOptions.name: string
```

<a id="tecs.assets.MeshOptions.vertices"></a>
#### tecs.assets.MeshOptions.vertices <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Supplies interleaved position xyz, normal xyz,
tangent xyzw, and texture uv floats.


```teal
tecs.assets.MeshOptions.vertices: {number}
```

<a id="tecs.assets.MeshOptions.indices"></a>
#### tecs.assets.MeshOptions.indices <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Supplies zero-based triangle indices.


```teal
tecs.assets.MeshOptions.indices: {integer}
```

<a id="tecs.assets.Model"></a>
### tecs.assets.Model <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Represents one decoded static glTF 2.0 scene.

Meshes use the same fixed vertex layout as `newMesh`. Images remain
decoded CPU pixels. `renderer.meshes:registerModel` consumes all of them
and returns spawnable primitive records. Skinning, morph targets,
animation are deliberately outside this static lane.
Read-only. Exposes the decoded static glTF scene type.


```teal
record tecs.assets.Model
    path: string
    meshes: {Mesh}
    images: {Image}
    materials: {ModelMaterial}
    draws: {ModelDraw}

    release: function(self)
end
```

<a id="tecs.assets.Model.path"></a>
#### tecs.assets.Model.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the requested `.gltf` or `.glb` path unchanged.


```teal
tecs.assets.Model.path: string
```

<a id="tecs.assets.Model.meshes"></a>
#### tecs.assets.Model.meshes <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains unique decoded primitive geometry.


```teal
tecs.assets.Model.meshes: {Mesh}
```

<a id="tecs.assets.Model.images"></a>
#### tecs.assets.Model.images <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains unique decoded source images.


```teal
tecs.assets.Model.images: {Image}
```

<a id="tecs.assets.Model.materials"></a>
#### tecs.assets.Model.materials <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains decoded metallic-roughness material descriptions.


```teal
tecs.assets.Model.materials: {ModelMaterial}
```

<a id="tecs.assets.Model.draws"></a>
#### tecs.assets.Model.draws <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the selected scene's flattened static draws.


```teal
tecs.assets.Model.draws: {ModelDraw}
```

<a id="tecs.assets.Model.release"></a>
#### tecs.assets.Model:release <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Releases every CPU mesh and image not already consumed. Calling it
again does nothing.


```teal
function tecs.assets.Model.release(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Model` |  |

##### Returns

None.

<a id="tecs.assets.ModelDraw"></a>
### tecs.assets.ModelDraw <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Describes one static primitive instance in a decoded glTF scene.
Read-only. Exposes one flattened glTF primitive instance.


```teal
record tecs.assets.ModelDraw
    mesh: integer
    material: integer
    x: number
    y: number
    z: number
    rotationX: number
    rotationY: number
    rotationZ: number
    rotationW: number
    scaleX: number
    scaleY: number
    scaleZ: number
end
```

<a id="tecs.assets.ModelDraw.mesh"></a>
#### tecs.assets.ModelDraw.mesh <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects a one-based entry in `Model.meshes`.


```teal
tecs.assets.ModelDraw.mesh: integer
```

<a id="tecs.assets.ModelDraw.material"></a>
#### tecs.assets.ModelDraw.material <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects a one-based entry in `Model.materials`, or zero for
the neutral material.


```teal
tecs.assets.ModelDraw.material: integer
```

<a id="tecs.assets.ModelDraw.x"></a>
#### tecs.assets.ModelDraw.x <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world translation x from the selected glTF scene.


```teal
tecs.assets.ModelDraw.x: number
```

<a id="tecs.assets.ModelDraw.y"></a>
#### tecs.assets.ModelDraw.y <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world translation y.


```teal
tecs.assets.ModelDraw.y: number
```

<a id="tecs.assets.ModelDraw.z"></a>
#### tecs.assets.ModelDraw.z <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world translation z.


```teal
tecs.assets.ModelDraw.z: number
```

<a id="tecs.assets.ModelDraw.rotationX"></a>
#### tecs.assets.ModelDraw.rotationX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world quaternion x.


```teal
tecs.assets.ModelDraw.rotationX: number
```

<a id="tecs.assets.ModelDraw.rotationY"></a>
#### tecs.assets.ModelDraw.rotationY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world quaternion y.


```teal
tecs.assets.ModelDraw.rotationY: number
```

<a id="tecs.assets.ModelDraw.rotationZ"></a>
#### tecs.assets.ModelDraw.rotationZ <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world quaternion z.


```teal
tecs.assets.ModelDraw.rotationZ: number
```

<a id="tecs.assets.ModelDraw.rotationW"></a>
#### tecs.assets.ModelDraw.rotationW <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world quaternion w.


```teal
tecs.assets.ModelDraw.rotationW: number
```

<a id="tecs.assets.ModelDraw.scaleX"></a>
#### tecs.assets.ModelDraw.scaleX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world scale x.


```teal
tecs.assets.ModelDraw.scaleX: number
```

<a id="tecs.assets.ModelDraw.scaleY"></a>
#### tecs.assets.ModelDraw.scaleY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world scale y.


```teal
tecs.assets.ModelDraw.scaleY: number
```

<a id="tecs.assets.ModelDraw.scaleZ"></a>
#### tecs.assets.ModelDraw.scaleZ <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports world scale z.


```teal
tecs.assets.ModelDraw.scaleZ: number
```

<a id="tecs.assets.ModelMaterial"></a>
### tecs.assets.ModelMaterial <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Describes one decoded glTF material before GPU registration.
Read-only. Exposes one glTF material description.


```teal
record tecs.assets.ModelMaterial
    name: string
    model: integer
    alphaMode: integer
    baseColorImage: integer
    normalImage: integer
    metallicRoughnessImage: integer
    occlusionImage: integer
    emissiveImage: integer
    alphaCutoff: number
    baseR: number
    baseG: number
    baseB: number
    baseA: number
    emissiveR: number
    emissiveG: number
    emissiveB: number
    metallic: number
    roughness: number
    normalScale: number
    occlusionStrength: number
end
```

<a id="tecs.assets.ModelMaterial.name"></a>
#### tecs.assets.ModelMaterial.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the stable material name.


```teal
tecs.assets.ModelMaterial.name: string
```

<a id="tecs.assets.ModelMaterial.model"></a>
#### tecs.assets.ModelMaterial.model <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects metallic-roughness PBR at zero or unlit at one.


```teal
tecs.assets.ModelMaterial.model: integer
```

<a id="tecs.assets.ModelMaterial.alphaMode"></a>
#### tecs.assets.ModelMaterial.alphaMode <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects opaque, masked, or blended rendering with a
`MeshDomain.ALPHA_*` integer constant.


```teal
tecs.assets.ModelMaterial.alphaMode: integer
```

<a id="tecs.assets.ModelMaterial.baseColorImage"></a>
#### tecs.assets.ModelMaterial.baseColorImage <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects a one-based entry in `Model.images`, or zero.


```teal
tecs.assets.ModelMaterial.baseColorImage: integer
```

<a id="tecs.assets.ModelMaterial.normalImage"></a>
#### tecs.assets.ModelMaterial.normalImage <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects a tangent-space normal image, or zero.


```teal
tecs.assets.ModelMaterial.normalImage: integer
```

<a id="tecs.assets.ModelMaterial.metallicRoughnessImage"></a>
#### tecs.assets.ModelMaterial.metallicRoughnessImage <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects a glTF metallic-roughness image, or zero.


```teal
tecs.assets.ModelMaterial.metallicRoughnessImage: integer
```

<a id="tecs.assets.ModelMaterial.occlusionImage"></a>
#### tecs.assets.ModelMaterial.occlusionImage <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects an occlusion image, or zero.


```teal
tecs.assets.ModelMaterial.occlusionImage: integer
```

<a id="tecs.assets.ModelMaterial.emissiveImage"></a>
#### tecs.assets.ModelMaterial.emissiveImage <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Selects an emissive image, or zero.


```teal
tecs.assets.ModelMaterial.emissiveImage: integer
```

<a id="tecs.assets.ModelMaterial.alphaCutoff"></a>
#### tecs.assets.ModelMaterial.alphaCutoff <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the alpha-mask cutoff. Zero keeps every fragment.


```teal
tecs.assets.ModelMaterial.alphaCutoff: number
```

<a id="tecs.assets.ModelMaterial.baseR"></a>
#### tecs.assets.ModelMaterial.baseR <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the base-color red factor.


```teal
tecs.assets.ModelMaterial.baseR: number
```

<a id="tecs.assets.ModelMaterial.baseG"></a>
#### tecs.assets.ModelMaterial.baseG <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the base-color green factor.


```teal
tecs.assets.ModelMaterial.baseG: number
```

<a id="tecs.assets.ModelMaterial.baseB"></a>
#### tecs.assets.ModelMaterial.baseB <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the base-color blue factor.


```teal
tecs.assets.ModelMaterial.baseB: number
```

<a id="tecs.assets.ModelMaterial.baseA"></a>
#### tecs.assets.ModelMaterial.baseA <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the base-color alpha factor.


```teal
tecs.assets.ModelMaterial.baseA: number
```

<a id="tecs.assets.ModelMaterial.emissiveR"></a>
#### tecs.assets.ModelMaterial.emissiveR <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the emissive red factor.


```teal
tecs.assets.ModelMaterial.emissiveR: number
```

<a id="tecs.assets.ModelMaterial.emissiveG"></a>
#### tecs.assets.ModelMaterial.emissiveG <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the emissive green factor.


```teal
tecs.assets.ModelMaterial.emissiveG: number
```

<a id="tecs.assets.ModelMaterial.emissiveB"></a>
#### tecs.assets.ModelMaterial.emissiveB <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the emissive blue factor.


```teal
tecs.assets.ModelMaterial.emissiveB: number
```

<a id="tecs.assets.ModelMaterial.metallic"></a>
#### tecs.assets.ModelMaterial.metallic <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the metallic factor.


```teal
tecs.assets.ModelMaterial.metallic: number
```

<a id="tecs.assets.ModelMaterial.roughness"></a>
#### tecs.assets.ModelMaterial.roughness <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the roughness factor.


```teal
tecs.assets.ModelMaterial.roughness: number
```

<a id="tecs.assets.ModelMaterial.normalScale"></a>
#### tecs.assets.ModelMaterial.normalScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports tangent-space normal strength.


```teal
tecs.assets.ModelMaterial.normalScale: number
```

<a id="tecs.assets.ModelMaterial.occlusionStrength"></a>
#### tecs.assets.ModelMaterial.occlusionStrength <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports sampled occlusion strength.


```teal
tecs.assets.ModelMaterial.occlusionStrength: number
```

<a id="tecs.assets.Sound"></a>
### tecs.assets.Sound <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Represents a loaded clip and the caller's hold on it.

What a `loadSound` future settles to. Two overlapping loads of one path do
not share, unlike images, so a `Sound`
normally has one holder; the count is
here so that releasing twice frees once rather than twice.
Read-only. Exposes the loaded sound type.


```teal
record tecs.assets.Sound
    path: string
    audio: loader.CValue
    resident: boolean
    durationMs: integer

    release: function(self)
end
```

<a id="tecs.assets.Sound.path"></a>
#### tecs.assets.Sound.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the requested path unchanged.


```teal
tecs.assets.Sound.path: string
```

<a id="tecs.assets.Sound.audio"></a>
#### tecs.assets.Sound.audio <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the loaded clip until the last `release`. It is nil for one that
streams, which holds nothing, and nil once released.


```teal
tecs.assets.Sound.audio: loader.CValue
```

<a id="tecs.assets.Sound.resident"></a>
#### tecs.assets.Sound.resident <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports whether memory holds the decoded audio. It is false for a clip each voice
reads from the file for itself.


```teal
tecs.assets.Sound.resident: boolean
```

<a id="tecs.assets.Sound.durationMs"></a>
#### tecs.assets.Sound.durationMs <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the length in milliseconds, or -1 when the container
cannot say.


```teal
tecs.assets.Sound.durationMs: integer
```

<a id="tecs.assets.Sound.release"></a>
#### tecs.assets.Sound:release <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Gives up this caller's hold on the clip, and frees at the last.

A voice reads a clip where it lies, so release a clip when nothing
will play it again. A track holds its own reference, so releasing one
that is still sounding is safe: the mixer drops it when the last track
using it does.

Releasing a clip already down to nothing does nothing.


```teal
function tecs.assets.Sound.release(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Sound` |  |

##### Returns

None.

## Functions

<a id="tecs.assets.install"></a>
### tecs.assets.install <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Starts the loading worker.

Installing twice is installing once. Spawning unconditionally would leave
the first thread running with both its channels and nothing reading them,
and every queued decode would answer into an abandoned channel, so a load
in flight across the second call would never resolve.



```teal
function tecs.assets.install(luaPath: string)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `luaPath` | `string` | `package.path` for the worker's own state, which shares no loaded modules with this one. Defaults to this state's, which is what makes the worker resolve the same modules the game does. |

#### Returns

None.

<a id="tecs.assets.installed"></a>
### tecs.assets.installed <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Reports whether the loading worker is running.

For a subsystem that loads an asset of its own and has no way of knowing whether the
game has started the worker yet.



```teal
function tecs.assets.installed(): boolean
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Whether the worker exists, which is a fact about the process rather than about any world. False means a load raises rather than queueing, so this is the guard rather than an optimization. |

<a id="tecs.assets.loadGLTF"></a>
### tecs.assets.loadGLTF <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Queues static glTF 2.0 or GLB decoding on the asset worker.

External buffers and images resolve relative to the model path. Data
URIs and embedded GLB resources are supported. Triangle primitives,
static node transforms, metallic-roughness PBR, normal, occlusion,
emissive, alpha-mask, alpha-blended, and unlit materials are decoded.
A skinned, morphed, animated, sparse-accessor, or non-triangle input
settles as failed instead of being loaded incompletely. Registering a
model containing alpha blending requires a mesh domain created with
`transparency = true`.


```teal
function tecs.assets.loadGLTF(path: string): Future<Model>
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | The caller supplies a `.gltf` or `.glb` asset path. |

#### Returns

| Type | Description |
| --- | --- |
| [`Future`](/modules/Future/)`<`[`Model`](/modules/assets/#tecs.assets.Model)`>` | Returns a pending future that owns its ready model. |

<a id="tecs.assets.loadImage"></a>
### tecs.assets.loadImage <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Queues an image for loading and answers a future for it immediately.

Two loads of one path that overlap share a decode, because decoding the same
PNG twice at once duplicates work without producing another result.
They share the [`Image`](/modules/assets/#tecs.assets.Image), so the last caller to release it
one that frees, and each of them holds a future of its own, so one canceling
is invisible to the others. A load that starts after the first has settled
decodes again: nothing here is a cache.



```teal
function tecs.assets.loadImage(path: string): Future<Image>
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | The caller supplies a PNG, JPEG or static SVG path. The loader renders SVG at its intrinsic width and height, uses bundled JetBrains Mono for text, and ignores external image references. Another format fails during decoding. |

#### Returns

| Type | Description |
| --- | --- |
| [`Future`](/modules/Future/)`<`[`Image`](/modules/assets/#tecs.assets.Image)`>` | A pending future, never nil. Raises instead when `install` has not run. Failures reach it as a failed settlement, not through this call: a missing file is a future that fails once the worker has looked. Canceling it gives up this caller's interest in the decode, and the last caller to do so abandons it. |

<a id="tecs.assets.loadSound"></a>
### tecs.assets.loadSound <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Queues a sound for loading and answers a future for it immediately.

Whatever the mixer's decoders can read loads, so the format is the file's
business rather than the caller's.

`mode` is "resident", "stream", or "auto", and auto keeps anything shorter
than `streamMs` resident.

This call initializes the library instead of the worker because `MIX_Init`
does not support concurrent calls. Initialization before sending the task puts it
in order ahead of every decode without a lock.

Two overlapping loads of one path do not share, unlike images: each gets its
own clip, because that is what `MIX_LoadAudio` produces. So this answers the
root future rather than a link over one, and the
[`Sound`](/modules/assets/#tecs.assets.Sound) it settles to has
exactly one holder.



```teal
function tecs.assets.loadSound(
    path: string, mode: string, streamMs: integer
): Future<Sound>
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` |  |
| `mode` | `string` | "resident", "stream" or "auto". Any other value selects "stream". |
| `streamMs` | `integer` | The boundary "auto" decides on, in milliseconds. Read only under "auto", and a file whose length the container cannot state streams whatever it says. |

#### Returns

| Type | Description |
| --- | --- |
| [`Future`](/modules/Future/)`<`[`Sound`](/modules/assets/#tecs.assets.Sound)`>` | A pending future, or a failed future when mixer initialization fails. This is the only failure reported without waiting for the worker. |

<a id="tecs.assets.loadString"></a>
### tecs.assets.loadString <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Queues a complete file read and answers a future for its bytes immediately.

The returned string preserves embedded NUL bytes. Overlapping reads of one
path share the worker task while each caller receives its own future, so one
caller may cancel without affecting another. A later read after settlement
reads the file again.



```teal
function tecs.assets.loadString(
    path: string, kind: string
): Future<string>
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | The caller supplies an absolute path or one from [`assetPath`](/modules/io/files/#tecs.io.files.assetPath). |
| `kind` | `string` | The caller supplies the content kind used by file watching, or omits it to record a document. |

#### Returns

| Type | Description |
| --- | --- |
| [`Future`](/modules/Future/)`<string>` | Returns a pending [`Future`](/modules/Future/). A missing or unreadable file settles it as failed. |

<a id="tecs.assets.pending"></a>
### tecs.assets.pending <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the number of loads still waiting on the worker.



```teal
function tecs.assets.pending(): integer
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `integer` | Every load in flight in this process, not one caller's. A load every caller has canceled is still counted until the worker answers for it, because the address it sends still has to be taken and destroyed. |

<a id="tecs.assets.shutdown"></a>
### tecs.assets.shutdown <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Stops the loading worker.

Blocks until the thread exits. This call drops loads still in flight and leaves their
futures pending forever, so drain with `waitAll` first when their results matter.
Releasing an [`Image`](/modules/assets/#tecs.assets.Image) or a
[`Sound`](/modules/assets/#tecs.assets.Sound) that already settled still works afterwards; this
frees nothing.


```teal
function tecs.assets.shutdown()
```

#### Arguments

None.

#### Returns

None.

<a id="tecs.assets.update"></a>
### tecs.assets.update <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Takes finished loads and settles their futures. Call once per frame.

Polls, never blocks, and drains everything the worker has answered rather than one
result. Nothing settles without this, so a game that stops calling it has loads that
stay pending forever.



```teal
function tecs.assets.update(): integer
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `integer` | How many loads settled this call, which is zero when nothing has finished and also zero when no worker exists. |

<a id="tecs.assets.waitAll"></a>
### tecs.assets.waitAll <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Blocks until every queued load has finished.

For startup and tests. A frame should poll `update` instead.

Not a join over futures a caller holds, and no join expresses it: this waits
on every load in this process, including ones a subsystem the caller has
never heard of started, which is what a reload or a startup path wants.



```teal
function tecs.assets.waitAll(timeoutMs: number)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `timeoutMs` | `number` | Wall-clock milliseconds, defaulting to 5000. Running out is not an error and is not reported, so read `assets.pending` afterwards to tell the two endings apart. |

#### Returns

None.