Skip to content

@awaken/assets

Asset storage, the shared import IR, every model/engine importer, and the persistence formats. It depends on @awaken/core and @awaken/render (for MeshData, Material, and texture types).

This package is the seam between importing (turning a Unity/Godot/Unreal/glTF project into engine data) and shipping (writing that data to a binary project or a self-contained game file).

ts
import { AssetStore, spawnImportedScene, encodeProject, bundleToSceneFile } from "@awaken/assets";

AssetStore

The in-memory library of everything a scene references, keyed by string id. It is data-only - the renderer uploads from it, the animation system reads skeletons/clips/skins from it.

MemberTypeDescription
meshesMap<string, MeshData>Geometry by mesh id.
texturesMap<string, TextureData>Device-neutral texture pixels by id.
materialsMap<string, Material>Authored materials by id.
skeletons / clips / skinsMap<string, …>Skeletal-animation assets (skins keyed by the skinned mesh id).
revisionnumberBumped on every add/replace, so a per-frame uploader can skip re-scanning when nothing changed.
addMesh/addTexture/addMaterial/addSkeleton/addClip/addSkin(id, data) => voidRegister an asset.
addSound(id, bytes, mime) => voidRegister an imported sound (encoded bytes, decoded by the audio engine at load). See Audio.
has/hasTexture/hasMaterial, list/listTextures/listMaterials-Membership + key listing.

TextureData stores pixels device-neutral (data is RGBA8, with an optional role), so the renderer can compress for the local GPU at upload - a project ships safely to any device.

Import IR (types)

Every importer produces the same ImportedScene intermediate representation, so one consumer (spawnImportedScene) handles all sources. See the Import Overview for the pipeline philosophy.

SymbolShapeDescription
ImportedScene{ meshes: ImportedMesh[]; roots: ImportedNode[]; skeletons?; clips? }The whole imported scene.
ImportedMesh{ id; data: MeshData; color; modelName?; submesh?; skin? }One mesh + its default colour and optional per-vertex skin binding.
ImportedNode{ name; position; rotation; scale; meshId; children; animator? }One node in the imported hierarchy.

spawnImportedScene

The single consumer of the IR: register the scene's assets in an AssetStore, then instantiate its node hierarchy into a World as GameObjects with MeshRenderer components.

SymbolSignatureDescription
spawnImportedScene(world, scene, store, opts?) => Entity[]Register meshes/skeletons/clips/skins, spawn the node tree, return the root entities. opts.onAnimator is called for skinned nodes so the caller can attach an Animator (the assets layer can't reference the runtime type).
AnimatedNode{ entity; clip; skeleton }Reported for each skinned node via onAnimator.

Importers

Each importer parses a source format into an ImportedScene (or a richer result for whole-project imports). They are all generic - no model-pack-specific hardcoding.

FormatEntry point(s)
glTF / GLBimportGltf(bytes, name), parseGlb, parseGltfJson, importModel(bytes, name) (auto-detects glTF/GLB/OBJ)
OBJparseObj(text, idPrefix)
FBX(the fbx module - parsed via importModel / the editor's import flow)
Unity .unitypackageisUnityPackage(entries), parseUnityScene(…), parseMaterial, prefab helpers
Unreal .uasset / projectdecodeUassetStaticMesh, parseUassetTexturePng, isUnrealProject, importUnrealProject(files, onProgress?)
GodotparseGodotText, parseGodotMeshResource, parseGodotMeshSurfaces, plus the godotImport IR types
Retarget / animmakeRetargeter, buildCharSkeleton, buildIdleClip, remapSkin

Awaken file formats also round-trip here: parseMaterialFile/serializeScriptFile/parseScriptFile/serializePrefabFile/parsePrefabFile. See Import Overview and the per-engine pages (Unity, Godot, Unreal, Models).

Geometry helpers

SymbolSignatureDescription
weldMesh(m: MeshData) => MeshDataMerge duplicate vertices (shrinks geometry before shipping).
quantizePositions / dequantizePositions(Float32Array) ⇄ (Uint16Array + PosBbox)Positions → u16 over the mesh AABB, and back.
quantizeNormals / dequantizeNormals(Float32Array) ⇄ Int8ArrayNormals → octahedral int8, and back.
quantizeUvs / dequantizeUvs(Float32Array) ⇄ (Uint16Array + UvBbox)UVs → u16 over the UV bbox, and back.

Quantization cuts per-vertex bytes ~32 → ~12 and is idempotent (a value on the grid re-quantizes to the same index), so re-saving never compounds error. It backs both the binary project and the shipped scene file.

Persistence

Two output formats, both defined here. Their exact byte-level shapes and version relationship are in Data Formats & Versions.

The binary project (.awaken)

A glTF-.glb-style container: a JSON metadata chunk followed by one binary blob holding every mesh/texture byte.

SymbolSignatureDescription
PROJECT_VERSION6Current project format. v6 adds each script's uses list (the modules it runs jobs on); v5 added a script's language, folding v4's separate modules map into scripts; v4 added native module assets; v3 persisted the skeletal-animation library and particle-effect metadata; v2 quantized meshes; v1 stored raw f32.
ProjectBundleinterfaceThe in-memory round-trip shape: scene, scenes, entry, prefabs, scripts, materials, meshMaterials, materialAssets, render, dock, assetSources, meshes[], textures[].
encodeProject(b: ProjectBundle) => Uint8ArraySerialize a bundle to the AWKN binary (quantizes meshes).
decodeProject(bytes: Uint8Array) => ProjectBundleParse it back (dequantizes to Float32Array, so the in-memory MeshData is identical either way).

The shipped scene (SceneFile JSON)

The self-contained JSON the standalone player loads as scene.awaken.json.

SymbolSignatureDescription
SCENE_FILE_VERSION1Current ship-file format.
SceneFileinterface{ version, title?, entry?, scene, meshes: MeshJSON[], prefabs?, scenes?, scripts?, compiled?, materials?, meshMaterials?, shaders?, textures?, render? }, plus everything a shipped game needs to animate and play: skeletons?, clips?, skins?, animGraphs?, particleEffects?, uiClips?, fonts?. volumes? is read-only legacy, kept so games exported before volumes became components still grade.
MeshJSONinterfaceOne mesh's geometry as base64 typed-array bytes, optionally quantized (q) with bbox/uvbb; indices are u16 (idx16) when the mesh has < 65536 verts.
ScriptAsset / ScriptMap{ name, source, kind?, language?, uses?, obfuscation? } / Record<string, ScriptAsset>Scripts keyed by stable id, so renaming is safe. kind "editor" or "plugin" is not shipped; language picks the toolchain (typescript by default, or assemblyscript/c, which compile to wasm and run as jobs); uses lists the modules this script runs jobs on. What a script can do is inferred from what it exports, not from language.
meshToJSON(id, data) => MeshJSONEncode one mesh (quantized) for the ship file.
loadScene(world, assets, types, file) => voidLoad a SceneFile into a world + asset store (dequantizes meshes).
loadSkeletalAssets(assets, file) => voidRegister a ship file's skeletons, clips, skins, anim graphs, particle effects and UI clips into an asset store.
bundleToSceneFile(b: ProjectBundle) => SceneFileThe export seam - convert an editor project bundle into the shippable JSON.
normalizeScripts(raw) => ScriptMapAccept the legacy name → source shape and return the current id-keyed shape.

bundleToSceneFile is a pure function, so it currently ships geometry + vertex/material colour but not albedo textures (encoding base64 PNG needs a browser canvas the editor supplies separately). See Export to a game file.

The ship container

An optional single-blob binary wrapper around a SceneFile + its meshes (used by the compressed export path).

SymbolSignatureDescription
encodeSceneContainer(base, meshes) => Uint8ArrayPack a SceneFile (sans meshes) + mesh buffers into one blob.
decodeSceneContainer(bytes) => { file, meshes }Unpack it.
isSceneContainer(bytes) => booleanMagic-number check.
compactSceneForShip(scene, types) => SceneJSONDrop editor-only data to slim the shipped scene.

See also

Awaken — browser-native WebGPU game engine.