Assets & Tools

What the game loads, where each asset comes from, and the scripts that produce the generated ones (as of 2026-11-10).

Never edit a generated file by hand. Each runtime asset is either a copy of the original's media or the output of a script in this repository. Change the script or its input table and regenerate: avatars3d/**, tools/data/blocks.csv, BoltPaths.hpp, GlueStrands.hpp, LavaSurfaceData.hpp, textures3d/grass_top.png, worlds3d/*.vwr and plans/inventory/*.md are all generated.

Runtime assets

DirectoryContentsSource
Content/icons/Sprite sheets: object-m.png (terrain tiles), element.png (objects), explo.png (explosions, big decor), blupi.png, blupi1.png, pad.png, button.png, text.png, jauge.png; .xml sidecars for three of themCopied from Mobile Eggbert
Content/backgrounds/28 sky pictures decorNNN.png (region ids 0-31; 005, 014, 017 and 023 have none) and the screens: init, wait, pause, win, lost, setup, trial, gear, speedyblupi, blupiyoupie, plus galaxyeggbert.png (Galaxy's logo on the Setup screen, added 2026-11-08)Copied from Mobile Eggbert; the logo is Galaxy's
Content/sounds/93 WAV files, one per sound channel (Audio & Music)Copied from Mobile Eggbert
Content/music/10 OGG tracks, 27 MBThe original's MIDI, rendered by tools/music/render_music.py
Content/demos/demo001.gdemo-demo010.gdemo, the main menu's ten demos (Demos & Recordings)Recorded in the game (tools/record_demo.sh)
Content/icons2x/, Content/backgrounds2x/Optional sheets at twice the size, loaded only when a file is exactly twice its 1x one (TextureScale.hpp)In the repository since 2026-11-10: 9 icon sheets and 39 backgrounds, re-rendered from the Ray Dream scenes where that passed verification, otherwise an edge-padded Lanczos upscale of the 1x pixels. Never used on the web or Android
avatars3d/blupi/Blupi: skinned model, 68 clips, face textures, clips.txt; vehicles/ holds the helicopter, jeep, overcraft, skateboard and tankRay Dream scenes via tools/rd4_to_gltf/
avatars3d/objects/121 model directories listed in index.txt, each with its parts, skeleton, clip, icons.txt and materials.txtRay Dream scenes via tools/rd4_to_gltf/
avatars3d/terrain/145 relief files (.gtr) and index.txt, which maps 158 tile icons to themRay Dream scenes via tools/terrain_relief/
avatars3d/blupi_placeholder/A fox model, used only when avatars3d/blupi/ is missingKhronos glTF sample "Fox" (third-party)
textures3d/grass_top.pngThe grass seen from above (64 × 64, tiles seamlessly)tools/textures/grass_top.py, from the leaves of tile 107
worlds3d/81 .vwr worlds: the title scene, the campaign, world998 (gallery) and world999 (showcase)tools/worlds/*.py with tools/worldkit.py; world999 by GenerateSampleWorld3D (Campaign & Worlds)
worlds/Five original-format level filesCopied from Mobile Eggbert; read by the world generators and QA scripts, not by the game

The game opens every asset relative to its working directory. A desktop build copies Content/, worlds3d/, textures3d/ and avatars3d/ next to the executable after each link (changed files only). The web build packs Content/icons, backgrounds, sounds, demos, worlds3d, textures3d and avatars3d into its .data file and serves Content/music/ beside the page. Android packs the same directories into the APK (without the 2x sheets) and, before the game starts, copies them to internal storage when they are missing or come from an older build (AndroidAssets.cpp).

The tile sheet object-m.png

Every terrain block's texture is an icon of this sheet, and a block's type number is its icon number. The sheet is 1301 × 1431 pixels: 64-pixel tiles with a 1-pixel gap before each (a pitch of 65), 20 columns by 22 rows, so 440 slots, icons 0-439. Icon n starts at pixel (1 + 65 × (n mod 20), 1 + 65 × ⌊n / 20⌋). BlockTypes::tileUV() (include/GalaxyEggbert/BlockTypes.hpp) adds a half-texel inset so filtering never samples the neighbouring tile. The block registry has 441 entries (0-440); icon 440 lies outside the sheet, a known gap that VerifyTileUvBounds tracks.

For the terrain the game builds a padded copy in memory (1920 × 2112: each tile with a 16-pixel border of its own edge texels, a pitch of 96) and mipmaps it, so distant floors do not shimmer (TileAtlas). It also classifies every tile by its alpha channel as opaque, cut-out or translucent (TileCoverage.hpp), which picks the pass the tile is drawn in (Rendering).

Models from the original scenes: tools/rd4_to_gltf/

The original's sprites were rendered from Ray Dream Designer / Studio scenes. This toolchain reads those scenes, rebuilds their geometry, and checks each result against the sprites. Its README.md records every verified convention of the format.

  1. rd4.py, primitives.py, freeform_mesh.py, geometry.py and scene.py parse the ASCII scenes and build cones, spheres, cylinders, free-form surfaces and deformers.
  2. rd4_to_gltf.py converts one animated scene into a skinned glTF (gltf.py writes it), one joint per scene object.
  3. scene_render.py, render_cna.py and verify_sprites.py render with the original game camera and compare with the sprite icons by silhouette IoU (intersection over union).
  4. export_cna_avatar.py runs CNA's own converter (tools/avatar_asset_pipeline/convert_avatar.py in the CNA repository) to write CNA's SkinnedModelEXT directory, then fixes winding and per-part colours and textures.

These tools need sibling checkouts: the original scenes (speedyblupi-data), Mobile Eggbert's sheets and CNA. Without them you can still build, play and test the game, but not regenerate models.

Terrain reliefs: tools/terrain_relief/

tiles.txt says which scene belongs to which tile icon (reviewed by eye); match_tiles.py ranks candidate scenes; build_relief.py keeps each scene's front half, decimates it to about 700 triangles with headless Blender (blender_decimate.py), cuts it along the cell's edges, textures it with the tile's sprite and writes avatars3d/terrain/*.gtr and index.txt. reliefs.txt holds per-model options. Tiles without a scene stay textured cubes.

Other generators

ToolWrites
tools/music/render_music.pyContent/music/musicNNN.ogg from the original MIDI (libfluidsynth, ffmpeg)
tools/bolt/bolt_paths.pysrc/GalaxyEggbert/Game/BoltPaths.hpp: the lightning's centre lines from icons 305-307
tools/glue/glue_strands.pysrc/GalaxyEggbert/Game/GlueStrands.hpp: the glue death's strands
tools/a3d/a3d.py, lava_surface.pysrc/GalaxyEggbert/Game/LavaSurfaceData.hpp: the lava's wave from the original Amapi shapes
tools/textures/grass_top.pytextures3d/grass_top.png
tools/DumpBlockDefinitions.cpptools/data/blocks.csv for the Python world checkers (VerifyBlockDefinitionsCsv fails when it is stale)

Inventories: python3 tools/model_parity.py > plans/inventory/model_parity.md, tools/asset_inventory.py (plans/inventory/assets.md; --check reports a stale file, and on 2026-11-07 it was stale), and the ledgers plans/audit/asset_sources.csv and render_representation.csv (how each block icon and object is drawn).

Provenance and licences

docs/provenance/ is an inventory with evidence, taken on 2026-11-07; it is not legal advice and decides nothing. In short:

The open questions are listed in docs/provenance/README.md and licences.md; per-asset detail is in original-game-material.md, generated-assets.md and third-party.md.

Related pages