Skip to content

apps/tools ​

The level pipeline: from the Babylon.js Editor project (apps/level-editor) to the level data the game runs on, and back. Node scripts run directly (node apps/tools/src/cli.ts …, wrapped by the root level:* scripts); Babylon's math and a headless engine do the geometry. The editor's scene folders are read as the editor saves them: no packing, no editor CLI.

FileWhatTested by
cli.tsexport-level, port-level, recipe-level, dress-level, new-level, build-blocks, check-levels, kits-export, models-hulls, models-check, playby hand
scene-doc.tsA Babylon scene document reduced to its nodes (meshes, instances, groups) with their transforms, parents, metadata, geometry and colours; no engine neededscene-doc.test
frame.tsWorld transforms composed through the parents (double precision), yaw/pitch/roll, and the mirror between the editor and the game, for frames and geometryframe.test
export-level.tsBlocks (nodes with a block script) and kit pieces (meshes with metadata.foi) → placements and a piece library → LevelData; every problem names its nodeexport-level.test
level-json.tsThe canonical level file format (meshes, then one placement, road, point, door, container, vehicle spawner and garage per line; six decimals, three for vertices)level-json.test
level-sky.tsThe level's sky as the export and the level gate write it: the climate and the clock of the level spec beside the kits (<name>.spec.json, @foi/catalogue's skyOfSpec); none for a level of blocks.
block-library.tsThe seventeen unit source meshes (the game's BLOCK_MESHES through the mirror, plus the markers and the interactables) with their colours and scripts (numbers and texts)block-library.test
editor-project.tsWrites and reads the editor's scene folder format (binary geometry packed and read back, parents by uniqueId), on disk or in memoryeditor-project.test
port-level.tsLevelData → a scene of instances under groups (blocks from the library, kit pieces from their kits under _kits; a polyline road as a chain of slabs) → an editor scene folder; recipes reuse its createLevelSceneport-level.test
gltf.tsA reader for the glTF binaries Blender exports: pieces with their transforms baked, colours as groups, extraskits.test
kits.tsKits (<kit>.glb + <kit>.foi.json) read into pieces in the game's frame (with their tags, anchors, parts and look) and installed in a scene as source meshes under _kits with metadata.foikits.test
kit-library.tsThe kit library (npm run kits:export): every piece of the shared kits (all but a level's own) with its kind, collider, footprint and bounds measured on its triangles, triangle count, tags (its sidecar's, else derived from its kit, kind and id), anchors, breakable mass, roads, parts and, for a building, its roof (roof-spots.ts) → packages/catalogue/src/kits.generated.json, one piece per line, keys sorted; checkKitLibrary, the level gate's part that builds it again and compareskit-library.test
roof-spots.tsWhere a building's roof has room (roofSpots(mesh)): the height of its highest large flat (up-facing triangles on a grid of 0.5 m cells: the largest flat 3 m up or more and of 24 m² or more, or a higher one at least half as large) and up to eight circles on it clear of everything that rises from it (a parapet, a penthouse, a sign), the freest first, none reaching another; none for a pitched, a small or a low roofroof-spots.test
recipe.tsRecipes (kit pieces and blocks placed in the game's frame, roads, spawns) → an editor scene, or straight to level data in memory (exportRecipe); levelToRecipe dresses a block level with the city kitrecipe.test
models.tsThe vehicle bodies of apps/client/public/models/vehicles.glb (wheels and steering wheels left out) → the hull point clouds of packages/sim/src/vehicle-hulls.json, in the game's frame; the check that they agreemodels.test
audio-placeholders.tsThe radio's sample stations' placeholder items (npm run audio:placeholders, --force to write them again): a spoken ident (the system's text-to-speech) over music synthesized on a seeded stream, or a spoken episode over a quiet bed, encoded to mono 48 kbit/s MP3 by Blender's FFmpeg; an item already there (a real recording) is never written overaudio-placeholders.test
level-files.tsThe derived files: the generated registry packages/world/levels/index.ts, the naming check, the orphans of a gone scenelevel-files.test
pipeline.tsNames checked, every scene folder exported, orphans removed, registry writtencheck-levels.test
check-levels.tsThe gate: names, every level file byte for byte what its scene exports, levels playable, no orphan, registry current, skill copies alike, kit library currentcheck-levels.test
index.ts@foi/tools as a library for the worker (apps/worker): recipes and kits read, exportRecipe, canonicalLevel, the pathsrecipe.test
watch.ts, play.tsRe-export on save; run the game on a level while watchingby hand

Conventions: the editor's scene is left-handed, the game's right-handed; the mirror (x and the yaw change sign; a mesh's x negated, its triangles kept in order since the two frames wind their front faces the opposite way) is applied in frame.ts at the tool boundary and nowhere else. A kit piece is a source mesh whose metadata.foi says { kind, collider, roads? }; its geometry is read from the scene's binary files, mirrored, welded and stored once in the level's meshes. Level data is written with formatLevelJson, so a re-export of an unchanged scene changes no byte. See the Levels guide.