DocsAPI reference

cinder.assets

cinder.assets #

Source: cinder/assets/__init__.mojo

Assets: glTF import into engine meshes, materials, textures, skins and scenes.

cinder.assets.cook #

Source: cinder/assets/cook.mojo

The asset cooker: glTF sources become cooked meshes, materials and animation clips. Cooking is incremental and keyed on content: the manifest (manifest.txt in the output directory) records, per logical asset, the FNV-1a hash of the source file and the output file name. A source whose hash is unchanged and whose outputs still exist is skipped. Output files are named by the asset id of their logical path, so ids are stable across cooks and machines. Textures are cooked by cook_textures (KTX2) in the native-backed path; this module records the image references materials need (<prefix>#image<N>).

struct ManifestEntry #

mojo
struct ManifestEntry(Copyable, Movable)

One manifest line: logical asset path, FNV-1a source hash (hex64) and output file name. file is relative to the output directory and named by asset_file_name(logical, ...).

Fields:

  • logical: String
  • source_hash: String
  • file: String

struct CookManifest #

mojo
struct CookManifest(Movable)

The cook manifest (<out_dir>/manifest.txt), shared by the cooker and AssetServer. One entry per logical asset; lookups are linear scans. Re-cooking a source drops (prune) the entries of outputs it no longer produces.

Fields:

  • entries: List[ManifestEntry]

  • __init__ __init__(out self)

  • load load(path: String) raises -> CookManifest
    Reads a manifest; a missing file yields an empty one. Lines without exactly three space-separated fields (<logical> <hash> <file>) are skipped, so logical paths must not contain spaces. Raises when the file exists but cannot be read.

  • save save(self, path: String) raises
    Writes a <logical> <hash> <file> line per entry, replacing path; raises on error.

  • find find(self, logical: String) -> Int
    Index of the entry for logical, or -1.

  • record record(mut self, logical: String, source_hash: String, file: String)
    Replaces the entry for logical, or appends one when there is none.

  • prune prune( mut self, root: String, produced: List[String] ) -> List[ManifestEntry]
    Removes the entries under root (logical paths starting with it, root itself excepted) that are not in produced, and returns them in manifest order.

  • up_to_date up_to_date( self, logical: String, source_hash: String, out_dir: String ) -> Bool
    Whether logical is recorded with source_hash and its file exists in out_dir.

struct CookReport #

mojo
struct CookReport(Movable)

What one cook_gltf call did. cooked counts outputs written; skipped counts outputs found up to date when the whole source was skipped (the two never mix in one report).

Fields:

  • cooked: Int

  • skipped: Int

  • outputs: List[String] — Logical paths produced (or found up to date), in cook order.

  • __init__ __init__(out self)

def cook_gltf #

mojo
def cook_gltf( lib: NativePtr, source_path: String, prefix: String, out_dir: String, mut manifest: CookManifest, ) raises -> CookReport

Cooks every mesh primitive, material and animation of a glTF file. prefix is the logical name of the source (e.g. models/Fox); outputs are <prefix>#mesh<i>/prim<p>, <prefix>#material<m> and <prefix>#anim<a>.

cinder.assets.cooked #

Source: cinder/assets/cooked.mojo

Cooked asset formats (little-endian binary, versioned, bounds-checked on read). - .cmesh: engine vertex stream (56-byte vertices), optional skin stream, u32 indices with LOD ranges, bounds, morph deltas. Written by the cooker from glTF primitives (tangents generated, LODs built with meshoptimizer), loaded by AssetServer without any further processing. - .cclip: an animation clip (channels of keyframes targeting node indices). Asset ids are the FNV-1a hash of the logical asset path (models/Fox.glb#mesh0/prim1), so they are stable across cooks and machines.

  • comptime MESH_MAGIC = "CMSH" — First four bytes of a .cmesh file.
  • comptime MESH_VERSION = 1 — .cmesh layout version; read_mesh rejects any other (re-cook, there are no migrations).
  • comptime CLIP_MAGIC = "CCLP" — First four bytes of a .cclip file.
  • comptime CLIP_VERSION = 1 — .cclip layout version; read_clip rejects any other.

def asset_id #

mojo
def asset_id(logical_path: String) -> UInt64

Stable 64-bit id of a logical asset path: its FNV-1a hash.

def asset_file_name #

mojo
def asset_file_name(logical_path: String, extension: String) -> String

<16 hex digits>.<extension>: content-addressed by logical path, not by contents.

struct CookedMesh #

mojo
struct CookedMesh(Movable)

What .cmesh holds. Vertex and index bytes are already in GPU layout.

Fields:

  • vertex_count: UInt32

  • vertex_bytes: List[UInt8]

  • skin_bytes: List[UInt8]

  • index_bytes: List[UInt8]

  • lods: List[MeshLod]

  • bounds_min: Vec3

  • bounds_max: Vec3

  • morph_target_count: UInt32

  • morph_bytes: List[UInt8]

  • material: String — Logical path of the cooked material, or empty.

  • __init__ __init__(out self)

def cook_mesh #

mojo
def cook_mesh( data: MeshData, lod_indices: List[List[UInt32]], material: String ) -> CookedMesh

Packs a validated MeshData and its LOD index lists.

def write_mesh #

mojo
def write_mesh(mesh: CookedMesh) -> List[UInt8]

Serialises a CookedMesh to .cmesh bytes (version MESH_VERSION, little-endian). Layout: magic, version, vertex count, byte lengths of the vertex, skin and index streams, LOD count, bounds min and max, morph target count and byte length, material path (u32 length + bytes), the LOD table (first index, index count, error), then the vertex, skin, index and morph streams.

struct Cursor #

mojo
struct Cursor(Movable)

Bounds-checked little-endian reader over an owned byte buffer (the cooked-format readers). Every read raises when it would run past the end of the data.

Fields:

  • data: List[UInt8]

  • pos: Int

  • __init__ __init__(out self, var data: List[UInt8])

  • remaining remaining(self) -> Int
    Bytes left after the read position.

  • u8 u8(mut self) raises -> UInt8
    Reads one byte; raises at the end of the data.

  • u32 u32(mut self) raises -> UInt32
    Reads a little-endian u32; raises when fewer than 4 bytes remain.

  • f32 f32(mut self) raises -> Float32
    Reads a float32 from the bit pattern of a little-endian u32.

  • bytes bytes(mut self, count: Int) raises -> List[UInt8]
    Copies the next count bytes; raises if count is negative or above remaining().

  • string string(mut self) raises -> String
    Reads a u32 byte length and that many bytes of UTF-8 text; raises when the length exceeds the data or the bytes are not valid UTF-8.

  • magic magic(mut self, expected: String) raises
    Reads the bytes of expected; raises bad magic at the first mismatch.

def read_mesh #

mojo
def read_mesh(var data: List[UInt8]) raises -> CookedMesh

Parses .cmesh bytes into a CookedMesh, taking ownership of data. Raises on a wrong magic or version; stream lengths that disagree with the vertex count (56 bytes per vertex, 24 per skin vertex, 32 per vertex and morph target); index bytes not a multiple of 4; a LOD count outside 1..16; a LOD range outside the index buffer or not a multiple of 3; any index >= vertex count; truncated data or trailing bytes.

struct ClipChannel #

mojo
struct ClipChannel(Copyable, Movable)

Keyframes for one node property. path: 0 translation, 1 rotation, 2 scale, 3 weights. interpolation: 0 linear, 1 step, 2 cubic spline. values holds components floats per key (3 per cubic-spline key: in-tangent, value, out-tangent).

Fields:

  • node: Int
  • path: Int
  • interpolation: Int
  • components: Int
  • times: List[Float32]
  • values: List[Float32]

struct Clip #

mojo
struct Clip(Copyable, Movable)

A cooked animation clip: name, duration (the latest keyframe time) and its channels.

Fields:

  • name: String

  • duration: Float32

  • channels: List[ClipChannel]

  • __init__ __init__(out self, name: String)

def write_clip #

mojo
def write_clip(clip: Clip) -> List[UInt8]

Serialises a clip to .cclip bytes (version CLIP_VERSION, little-endian). Layout: magic, version, name, duration, channel count, then per channel its node, path, interpolation, components, key count and value count followed by the times and values.

def read_clip #

mojo
def read_clip(var data: List[UInt8]) raises -> Clip

Parses .cclip bytes into a Clip, taking ownership of data. Raises on a wrong magic or version, more than 100000 channels, a path above 3, an interpolation above 2, components outside 1..64, a value count other than keys * components (* 3 for cubic spline), decreasing keyframe times, truncated data or trailing bytes. Node indices are not checked.

  • comptime MATERIAL_MAGIC = "CMAT" — First four bytes of a .cmat file.
  • comptime MATERIAL_VERSION = 1 — .cmat layout version; read_material rejects any other.

struct CookedMaterial #

mojo
struct CookedMaterial(Copyable, Movable)

Material parameters plus the logical image path per map slot (empty when absent).

Fields:

  • material: Material

  • images: Array[String, 5]

  • linear_filter: Bool

  • address_mode: UInt32

  • __init__ __init__(out self, var material: Material)

def write_material #

mojo
def write_material(cm: CookedMaterial) -> List[UInt8]

Serialises a CookedMaterial to .cmat bytes (version MATERIAL_VERSION, little-endian). Layout: magic, version, name, factors, alpha mode and cutoff, double-sided flag, UV transform, then per map slot (5) the texcoord set and image path, then the sampler's linear-filter flag and address mode.

def read_material #

mojo
def read_material(var data: List[UInt8]) raises -> CookedMaterial

Parses .cmat bytes into a CookedMaterial, taking ownership of data. Raises on a wrong magic or version, an alpha mode above 2, truncated data or trailing bytes. Only the parameters and image paths are read; AssetServer resolves the textures and creates the sampler.

cinder.assets.gltf #

Source: cinder/assets/gltf.mojo

glTF 2.0 import through cgltf: meshes (with tangents, second UV set, skinning, morph targets), metallic-roughness materials (KHR_texture_transform, KHR_materials_emissive_strength), images (file, data URI or GLB buffer view), nodes, skins and scenes. load_gltf produces a CPU-side GltfAsset; instantiate uploads it through a Renderer and fills a RenderScene with one instance per (node, primitive), joints at the bind pose and the default morph weights. Malformed files raise; they never crash (cgltf validates the structure and every accessor read is bounds-checked by cgltf).

struct GltfImage #

mojo
struct GltfImage(Copyable, Movable)

An image decoded on the CPU by load_gltf, with its glTF name (empty when unnamed).

Fields:

  • name: String
  • image: Image

struct GltfTexture #

mojo
struct GltfTexture(ImplicitlyCopyable, Movable)

A glTF texture: image index (-1 without one) plus sampler settings. linear_filter is True for a linear or unspecified magnification filter; address_mode is the WGPU address mode from the sampler's wrap_s (repeat when there is no sampler).

Fields:

  • image: Int
  • linear_filter: Bool
  • address_mode: UInt32

struct GltfMaterial #

mojo
struct GltfMaterial(Copyable, Movable)

Engine material parameters plus the asset texture index per map (-1 when absent).

Fields:

  • material: Material

  • textures: Array[Int, 5]

  • __init__ __init__(out self, var material: Material)

struct GltfPrimitive #

mojo
struct GltfPrimitive(Copyable, Movable)

A triangle primitive as validated MeshData and its material index (-1 when none). Point, line and strip primitives are dropped on import.

Fields:

  • mesh: MeshData
  • material: Int

struct GltfMesh #

mojo
struct GltfMesh(Copyable, Movable)

A glTF mesh: name, its triangle primitives and the mesh's default morph weights.

Fields:

  • name: String
  • primitives: List[GltfPrimitive]
  • weights: List[Float32]

struct GltfNode #

mojo
struct GltfNode(Copyable, Movable)

A scene node; every reference is an index into GltfAsset, -1 when absent. local and world are computed by cgltf at import (the world matrix walks the parents); weights are the node's morph weight overrides (empty when it has none).

Fields:

  • name: String
  • parent: Int
  • children: List[Int]
  • mesh: Int
  • skin: Int
  • local: Mat4
  • world: Mat4
  • weights: List[Float32]

struct GltfSkin #

mojo
struct GltfSkin(Copyable, Movable)

Joint node indices and an inverse bind matrix each (identity where the file has none).

Fields:

  • joints: List[Int]
  • inverse_bind: List[Mat4]

struct GltfChannel #

mojo
struct GltfChannel(Copyable, Movable)

One animated node property. path: 0 translation, 1 rotation, 2 scale, 3 weights; interpolation: 0 linear, 1 step, 2 cubic spline (values hold in-tangent, value, out-tangent).

Fields:

  • node: Int
  • path: Int
  • interpolation: Int
  • components: Int
  • times: List[Float32]
  • values: List[Float32]

struct GltfAnimation #

mojo
struct GltfAnimation(Copyable, Movable)

An animation: name, duration (latest keyframe time over its channels) and channels. Channels without a sampler, target node or supported target path are skipped on import.

Fields:

  • name: String

  • duration: Float32

  • channels: List[GltfChannel]

  • __init__ __init__(out self, name: String)

struct SceneBounds #

mojo
struct SceneBounds(ImplicitlyCopyable, Movable)

Axis-aligned box from GltfAsset.world_bounds; all zero when the asset has no meshes.

Fields:

  • min: Vec3

  • max: Vec3

  • centre centre(self) -> Vec3
    Midpoint of the box.

  • radius radius(self) -> Float32
    Half the diagonal: the radius of the sphere around centre that encloses the box.

struct GltfAsset #

mojo
struct GltfAsset(Movable)

Everything load_gltf imported from one file, CPU side; cross references are list indices. roots holds the default scene's root nodes, or every parentless node when the file has no default scene.

Fields:

  • path: String

  • images: List[GltfImage]

  • textures: List[GltfTexture]

  • materials: List[GltfMaterial]

  • meshes: List[GltfMesh]

  • nodes: List[GltfNode]

  • skins: List[GltfSkin]

  • animations: List[GltfAnimation]

  • roots: List[Int]

  • __init__ __init__(out self, path: String)

  • world_bounds world_bounds(self) -> SceneBounds
    Axis-aligned bounds of every mesh instance in world space. Skinned meshes are posed on the CPU with their bind-pose joint matrices (what the GPU draws at rest).

def load_gltf #

mojo
def load_gltf(lib: NativePtr, path: String) raises -> GltfAsset

Parses a .gltf or .glb file (and its buffers and images) into a GltfAsset.

struct UploadedGltf #

mojo
struct UploadedGltf(Movable)

Renderer ids for an uploaded asset: mesh_ids[mesh][primitive], material_ids[material] and a default material for primitives without one.

Fields:

  • mesh_ids: List[List[Int]]

  • material_ids: List[Int]

  • default_material: Int

  • __init__ __init__(out self)

def upload_gltf #

mojo
def upload_gltf( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, lods: Bool = True, ) raises -> UploadedGltf

Uploads every image, material and primitive of asset to the renderer. Base colour and emissive images are sRGB; the rest are linear. Materials without textures share the renderer's default textures.

def instantiate #

mojo
def instantiate( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut scene: RenderScene, lods: Bool = True, ) raises -> Int

Uploads the asset and appends its instances, joints and morph weights to scene. Returns the number of instances added. Base colour and emissive images are sRGB; the rest are linear. Materials without textures share the renderer's default textures.

cinder.assets.runtime #

Source: cinder/assets/runtime.mojo

Runtime asset server: asynchronous loading of cooked assets on the job system, reference counting, and hot reload. Loading a cooked mesh has two halves: a job reads and parses the file (CPU only, any thread) into a heap slot; update() on the main thread uploads finished slots to the GPU through the renderer and flips the handle to ready. Nothing GPU-related happens off the main thread. Each logical asset is loaded once and shared; release drops a reference and frees the GPU mesh when the last one goes. check_for_changes reloads assets whose cooked file changed on disk and swaps the GPU mesh (or material) under the same renderer index, so instances keep working.

struct AssetState #

mojo
struct AssetState(Equatable, ImplicitlyCopyable, RegisterPassable)

Load state of an AssetServer record: loading, ready or failed.

Fields:

  • value: Int

  • loading loading() -> AssetState
    Load started and not finished; a material also stays here while its textures load.

  • ready ready() -> AssetState
    Loaded; the accessors return data.

  • failed failed() -> AssetState
    The load failed (see AssetServer.error) or the record was released.

struct AssetRecord #

mojo
struct AssetRecord(Movable)

One logical asset known to an AssetServer and its loaded result. kind is 0 mesh, 1 clip, 2 texture, 3 material. Records are never removed: releasing the last reference leaves the record with state failed and error released (and an empty logical, so loading the path again makes a new record).

Fields:

  • logical: String

  • file: String

  • kind: Int

  • state: AssetState

  • refcount: Int

  • mesh_index: Int — Renderer mesh index once ready (meshes).

  • clip: Optional[Clip]

  • texture_view: TextureViewHandle

  • texture: TextureHandle

  • material_index: Int — Renderer material index once built (materials); kept across hot reloads.

  • sampler: SamplerHandle — Material: the sampler the server created for it (null until built).

  • cooked_material: Optional[CookedMaterial]

  • pending_textures: List[Int] — Material: asset indices of textures that must be ready before the material is built (one reference each, held until the material is released or reloaded).

  • retired_textures: List[Int] — Material being hot reloaded: the previous version's texture references, released once the new version is built (the renderer still draws the old one until then).

  • error: String

  • mtime: Int

  • slot_index: Int

  • reloads: Int

  • __init__ __init__(out self, logical: String, file: String, kind: Int)

struct AssetServer #

mojo
struct AssetServer(Movable)

Loads cooked assets listed in <root>/manifest.txt, shared by logical path and refcounted. Jobs only read, parse and transcode files; update does every GPU upload. All methods are main-thread calls (there are no locks). Indices from load_* stay valid for the server's lifetime. Dropping the server waits for all jobs but does not free the GPU resources of assets that were never released.

Fields:

  • lib: NativePtr

  • jobs: Pointer[JobSystem, MutUntrackedOrigin]

  • renderer: Pointer[Renderer, MutUntrackedOrigin]

  • root: String

  • manifest: CookManifest

  • records: List[AssetRecord]

  • counter: Counter

  • in_flight: Int

  • slots: List[List[_LoadSlot]] — Load slots as single-element lists: their heap storage never moves, so jobs can hold pointers into them while this list grows.

  • slot_busy: List[Bool]

  • __init__ __init__( out self, lib: NativePtr, jobs: Pointer[JobSystem, MutUntrackedOrigin], renderer: Pointer[Renderer, MutUntrackedOrigin], root: String, ) raises

  • load_mesh load_mesh(mut self, logical: String) raises -> Int
    Returns the asset index (also a reference). Loading starts immediately on a job. Raises when logical is not in the manifest or is known as another kind of asset. A known path returns its existing record (refcount + 1); one whose load failed is loaded again.

  • load_clip load_clip(mut self, logical: String) raises -> Int
    Returns the asset index of a cooked clip (also a reference); a job parses the file. Same sharing, kind check and retry of failed loads as load_mesh.

  • load_texture load_texture(mut self, logical: String) raises -> Int
    Returns the asset index of a cooked KTX2 texture (also a reference). The job reads and transcodes it (BC7 when the device supports block compression, else RGBA8); update uploads it. Same sharing, kind check and retry of failed loads as load_mesh.

  • load_material load_material(mut self, logical: String) raises -> Int
    Loads a cooked material and every texture it references; ready when all of them are. Same sharing, kind check and retry of failed loads as load_mesh.

  • material_index material_index(self, index: Int) raises -> Int
    Renderer material index of a ready material; raises otherwise.

  • texture_view texture_view(self, index: Int) raises -> TextureViewHandle
    View of a ready texture; raises otherwise. A hot reload destroys it for a new one.

  • update update(mut self) raises -> Int
    Main thread: finishes loads whose job completed (GPU upload for meshes). Returns how many assets became ready or failed this call.

  • wait_all wait_all(mut self) raises
    Blocks until every pending load is applied (tests, loading screens).

  • state state(self, index: Int) -> AssetState
    State of the record at index (failed once released).

  • mesh_index mesh_index(self, index: Int) raises -> Int
    Renderer mesh index of a ready asset, stable across hot reloads; raises when not ready. The kind is not checked: a ready asset that is not a mesh returns -1.

  • clip clip(self, index: Int) raises -> Clip
    A copy of a ready clip with all its keyframes; raises when not a ready clip.

  • error error(self, index: Int) -> String
    Message of a failed load (released after the last release), empty otherwise.

  • refcount refcount(self, index: Int) -> Int
    Outstanding references to the record at index; 0 once released.

  • release release(mut self, index: Int) raises
    Drops one reference; the last one frees what the asset holds on the GPU (mesh, texture, renderer material and its sampler), releases a material's textures and forgets the asset.

  • check_for_changes check_for_changes(mut self) -> Int
    Starts a reload for every ready asset whose cooked file changed. Returns how many.

  • live_count live_count(self) -> Int
    Number of records with a positive refcount.

cinder.assets.textures #

Source: cinder/assets/textures.mojo

KTX2 textures: cooking (BasisU ETC1S or UASTC with full mip chains) and runtime loading (transcode to BC7 when the device has block compression, otherwise to RGBA8), through libktx.

  • comptime VK_FORMAT_R8G8B8A8_UNORM: UInt32 = 37 — Vulkan format id for linear RGBA8: written for non-sRGB images, accepted when uncompressed.
  • comptime VK_FORMAT_R8G8B8A8_SRGB: UInt32 = 43 — Vulkan format id for sRGB RGBA8: written for sRGB images and read back as "is sRGB".
  • comptime VK_FORMAT_BC7_SRGB_BLOCK: UInt32 = 146 — Vulkan format id libktx reports after transcoding an sRGB texture to BC7.

def cook_texture_ktx2 #

mojo
def cook_texture_ktx2( lib: NativePtr, img: Image, path: String, srgb: Bool, normal_map: Bool, uastc: Bool, quality: Int = 128, ) raises

Writes img with a full mip chain as a BasisU-compressed KTX2 file. uastc picks the high-quality UASTC codec (normal maps, detail textures); otherwise ETC1S at quality (1..255). sRGB images carry the sRGB transfer function in the data format descriptor so loaders choose an sRGB GPU format.

struct LoadedTexture #

mojo
struct LoadedTexture(Movable)

A KTX2 file transcoded on the CPU: one byte list per mip level, ready for upload_loaded.

Fields:

  • width: Int

  • height: Int

  • levels: List[List[UInt8]]

  • bc7: Bool

  • srgb: Bool

  • __init__ __init__(out self, width: Int, height: Int, bc7: Bool, srgb: Bool)

def load_ktx2 #

mojo
def load_ktx2( lib: NativePtr, path: String, prefer_bc7: Bool ) raises -> LoadedTexture

Reads and transcodes a KTX2 file (CPU only; safe on a job thread).

def upload_loaded #

mojo
def upload_loaded( mut res: Resources, label: String, loaded: LoadedTexture ) raises -> Tuple[TextureHandle, TextureViewHandle]

Creates the GPU texture (BC7 or RGBA8, sRGB variants when flagged) and uploads every level.

View as MarkdownSource: docs/api/assets.md