assetc compiles source assets (textures, meshes, shaders, materials, fonts) into runtime-friendly binary formats for video games. Supports octahedral-packed normals/tangents and meshletized geometry; mesh files reference materials by stable 64-bit hashes so the engine can share resources across assets. Mesh additionally emits a companion material table plus one content-addressed .ktx2 per referenced image. Embedded textures are written to a shared flat store keyed by content, so the same texture used by two different models is encoded once and shares one runtime handle.
Texture refs are stored as 64-bit hashes, not paths. A single global manifest maps each hash back to the file on disk. Load it once into a hash → path map and you can resolve any asset by a stable id - textures (baseColorTex etc.), but also meshes, materials, animations, fonts, shaders, and embeds - through one uniform lookup.
Everything assetc writes has a detailed spec under docs/spec/, and every feature under docs/features/. Games read the output with the assetc crate, a small runtime SDK with no dependencies.
The Output tab inspects what the build wrote: here Fox.amesh.bin with its meshlet bounds, levels of detail, skin and locators.
| Command | What it does |
|---|---|
assetc |
Open the GUI for the project containing the current directory. |
assetc init |
Write a starter assetc.toml in the current directory (never overwrites one). |
assetc build |
Compile input into output; --target <name> builds a target instead. |
assetc watch |
Build, then rebuild what changes until stopped (Ctrl+C); --target <name> too. |
assetc pack |
Build, then bundle the output into one .pak file; --target <name> too. |
assetc --help, --version |
Usage, version. |
Like git with .git, assetc finds its project by looking for assetc.toml in the current directory and its parents. See docs/features/cli.md.
assetc watch keeps the output up to date while you edit: after the first build it compiles only the assets whose settings or source files changed (a data file also when a schema changes, a .gltf when any file in its folder does).
| Target | Does |
|---|---|
make run / make build / make release |
cargo run, debug build, release build |
make install |
Release build, copied to ~/bin/assetc |
make demo |
Packs the samples, then shows them in the demo |
make publish-check / make publish |
Dry run / publish of the assetc crate to crates.io |
Pushing a tag v<version> (matching the version in Cargo.toml) builds assetc for Linux x64, macOS Apple silicon and Windows x64 on GitHub Actions and attaches the archives to a GitHub release; see docs/features/release.md. The SDK crate is published by hand (make publish).
Every key is optional; an empty file builds assets/ into build/. Full reference: docs/spec/assetc.toml.md.
input = "samples" # source assets (default "assets")
output = "tmp/build" # compiled assets (default "build")
[textures] # globs over the input-relative path
linear = ["**/*_n.png"] # linear data, not sRGB color
no_mipmaps = ["ui/**"]
volume_luts = [] # .cube LUTs as 3D textures instead of strips
[meshes]
meshlet_max_vertices = 64
meshlet_max_triangles = 124
lod_ratios = [0.5, 0.25] # levels of detail, as fractions of the triangles
[pack]
alignment = 4096 # page-aligned files, for memory mapping
exclude = ["**/*.orm.ktx2"]
[targets.mobile] # assetc build --target mobile -> tmp/build-mobile
textures = { no_mipmaps = [] }
meshes = { lod_ratios = [0.5] }A target is a named variant of the settings: its keys replace the top-level ones (tables merge key by key), and it builds into its own output, <output>-<name> unless it sets one. Every target shares the input.
Per-file settings live next to the file in an .amod sidecar (Fox.glb.amod): a model's mesh settings, locators, properties, shapes, regions, paths, vertex colors and animation events, a texture's modifiers, and attached structs, edited in the GUI's inspector.
| Source | Asset | Output |
|---|---|---|
*.png, *.jpg, *.jpeg, *.tga |
texture | .ktx2 (textures) |
*.anoise |
generated texture: noise and patterns | .ktx2 (noise) |
*.agradient |
generated texture and gradients | .ktx2 + .agradient.bin (gradients) |
*.apalette |
generated texture and named colors | .ktx2 + .apalette.bin (palettes) |
folder *.atlas |
packed texture and frames | .ktx2 + .aatlas.bin (atlases) |
*.exr |
HDR environment cubemap | .ktx2 (HDR) |
*.cube |
color LUT, as a strip or 3D texture | .ktx2 (LUT) |
*.amat and textures by name (brick.jpg, brick.n.jpg, brick.r.jpg, …) |
PBR material, ORM packed | .amat.bin + .ktx2 (materials) |
*.ashader + SPIR-V stages |
shader interface | .ashader.bin (custom materials) |
*.acmat and textures by name |
custom material | .acmat.bin (custom materials) |
*.gltf, *.glb |
mesh with meshlets and LODs, materials, textures, skin, animations | .amesh.bin, .aanim.bin, <model>/… (models) |
*.ttf, *.otf |
SDF font | .afont.bin + .ktx2 atlas (fonts) |
*.aschema |
types for data | .aschema.bin (schemas) |
*.adata |
instances of a schema, resolved | .adata.bin (instances) |
*.agraph |
directed graph | .agraph.bin (graphs) |
*.atree |
tree | .atree.bin (trees) |
Anything else is skipped. For samples/Fox.glb the build writes:
build/Fox.amesh.bin geometry, meshlets, levels of detail, skin, locators
build/Fox.aanim.bin animation clips, with events
build/Fox/… the model's materials (.amat.bin) and textures (.ktx2)
build/assetc.aman.bin every asset ID -> output file (all assets, written once)
Asset IDs are FNV-1a-64 of the asset path (textures/Grass.png -> textures/Grass), so they are stable across builds and source formats. See docs/features/build.md.
Every binary format is little-endian. Most are chunked files: a header naming the format and version, then tagged chunks a reader can skip.
| Format | Spec | Holds |
|---|---|---|
.ktx2 |
ktx2.md | Textures: 2D, cubemaps, volume LUTs, with mipmaps and their usage |
.amesh.bin |
amesh.bin.md | Mesh: vertices with octahedral normals and tangents, meshlets, LODs, skin, skeleton, locators, shapes, regions, paths |
.aanim.bin |
aanim.bin.md | Animation clips of a skinned mesh, with events |
.amat.bin |
amat.bin.md | PBR material: factors and texture IDs |
.ashader.bin |
ashader.bin.md | Shader interface: params block layout, textures, render state, SPIR-V stages |
.acmat.bin |
acmat.bin.md | Custom material: shader ID, GPU block bytes, texture IDs |
.afont.bin |
afont.bin.md | Font metrics, glyphs and kerning beside an SDF atlas |
.aatlas.bin |
aatlas.bin.md | Atlas frames by name |
.agradient.bin |
agradient.bin.md | Gradients by name, to sample at runtime |
.apalette.bin |
apalette.bin.md | Colors by name or index |
.aschema.bin |
aschema.bin.md | Types, fields and enums |
.adata.bin |
adata.bin.md | Instances with every field resolved |
.agraph.bin |
agraph.bin.md | Directed graph: nodes, edges, their data |
.atree.bin |
atree.bin.md | Tree, nodes breadth-first |
assetc.aman.bin |
aman.bin.md | Manifest: every asset ID -> output file |
.pak |
pak.md | The whole output in one file, each asset readable (or memory-mapped) in place |
Many assets can carry attached structs: values of your own schema types on the asset, a node, an edge or a color (astruct.md).
The source formats assetc reads and its GUI writes are specified too: .amat, .acmat, .ashader, .amod, .anoise, .agradient, .apalette, atlas.toml, and the data files of the a/1 property format (.aschema, .adata, .agraph, .atree).
assetc with no arguments opens the editor: the input and output trees on the left, a 2D/3D preview in the middle and the inspector on the right.
- Previews inputs and outputs: textures with their channels, models with locators and levels of detail, materials on a sphere or other shapes, fonts, atlases, graphs and trees.
- Edits the source files it understands: materials, custom materials and shaders, noise, gradients, palettes, schemas, data instances, graphs, trees, and
.amodsettings (locators, properties, shapes, regions, paths, animation events). - Creates, renames, deletes and moves files (drag and drop, with their companion files and with paths fixed across the project).
- Builds with
Ctrl+Band marks each file's build result in the tree.
Keybindings: KEYS.md. Layout and look: docs/features/gui.md.
sdk/ is the assetc crate on crates.io: the runtime side of assetc, for the game. It reads every output format above, from a .pak or an output directory, into plain Rust values. It depends on nothing but std: no Bevy, glTF, meshoptimizer, image or shader crates, and none of the compiler's code.
[dependencies]
assetc = "0.2"use assetc::{Assets, Kind, Mesh, Texture};
let assets = Assets::open("build/assetc.pak")?; // or the output directory
for entry in assets.of_kind(Kind::Mesh) {
let mesh: Mesh = assets.load(entry.id)?; // by asset ID
println!("{}: {} vertices", entry.path, mesh.vertices.len());
}
let texture: Texture = assets.load_path("ui/fire.ktx2")?;The compiler itself is the unpublished assetc-core package at the repository root (its binary is assetc). See docs/features/sdk.md and sdk/README.md.
samples/ is a project with an input of every kind, used by the tests, the screenshots and the demo. Most of it was made for assetc; the third-party assets (the Khronos Fox model, the monogram font, …) and their licenses are listed in NOTICE.md.
make demo packs the samples and opens demo/, a small Bevy app that shows every sample through the SDK only, the way a game would load them. See docs/features/demo.md.







