Architecture

The pieces

Cairn/           the engine, a static library
Cairn-Editor/    the editor: a static library with all of its code, plus main()
Cairn-Runtime/   the game executable
Cairn-CLI/       sends agent commands to a running editor

The engine is a static library. The editor and the runtime are two programs that link it.

Why the editor is a separate program

An exported game must contain no editor code and no debug tools. With two programs that is true by construction: the runtime never links the editor, Dear ImGui, the gizmos or the command server.

Play mode still runs inside the editor process, using the same engine library the runtime uses, so a game behaves the same in both.

Inside the engine

FolderWhat is in it
CoreApplication, window, logging, assertions, files, packs, UUIDs
AssetAsset handles, the registry, the manager, materials, glTF import
SceneScene, entity, components, the YAML serializer
RendererGraphics device, meshes, the scene renderer, post-processing, fonts
ScriptingThe Lua script engine and its bindings, and the definitions for code editors in Lua and TypeScript
PhysicsThe physics world, on Jolt
InputThe input map and per-frame input state
AudioThe audio engine, on miniaudio
AnimationSkeletons, clips, animator controllers
Particles, UIParticle simulation, screen UI layout and interaction
RuntimeSceneRuntime, which runs a scene: scripts, physics, input, audio, animation, UI

Shaders are GLSL in Cairn/Shaders, compiled to SPIR-V at build time and embedded in the library.

A frame

  1. Input is settled, so "was pressed" is true for exactly one frame.
  2. Scripts' OnUpdate run.
  3. Fixed steps run, 60 per second of game time: scripts' OnFixedUpdate, then physics, then contact callbacks.
  4. Animation, audio and particles update.
  5. The scene is drawn: shadow maps, a depth prepass, ambient occlusion, the lit pass, the sky, particles, then post-processing and the UI.

The scene is drawn into a texture, never straight to the window. The window, the editor's viewport, screenshots and tests all read that texture.

One way to change a scene

In the editor, every change to a scene goes through an EditorCommand. That is the only way a change becomes undoable, and it is the same layer the agent commands call. A panel and an agent cannot do different things.

Assets

Every asset has an ID stored in a .meta file beside it. Components and files refer to assets by ID, never by path. The asset manager loads meshes, materials and textures on first use and keeps them.

Files are read through one file system layer, which also reads from a mounted pack. That is why the same code runs a project folder in the editor and a packed game.

Dependencies

All are fetched by CMake at pinned versions. All have permissive licences.

LibraryLicenceUsed for
GLFWzlibWindows and input
nvrhiMITRendering on Vulkan
glmMITMath
EnTTMITEntity storage
yaml-cppMITEvery file the editor saves
stbPublic domain or MITImages and fonts
cgltfMITglTF import
LuaMITGame scripting
sol2MITC++ bindings for Lua
TypeScriptToLuaMITCompiling TypeScript scripts to Lua, in the editor only
TypeScriptApache 2.0The checker TypeScriptToLua runs on, in the editor only
Jolt PhysicsMITPhysics
miniaudioPublic domain or MIT-0Audio
Dear ImGuiMITEditor UI, editor only
ImGuizmoMITEditor gizmos, editor only
nlohmann/jsonMITAgent commands, editor only
doctestMITUnit tests

The built-in font is Roboto (Apache 2.0). MoltenVK (Apache 2.0) is the Vulkan driver on macOS, and is bundled into an exported .app.