On this page
  1. tecs.assets
  2. Lifetime
  3. Module contents
    1. Constructors
    2. Types
    3. Functions
  4. Constructors
    1. newMesh
  5. Types
    1. Image
    2. Mesh
    3. MeshOptions
    4. Model
    5. ModelDraw
    6. ModelMaterial
    7. Sound
  6. Functions
    1. install
    2. installed
    3. loadGLTF
    4. loadImage
    5. loadSound
    6. loadString
    7. pending
    8. shutdown
    9. update
    10. waitAll

tecs.assets

Reads bytes and decodes images and sounds on a worker.

Each load returns a Future immediately:

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 installs and shuts down the process-wide worker, and 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 Builds procedural mesh data in the renderer's fixed vertex layout.

Types

Type Kind Description
Image record Represents decoded pixels and the caller's hold on them.
Mesh record Describes one immutable indexed triangle mesh before GPU registration.
MeshOptions record Supplies procedural geometry to newMesh.
Model record Represents one decoded static glTF 2.0 scene.
ModelDraw record Describes one static primitive instance in a decoded glTF scene.
ModelMaterial record Describes one decoded glTF material before GPU registration.
Sound record Represents a loaded clip and the caller's hold on it.

Functions

Function Kind Description
install Static Starts the loading worker.
installed Static Reports whether the loading worker is running.
loadGLTF Static Queues static glTF 2.0 or GLB decoding on the asset worker.
loadImage Static Queues an image for loading and answers a future for it immediately.
loadSound Static Queues a sound for loading and answers a future for it immediately.
loadString Static Queues a complete file read and answers a future for its bytes immediately.
pending Static Returns the number of loads still waiting on the worker.
shutdown Static Stops the loading worker.
update Static Takes finished loads and settles their futures.
waitAll Static Blocks until every queued load has finished.

Constructors

tecs.assets.newMesh Static

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

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

Arguments

Name Type Description
options MeshOptions The caller supplies a name, interleaved vertices, and zero-based triangle indices.

Returns

Type Description
Mesh Returns CPU geometry that renderer.meshes:registerMesh consumes or the caller releases.

Types

tecs.assets.Image record

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.

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

    release: function(self)
end

tecs.assets.Image.path field

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

tecs.assets.Image.path: string

tecs.assets.Image.pixels field

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

tecs.assets.Image.pixels: loader.CValue

tecs.assets.Image.width field

Read-only. Reports the width in pixels.

tecs.assets.Image.width: integer

tecs.assets.Image.height field

Read-only. Reports the height in pixel rows.

tecs.assets.Image.height: integer

tecs.assets.Image.pitch field

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

tecs.assets.Image.pitch: integer

tecs.assets.Image:release Instance

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.

function tecs.assets.Image.release(self)
Arguments
Name Type Description
self Image
Returns

None.

tecs.assets.Mesh record

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.

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

tecs.assets.Mesh.name field

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

tecs.assets.Mesh.name: string

tecs.assets.Mesh.vertices field

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

tecs.assets.Mesh.vertices: loader.CArray

tecs.assets.Mesh.indices field

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

tecs.assets.Mesh.indices: loader.CArray

tecs.assets.Mesh.vertexCount field

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

tecs.assets.Mesh.vertexCount: integer

tecs.assets.Mesh.indexCount field

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

tecs.assets.Mesh.indexCount: integer

tecs.assets.Mesh.centerX field

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

tecs.assets.Mesh.centerX: number

tecs.assets.Mesh.centerY field

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

tecs.assets.Mesh.centerY: number

tecs.assets.Mesh.centerZ field

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

tecs.assets.Mesh.centerZ: number

tecs.assets.Mesh.radius field

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

tecs.assets.Mesh.radius: number

tecs.assets.Mesh:release Instance

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

function tecs.assets.Mesh.release(self)
Arguments
Name Type Description
self Mesh
Returns

None.

tecs.assets.MeshOptions record

Supplies procedural geometry to newMesh.

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

tecs.assets.MeshOptions.name field

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

tecs.assets.MeshOptions.name: string

tecs.assets.MeshOptions.vertices field

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

tecs.assets.MeshOptions.vertices: {number}

tecs.assets.MeshOptions.indices field

Caller-writable. Supplies zero-based triangle indices.

tecs.assets.MeshOptions.indices: {integer}

tecs.assets.Model record

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.

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

    release: function(self)
end

tecs.assets.Model.path field

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

tecs.assets.Model.path: string

tecs.assets.Model.meshes field

Read-only. Contains unique decoded primitive geometry.

tecs.assets.Model.meshes: {Mesh}

tecs.assets.Model.images field

Read-only. Contains unique decoded source images.

tecs.assets.Model.images: {Image}

tecs.assets.Model.materials field

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

tecs.assets.Model.draws field

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

tecs.assets.Model.draws: {ModelDraw}

tecs.assets.Model:release Instance

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

function tecs.assets.Model.release(self)
Arguments
Name Type Description
self Model
Returns

None.

tecs.assets.ModelDraw record

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

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

tecs.assets.ModelDraw.mesh field

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

tecs.assets.ModelDraw.mesh: integer

tecs.assets.ModelDraw.material field

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

tecs.assets.ModelDraw.material: integer

tecs.assets.ModelDraw.x field

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

tecs.assets.ModelDraw.x: number

tecs.assets.ModelDraw.y field

Read-only. Reports world translation y.

tecs.assets.ModelDraw.y: number

tecs.assets.ModelDraw.z field

Read-only. Reports world translation z.

tecs.assets.ModelDraw.z: number

tecs.assets.ModelDraw.rotationX field

Read-only. Reports world quaternion x.

tecs.assets.ModelDraw.rotationX: number

tecs.assets.ModelDraw.rotationY field

Read-only. Reports world quaternion y.

tecs.assets.ModelDraw.rotationY: number

tecs.assets.ModelDraw.rotationZ field

Read-only. Reports world quaternion z.

tecs.assets.ModelDraw.rotationZ: number

tecs.assets.ModelDraw.rotationW field

Read-only. Reports world quaternion w.

tecs.assets.ModelDraw.rotationW: number

tecs.assets.ModelDraw.scaleX field

Read-only. Reports world scale x.

tecs.assets.ModelDraw.scaleX: number

tecs.assets.ModelDraw.scaleY field

Read-only. Reports world scale y.

tecs.assets.ModelDraw.scaleY: number

tecs.assets.ModelDraw.scaleZ field

Read-only. Reports world scale z.

tecs.assets.ModelDraw.scaleZ: number

tecs.assets.ModelMaterial record

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

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

tecs.assets.ModelMaterial.name field

Read-only. Contains the stable material name.

tecs.assets.ModelMaterial.name: string

tecs.assets.ModelMaterial.model field

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

tecs.assets.ModelMaterial.model: integer

tecs.assets.ModelMaterial.alphaMode field

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

tecs.assets.ModelMaterial.alphaMode: integer

tecs.assets.ModelMaterial.baseColorImage field

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

tecs.assets.ModelMaterial.baseColorImage: integer

tecs.assets.ModelMaterial.normalImage field

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

tecs.assets.ModelMaterial.normalImage: integer

tecs.assets.ModelMaterial.metallicRoughnessImage field

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

tecs.assets.ModelMaterial.occlusionImage field

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

tecs.assets.ModelMaterial.occlusionImage: integer

tecs.assets.ModelMaterial.emissiveImage field

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

tecs.assets.ModelMaterial.emissiveImage: integer

tecs.assets.ModelMaterial.alphaCutoff field

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

tecs.assets.ModelMaterial.alphaCutoff: number

tecs.assets.ModelMaterial.baseR field

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

tecs.assets.ModelMaterial.baseR: number

tecs.assets.ModelMaterial.baseG field

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

tecs.assets.ModelMaterial.baseG: number

tecs.assets.ModelMaterial.baseB field

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

tecs.assets.ModelMaterial.baseB: number

tecs.assets.ModelMaterial.baseA field

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

tecs.assets.ModelMaterial.baseA: number

tecs.assets.ModelMaterial.emissiveR field

Read-only. Reports the emissive red factor.

tecs.assets.ModelMaterial.emissiveR: number

tecs.assets.ModelMaterial.emissiveG field

Read-only. Reports the emissive green factor.

tecs.assets.ModelMaterial.emissiveG: number

tecs.assets.ModelMaterial.emissiveB field

Read-only. Reports the emissive blue factor.

tecs.assets.ModelMaterial.emissiveB: number

tecs.assets.ModelMaterial.metallic field

Read-only. Reports the metallic factor.

tecs.assets.ModelMaterial.metallic: number

tecs.assets.ModelMaterial.roughness field

Read-only. Reports the roughness factor.

tecs.assets.ModelMaterial.roughness: number

tecs.assets.ModelMaterial.normalScale field

Read-only. Reports tangent-space normal strength.

tecs.assets.ModelMaterial.normalScale: number

tecs.assets.ModelMaterial.occlusionStrength field

Read-only. Reports sampled occlusion strength.

tecs.assets.ModelMaterial.occlusionStrength: number

tecs.assets.Sound record

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.

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

    release: function(self)
end

tecs.assets.Sound.path field

Read-only. Contains the requested path unchanged.

tecs.assets.Sound.path: string

tecs.assets.Sound.audio field

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

tecs.assets.Sound.audio: loader.CValue

tecs.assets.Sound.resident field

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

tecs.assets.Sound.resident: boolean

tecs.assets.Sound.durationMs field

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

tecs.assets.Sound.durationMs: integer

tecs.assets.Sound:release Instance

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.

function tecs.assets.Sound.release(self)
Arguments
Name Type Description
self Sound
Returns

None.

Functions

tecs.assets.install Static

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.

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.

tecs.assets.installed Static

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.

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.

tecs.assets.loadGLTF Static

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.

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<Model> Returns a pending future that owns its ready model.

tecs.assets.loadImage Static

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, 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.

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

tecs.assets.loadSound Static

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 it settles to has exactly one holder.

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<Sound> A pending future, or a failed future when mixer initialization fails. This is the only failure reported without waiting for the worker.

tecs.assets.loadString Static

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.

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.
kind string The caller supplies the content kind used by file watching, or omits it to record a document.

Returns

Type Description
Future<string> Returns a pending Future. A missing or unreadable file settles it as failed.

tecs.assets.pending Static

Returns the number of loads still waiting on the worker.

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.

tecs.assets.shutdown Static

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 or a Sound that already settled still works afterwards; this frees nothing.

function tecs.assets.shutdown()

Arguments

None.

Returns

None.

tecs.assets.update Static

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.

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.

tecs.assets.waitAll Static

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.

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.