KOSMOS

Developers Manual

Getting the source

Kosmos is built on simlib, which is a separate library. Check both out and install simlib once, then build Kosmos against the installed copy.

git clone <kosmos repository> kosmos
git clone <simlib repository> simlib

A fresh checkout runs as it is: it carries a default texture set (see below) and procedural terrain. The optional data described in this manual only improves detail.

Building

CommandResult
scripts/build-linux.shClean build of build/package/kosmos-beta-Linux.tar.gz. Needs simlib installed under SIMLIB_PREFIX (default ~/opt).
scripts/build-windows.shCross-build of build-win/package/kosmos-beta-Windows.zip (mingw-w64 and vcpkg; simlib under ~/opt-win).
INCREMENTAL=1 scripts/build-linux.shReuses the existing build tree instead of cleaning first.
KOSMOS_SIMLIB_DIR=/path/to/simlibBuilds against a simlib source tree instead of an installed one.

For everyday work configure once and use Ninja:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$HOME/opt
ninja -C build
build/kosmos --check-data

--check-data loads, saves and reloads every scenario and base and reports any that do not round-trip. Run it after editing data. See Testing & screenshots for the screenshot options.

Repository layout

PathContents
src/The simulator.
addons/Ships, aircraft and MFD modes.
data/Bodies (sol.json), systems, scenarios, bases, scripts and textures.
data/celestia/Celestia source data. Moon sources are grouped under moons/<planet>/<Moon>/; that source-only tree is not scanned at runtime and is excluded from packages. Large optional hires/ packs are git-ignored and excluded from packages.
data/parts/Shared part kits (a folder with part.json and a glTF mesh) for building multistage rockets; see Part kits.
data/blender/kosmos_edit.py, the script Blender runs for “Edit in Blender” in the part editor and the base editor.
data/textures/Installed runtime fallback images and normal maps, in the repository. Import desired source maps here with tools/celestia_import.py.
data/textures-hires/Optional installed image and normal-map overrides. Git-ignored, never packaged.
data/orbiter/Optional Orbiter terrain and imagery archives, read directly at run time. Git-ignored, never packaged.
data/bodies/, data/nasa/bodies/ holds glTF shapes that bodies use through "mesh". nasa/ is the folder of NASA 3D Resources downloads that tools/nasa_import.py converts into data/parts/nasa_* and bodies/vesta.glb (needs Blender and pip install DracoPy trimesh). nasa/ is Git-ignored and never packaged.
tools/Import and generator scripts (Python).
scripts/Build, release and publish scripts.

Ship systems architecture

Ship power, consumables and cabin air live in two layers: a general-purpose gas grid in simlib, and the Kosmos model that uses it. The data formats are in the Modders Manual; this section is how the code fits together.

Files

PathContents
rsimlib/include/gas_vessel.h, src/engine/gas_vessel.cppsl::GasVessel: a 12³ cellular grid of three gases (O2, N2, CO2). Exported through sl.h. Test: gas_vessel_test in simlib.
src/systems.h, src/systems.cppSystemComponent, VesselSpec, parse_systems, the Systems runtime class, find_tagged_vessels and LifeSupportMode.
src/ship.cpp, src/ship.hShipSpec::systems, Ship::systems_, the per-step hook, staging and property publishing.
src/part_kit.cppForwards a kit’s systems and vessels into the ship with the stage index set.
src/scenario.cpp, src/sim.cppThe life_support and per-ship systems scenario keys; Sim::life_support is the default applied in Sim::spawn.
src/kosmos.cpp, src/menu.cpp, src/mfd.cppThe F1 window, HUD alarm, scenario-editor fields, and the bus-power gate on MFDs and lights.
addons/stockmfd/systems.luaThe Systems MFD mode.
tests/systems_test.cppUnit tests (see below).

The gas grid (simlib)

FluidSimulation3D has no non-buoyant gas and its smoke and steam expire, so cabin air is a separate, simpler type rather than a mode of the fluid solver. The grid is a cube whose outer layer is the hull. Each step exchanges a fraction of the density difference between neighbouring open cells, which conserves mass; it is stable up to 1/6 per face and the step is split into sub-steps. Vent cells sit in the hull layer and drain their neighbours at a given rate to vacuum, and gas_vessel_step returns the mass vented. The API is plain functions: create_gas_vessel, gas_vessel_fill, _inject, _consume, _set_vent, _step, _total, _cell, _open_cells and _set_total. After changing simlib, rebuild and install it (cmake --build build --target simlib, then cmake --install build) before building Kosmos.

Authority: lumped masses

Each Systems::Vessel keeps lumped masses o2, n2 and co2 in kilograms. These are the truth: pressure, partial pressures, hazards and leaks are computed from them (ideal gas, fixed temperature). The grid only distributes the gas for display and local effects. Each step crew inject CO2 and consume O2 at their cells, the grid mixes, and then gas_vessel_set_total rescales each gas to its lumped total, so the grid can never drift from the books. Above SystemsContext::grid_dt_limit (30 s) the grid skips diffusion and only rescales, which keeps high time warp cheap.

A leak is a decay constant k = aream² × 170 / volume per second, applied as exp(−k·dt) to all three gases, so it is exact at any step size.

The step

Ship::after_step works out the Sun’s direction in the ship frame, the flux relative to 1 AU, and whether a body shadows the ship (a ray–sphere test against every non-star body), and passes them in a SystemsContext to Systems::step(dt, ctx). Order inside a step:

  1. solve_bus in 10 s substeps (at most 2000 per call, so warp works). Fixed sources (solar, RTG) come first. Loads are then walked by priority, all-or-nothing; a load that does not fit is shed. The shortfall is met by the dispatchable sources (fission, fusion, fuel cell) and then by the batteries, and any surplus charges the batteries. Electrolyser output is applied after the bus.
  2. step_vessels: crew metabolism, grid sources and sinks, scrubbers and regulators (only when powered), leaks, the grid step and total sync, then hazard classification and the health clock.
  3. Alarms are collected and, in fatal mode, a vessel whose crew health reaches zero sets crew_lost_; Ship then sets crashed_ and the HUD says CREW LOST.

Hazard thresholds, crew rates and conversion constants are the constexpr values at the top of systems.cpp and in step_vessels. Health only changes when the mode is not off, and only for vessels with crew.

Stages

Components and vessels carry a stage index (names are resolved against the ship’s stage names at parse time; the default is the last stage). Ship::drop_stage calls Systems::drop_stage, which marks the equipment of that stage dead and removes its vessels. Stages drop bottom-first, so equipment in the top stage survives.

Tagged meshes

find_tagged_vessels runs at the end of load_ship_spec and needs no graphics context. It reads each part model’s file: OBJ files are parsed directly (the loader makes one part per usemtl group, so that name is the tag), and other formats go through sl::load_model. It takes the bounds of every object named vessel*, vent* or crew*, transforms them by the part’s placement, and merges the result with a same-named JSON vessel. Visuals::part_model in kosmos.cpp hides those objects when drawing. Note that spec.directory must be set (it is derived from the file path when empty) before the models can be found.

Properties, UI and power gating

Systems::publish ties getters under /ships/ship[N]/systems/, including the write-only vessel[i]/breach-cm2. The F1 window and HUD alarm read the Systems object directly; the MFD reads properties. Systems::instruments_powered() is false only when the bus has no electricity at all; MfdSystem::bus_power is set from it each frame (MFDs act as switched off) and ship lights skip drawing. Cabin gas heat-maps come from Systems::grid_cell.

Tests

tests/systems_test.cpp is excluded from the normal build. Run cmake --build build --target systems_test && build/systems_test. It covers parsing and stage defaults, solar and eclipse charging, load shedding, scrubber and regulator behaviour, a leak, the fatal path, stage drops, the electrolyser, and a tagged OBJ model written to a temporary directory. Tests must #undef NDEBUG before including <cassert> or the asserts compile away in release builds. simlib’s own gas_vessel_test covers mass conservation, venting and set_total.

Limits and ideas

Default & hi-res textures

Keep source collections separate from the images Kosmos loads as runtime textures. Surface images are equirectangular maps named after the body in lower case:

FileUsed forDefault (in the repo)
earth.ddsEarth from orbittextures/earth.jpg
moon.ddsThe Moon from orbittextures/moon.jpg
mars.ddsMars from orbittextures/mars.jpg
earth_lights.ddsEarth’s city lights on the night sidetextures/earth_lights.jpg
earth_clouds.ddsEarth’s cloud layera procedural cloud layer
<body>.dds / .png / .jpgAny other bodytextures/<body>.jpg

Above about 300 km for Earth (scaled by body radius for other worlds), the whole-body image or streamed Earth tiles are drawn. Closer in, Orbiter Elev.tree continues to provide terrain heights when installed; the terrain chooses the finer available colour imagery per location from Orbiter Surf.tree and the Celestia map or tiles. If neither provides imagery, it uses procedural terrain colours.

How textures are found

For each name the game checks data/textures-hires/ first, then the optional matching Celestia hi-res folder described below, then data/textures/. Within a folder the extensions are tried in the order .dds, .png, .jpg, .jpeg. So to override a body, drop a file of the same name in data/textures-hires/; to go back to the default, delete it. A body with "texture" set in sol.json uses exactly that path under data/ instead. Close-up terrain independently compares available Orbiter and Celestia colour imagery at each location and samples the finer source; normal maps (<body>_normal.jpg or .png) are looked up in data/textures-hires/ or data/textures/, with Celestia normal-map folders also supported as described below.

DDS files (BC1, BC3 or BC7, with a mip chain) are uploaded to the GPU still compressed: a 16384×8192 BC1 Earth costs about 90 MB of video memory and almost no system memory. PNG and JPEG are decoded first and cost about 1.3 GB of RAM at that size. Use --max-texture N to cap the size if your GPU is short of memory.

Getting and installing Orbiter hi-res data

Kosmos can use the planet packs from the Orbiter space flight simulator in two ways. Neither is included in the repository or in the downloads.

  1. Get the packs. Download the Orbiter 2016 high-resolution texture packs for Earth, Moon and Mars (from the Orbiter site or the Orbiter-Forum add-on list). Each pack holds Elev.tree (heights) and Surf.tree (imagery); Earth also has cloud and mask layers. Read each pack’s licence first.
  2. Terrain and close-up imagery (read directly). Unpack each pack as its own folder inside data/orbiter/. The folder name must end with the body’s name:
    data/orbiter/
      Orbiter 2016 High Res: Earth/   Elev.tree  Surf.tree  ...
      Orbiter 2016 High Res: Moon/    Elev.tree  Surf.tree
      Orbiter 2016 High Res: Mars/    Elev.tree  Surf.tree
    Nothing else is needed. The console prints Earth terrain from Orbiter data (and so on) at start-up for each body it found.
  3. Whole-planet maps for the orbital view (converted once). Convert each Surf.tree to a single compressed DDS in data/textures-hires/. The tool copies the compressed tiles block for block (no decode), so even a 16384×8192 map converts without decoding anything:
    python3 tools/orbiter_import.py "data/orbiter/Orbiter 2016 High Res: Earth/Surf.tree" --body earth --level 8
    python3 tools/orbiter_import.py "data/orbiter/Orbiter 2016 High Res: Moon/Surf.tree"  --body moon  --level 8
    python3 tools/orbiter_import.py "data/orbiter/Orbiter 2016 High Res: Mars/Surf.tree"  --body mars  --level 8
    The result is data/textures-hires/earth.dds, moon.dds and mars.dds. The first argument may also be an Orbiter installation folder together with --body. --level 8 gives 16384×8192; --level 7 gives 8192×4096. --out DIR changes the output folder. python3 tools/orbiter_import.py --selftest checks the tool.
  4. Check it. Start Kosmos and look for surface texture .../textures-hires/earth.dds: 16384x8192 in the console, or compare an Earth orbit shot before and after.

The importer reads only the Surf.tree layer. Earth’s night-light and cloud maps (earth_lights.dds and earth_clouds.dds) are optional and are not produced by it; without them the game uses the default city-lights image and the procedural clouds.

You can install only some bodies. A body without a pack, or without a textures-hires file, simply keeps its default.

Other hi-res sources

Licences

The repository, and every download, may contain only data we are free to redistribute. data/orbiter/, data/celestia/hires/ and data/textures-hires/ are git-ignored and excluded from packages. data/celestia/moons/ is also excluded from packages because it is source-only, though selected source files may be kept in the repository. Check the licence of any map before redistributing it or installing it in data/textures/.

Packaging & release

scripts/release.sh builds the Linux and Windows packages and publishes them to the web site (--no-publish, --linux-only and --windows-only narrow it). Publishing only acts when an archive’s checksum changed; it then stamps the new version on the pages and moves VERSION on to the next build. The packages contain addons/ and data/ except for data/orbiter/, data/celestia/hires/, data/celestia/moons/ and data/textures-hires/, plus the monospace font. A download therefore uses the bundled runtime fallbacks until its owner adds optional hi-res data as described above.

Roadmap

Ideas under consideration. Nothing here is scheduled or implemented; they are recorded so the design can be reviewed first.

Native addon modules (C interface)

Addons today are data plus sandboxed Lua. This item would let an addon also ship compiled code, as Orbiter add-ons do, through a strictly C ABI so modules can be written in C, C++, Rust or Zig and Kosmos never exposes its C++ types. The full design is in docs/native_addon_roadmap.md in the source tree.