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 #
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: Stringsource_hash: Stringfile: String
struct CookManifest #
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) -
loadload(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. -
savesave(self, path: String) raises
Writes a<logical> <hash> <file>line per entry, replacingpath; raises on error. -
findfind(self, logical: String) -> Int
Index of the entry forlogical, or -1. -
recordrecord(mut self, logical: String, source_hash: String, file: String)
Replaces the entry forlogical, or appends one when there is none. -
pruneprune( mut self, root: String, produced: List[String] ) -> List[ManifestEntry]
Removes the entries underroot(logical paths starting with it,rootitself excepted) that are not inproduced, and returns them in manifest order. -
up_to_dateup_to_date( self, logical: String, source_hash: String, out_dir: String ) -> Bool
Whetherlogicalis recorded withsource_hashand its file exists inout_dir.
struct CookReport #
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 #
def cook_gltf( lib: NativePtr, source_path: String, prefix: String, out_dir: String, mut manifest: CookManifest, ) raises -> CookReportCooks 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.cmeshfile.comptime MESH_VERSION = 1—.cmeshlayout version;read_meshrejects any other (re-cook, there are no migrations).comptime CLIP_MAGIC = "CCLP"— First four bytes of a.cclipfile.comptime CLIP_VERSION = 1—.ccliplayout version;read_cliprejects any other.
def asset_id #
def asset_id(logical_path: String) -> UInt64Stable 64-bit id of a logical asset path: its FNV-1a hash.
def asset_file_name #
def asset_file_name(logical_path: String, extension: String) -> String<16 hex digits>.<extension>: content-addressed by logical path, not by contents.
struct CookedMesh #
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 #
def cook_mesh( data: MeshData, lod_indices: List[List[UInt32]], material: String ) -> CookedMeshPacks a validated MeshData and its LOD index lists.
def write_mesh #
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 #
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]) -
remainingremaining(self) -> Int
Bytes left after the read position. -
u8u8(mut self) raises -> UInt8
Reads one byte; raises at the end of the data. -
u32u32(mut self) raises -> UInt32
Reads a little-endian u32; raises when fewer than 4 bytes remain. -
f32f32(mut self) raises -> Float32
Reads a float32 from the bit pattern of a little-endian u32. -
bytesbytes(mut self, count: Int) raises -> List[UInt8]
Copies the nextcountbytes; raises ifcountis negative or aboveremaining(). -
stringstring(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. -
magicmagic(mut self, expected: String) raises
Reads the bytes ofexpected; raisesbad magicat the first mismatch.
def read_mesh #
def read_mesh(var data: List[UInt8]) raises -> CookedMeshParses .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 #
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: Intpath: Intinterpolation: Intcomponents: Inttimes: List[Float32]values: List[Float32]
struct Clip #
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 #
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 #
def read_clip(var data: List[UInt8]) raises -> ClipParses .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.cmatfile.comptime MATERIAL_VERSION = 1—.cmatlayout version;read_materialrejects any other.
struct CookedMaterial #
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 #
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 #
def read_material(var data: List[UInt8]) raises -> CookedMaterialParses .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 #
struct GltfImage(Copyable, Movable)An image decoded on the CPU by load_gltf, with its glTF name (empty when unnamed).
Fields:
name: Stringimage: Image
struct GltfTexture #
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: Intlinear_filter: Booladdress_mode: UInt32
struct GltfMaterial #
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 #
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: MeshDatamaterial: Int
struct GltfMesh #
struct GltfMesh(Copyable, Movable)A glTF mesh: name, its triangle primitives and the mesh's default morph weights.
Fields:
name: Stringprimitives: List[GltfPrimitive]weights: List[Float32]
struct GltfNode #
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: Stringparent: Intchildren: List[Int]mesh: Intskin: Intlocal: Mat4world: Mat4weights: List[Float32]
struct GltfSkin #
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 #
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: Intpath: Intinterpolation: Intcomponents: Inttimes: List[Float32]values: List[Float32]
struct GltfAnimation #
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 #
struct SceneBounds(ImplicitlyCopyable, Movable)Axis-aligned box from GltfAsset.world_bounds; all zero when the asset has no meshes.
Fields:
-
min: Vec3 -
max: Vec3 -
centrecentre(self) -> Vec3
Midpoint of the box. -
radiusradius(self) -> Float32
Half the diagonal: the radius of the sphere aroundcentrethat encloses the box.
struct GltfAsset #
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_boundsworld_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 #
def load_gltf(lib: NativePtr, path: String) raises -> GltfAssetParses a .gltf or .glb file (and its buffers and images) into a GltfAsset.
struct UploadedGltf #
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 #
def upload_gltf( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, lods: Bool = True, ) raises -> UploadedGltfUploads 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 #
def instantiate( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut scene: RenderScene, lods: Bool = True, ) raises -> IntUploads 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 #
struct AssetState(Equatable, ImplicitlyCopyable, RegisterPassable)Load state of an AssetServer record: loading, ready or failed.
Fields:
-
value: Int -
loadingloading() -> AssetState
Load started and not finished; a material also stays here while its textures load. -
readyready() -> AssetState
Loaded; the accessors return data. -
failedfailed() -> AssetState
The load failed (seeAssetServer.error) or the record was released.
struct AssetRecord #
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 #
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_meshload_mesh(mut self, logical: String) raises -> Int
Returns the asset index (also a reference). Loading starts immediately on a job. Raises whenlogicalis 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_clipload_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 asload_mesh. -
load_textureload_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);updateuploads it. Same sharing, kind check and retry of failed loads asload_mesh. -
load_materialload_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 asload_mesh. -
material_indexmaterial_index(self, index: Int) raises -> Int
Renderer material index of a ready material; raises otherwise. -
texture_viewtexture_view(self, index: Int) raises -> TextureViewHandle
View of a ready texture; raises otherwise. A hot reload destroys it for a new one. -
updateupdate(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_allwait_all(mut self) raises
Blocks until every pending load is applied (tests, loading screens). -
statestate(self, index: Int) -> AssetState
State of the record atindex(failed once released). -
mesh_indexmesh_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. -
clipclip(self, index: Int) raises -> Clip
A copy of a ready clip with all its keyframes; raises when not a ready clip. -
errorerror(self, index: Int) -> String
Message of a failed load (releasedafter the last release), empty otherwise. -
refcountrefcount(self, index: Int) -> Int
Outstanding references to the record atindex; 0 once released. -
releaserelease(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_changescheck_for_changes(mut self) -> Int
Starts a reload for every ready asset whose cooked file changed. Returns how many. -
live_countlive_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 #
def cook_texture_ktx2( lib: NativePtr, img: Image, path: String, srgb: Bool, normal_map: Bool, uastc: Bool, quality: Int = 128, ) raisesWrites 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 #
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 #
def load_ktx2( lib: NativePtr, path: String, prefer_bc7: Bool ) raises -> LoadedTextureReads and transcodes a KTX2 file (CPU only; safe on a job thread).
def upload_loaded #
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.