On this page
Engine-neutral game assets
Minecraft resource packs are one delivery target. General voxel and low-poly
games use packs[].format: game_assets, which emits GLB models, PNG sprites,
WAV or OGG sound variants, and an engine-neutral assets.json runtime manifest.
A game addresses assets by configured IDs instead of guessing build paths.
The same .ashfox sources and named exports can feed several game packs and
Minecraft packs in one atomic build. This does not introduce another source
language or couple the source compiler to a particular game engine.
Configuration
Download the complete game project and extract it. Its configuration is examples/game-assets/.ashfoxworkspace. Its pack declaration is:
{
"name": "voxel_game",
"format": "game_assets",
"directory": "dist/game",
"archive": true,
"audio": "wav",
"unitsPerMeter": 1,
"pixelsPerUnit": 16,
"spriteFilter": "nearest",
"assets": [
{ "id": "creature.griffin", "source": "griffin", "path": "models/griffin" },
{ "id": "item.iron_sword", "source": "iron_sword", "path": "sprites/items" },
{ "id": "sfx.claw_hit", "source": "claw_hit", "path": "audio/claw_hit" }
]
}
All fields shown are required. source is an export name; id is the runtime
asset ID; path is a relative directory inside the package. IDs must be unique
within the package. File/directory conflicts and duplicate emitted paths fail,
even when the bytes match. Paths cannot escape the package or occupy the reserved
assets.json path. Bindings accept only glb, png, and wav source exports.
Minecraft-specific file trees are not interpreted as generic models.
audio: wav needs no external encoder. audio: ogg converts bound sounds through
the same bounded FFmpeg Vorbis adapter used for Java packs. A pack with no sounds
never invokes FFmpeg. The original WAV exports remain available separately.
The package always emits a folder; archive: true adds a stable ZIP containing
exactly that folder's files, with assets.json at the archive root.
GLB portability and import settings
CLI GLB exports default to encoding: portable. This stores standard float
vertex attributes and skips Meshopt compression, requiring no compression or
mesh-quantization extensions. Embedded textures, meshes, materials, skins and
animation clips remain in one GLB. Static scene assets need no idle animation;
geometry, texture coverage and any authored animation are still validated.
Actor-specific Minecraft targets retain their idle requirement.
A project can explicitly opt into the smaller existing encoding:
{
"name": "griffin",
"entry": { "packageName": "workbench", "entryName": "griffin" },
"format": "glb",
"encoding": "optimized",
"directory": "dist/models"
}
The runtime manifest records the GLB's actual requiredExtensions; an optimized
asset may need Meshopt and mesh-quantization support in the receiving importer.
Use portable output unless you have checked your importer's extension support.
GLB geometry follows the existing glTF exporter: one source u is encoded as
1/16 meter. The model's coordinateSystem: gltf2 describes the encoded basis.
Let the receiving engine's GLB importer handle its axis/handedness conversion.
unitsPerMeter is an import hint for the game, not an extra baked transform; apply
it once if your importer does not already handle that conversion.
For example, use 1 for one engine unit per meter or 100 for centimeter-based units.
Sprite metadata carries pixel dimensions, pixelsPerUnit, and filter.
At 16 pixels per unit, a 16-pixel sprite is one game unit wide. nearest preserves
hard pixel edges; linear requests interpolation. These settings are hints for
the sprite/texture importer. They do not rewrite GLB material samplers or silently
configure an engine project.
Runtime manifest
The returned pack destination contains:
game-assets/
assets.json
models/griffin/griffin.glb
sprites/items/iron_sword.png
audio/claw_hit/<variant>.wav
voxel_game.zip
assets.json has format: ashfox-game-assets, version: 1, and assets ordered
by ID. Every file path is relative to the folder containing that manifest.
There are no absolute paths, compiler receipts or Minecraft registration files
inside the game folder. Build receipts remain in the canonical build bundle.
| Asset kind | Runtime fields |
|---|---|
model |
model file path, glTF coordinate convention, unitsPerMeter, required extensions, clip names and durations |
sprite |
image file path, width, height, pixels per unit and filter |
sound |
codec and named variants with file paths, intended durations, sample rates and channel counts |
Every entry also has id and files with exact byte lengths and SHA-256 hashes.
For OGG, duration is the source's intended duration; decoding may add or remove a
small encoder-padding interval, checked by the encoding adapter.
A web game can resolve the file to hand to its own GLB loader as follows:
const manifestUrl = new URL('./game-assets/assets.json', document.baseURI);
const response = await fetch(manifestUrl);
if (!response.ok) throw new Error(`Asset manifest: HTTP ${response.status}`);
const manifest = await response.json();
if (manifest.format !== 'ashfox-game-assets' || manifest.version !== 1) {
throw new Error('Unsupported asset manifest');
}
const griffin = manifest.assets.find(asset => asset.id === 'creature.griffin');
if (!griffin || griffin.kind !== 'model') throw new Error('Missing griffin model');
const glbUrl = new URL(griffin.model, manifestUrl);
// Pass glbUrl to your engine's GLB importer; use griffin.clips for animation names.
For a complete loader that displays models, plays animation, shows an item and plays sound, follow the web-game example.
Verification and boundaries
npx --no-install ashfox build .ashfoxworkspace --json
npx --no-install ashfox verify dist/build --json
Use the returned pack directory, or unpack its ZIP into your game’s asset
folder. Read assets.json, resolve IDs, and configure your importer using the
metadata above. Verify the canonical build before copying; keep the selected
bundle fixed for the entire game build. See automation.
The package contains render/audio assets and import metadata. It does not generate voxel chunk storage, terrain meshing, collision bodies, gameplay definitions, engine prefabs/scenes, or an engine-specific runtime loader. A receiving engine still needs its supported GLB/PNG/audio import path. Rendering in a particular engine has not been certified by the format tests.