Engine Integration (CNA, cna-engine)

The game is written against CNA's XNA 4.0 API, with two strict cna-engine modules as helpers (as of 2026-11-10).

Galaxy Eggbert has no engine of its own. Its host class GalaxyEggbertGame derives from Microsoft::Xna::Framework::Game and the game code calls CNA directly: devices, effects, textures, input, audio and storage are XNA types. cna-engine adds a camera and mesh building on top; it does not hide CNA. Both are sibling repositories built from source with the game (CMake add_subdirectory), looked for at ../cna and ../cna-engine or at -DCNA_HOME / -DCNA_ENGINE_HOME.

CNA

CNA is a C++ implementation of the XNA 4.0 API. It brings its own SDL3 and SDL3_mixer (vendored, built on the first configure) and needs three further siblings next to it: easy-gl, meta-gl and sharp-runtime (the .NET-style System types). The game links the umbrella targets CNA and SHARP_RUNTIME; CNA is built as a static library because cna-engine links its module libraries too, and a shared CNA would put a second copy into the process.

What the game uses, by namespace

NamespaceTypes usedWhere
Microsoft::Xna::FrameworkGame, GameTime, GraphicsDeviceManager, Vector2/3/4, Matrix, Color, Rectangle, BoundingFrustum, BoundingSpherethe host, the renderers, the camera code
...::GraphicsGraphicsDevice, BasicEffect, AlphaTestEffect, SkinnedEffect, ShaderEffect, Texture2D, RenderTarget2D, VertexBuffer, IndexBuffer, the state objects, SpriteBatch, SkinnedModelEXT TerrainRenderer, WorldRenderer, Hud, InputPad, the effects classes, the editor
...::InputKeyboard (and KeyDownEXT), Mouse, GamePad, Touch::TouchPanelGalaxyEggbertGame.cpp, GameHelpers.hpp (Input)
...::AudioSoundEffect, SoundEffectInstanceSound (Audio)
...::MediaSong, MediaPlayerthe music (UpdateMusic())
...::StorageStorageDevicethe player's folder (main.cpp)
Microsoft::Devices::SensorsAccelerometertilt steering (AccelTurnInput())
SystemTimeSpan, Object, EventArgsframe timing, OnDeactivated/OnExiting
::CNAShaderDialectEXT, GraphicsCapability, Platform/Entrypoint.hpp the shader preamble, the editor's wireframe fallback, Android's SDL_main

Note the two namespaces called CNA: ::CNA is the engine's own, GalaxyEggbert::CNA is the game's host.

CNA extensions the game depends on

CNA marks what XNA 4.0 does not have with an EXT suffix. The game needs these:

// src/GalaxyEggbertCNA/GalaxyEggbertGameLoad.cpp
content.setRootDirectoryProperty("avatars3d");
blupiModel_ = content.Load<std::shared_ptr<Microsoft::Xna::Framework::Graphics::SkinnedModelEXT>>(
    blupiModelIsReal_ ? "blupi/avatar" : "blupi_placeholder/avatar");

// src/GalaxyEggbert/Game/SurfaceShaders.cpp
switch (device.GetShaderDialectEXT())
{
    case CNA::ShaderDialectEXT::GlslDesktop: return "#version 330 core\n";
    case CNA::ShaderDialectEXT::GlslEs: return "#version 300 es\nprecision highp float;\n";
    default: return {};
}

The game asks for the HiDef graphics profile in its constructor: the screenshot and golden-frame captures use GetBackBufferData, which XNA allows only there.

Renderer identities

CNA picks its graphics backend at configure time from CNA_GRAPHICS_RENDERER. The game never names a renderer in its code; CMake passes the identity to CNA and links whatever CNA selected.

IdentityUsed forStatus
OPENGLES3the native default when nothing is set; AndroidLinux: builds and tests (CI uses the default). Android: built and run in the emulator.
OPENGL33desktop OpenGL 3.3 core; the configuration the maintainer plays and verifies on LinuxVerified
WEBGL2the Emscripten build (required there)Verified manually in headless Chrome; not in CI
SDLGPUthe Windows (MinGW) cross-build (spelled SDL_GPU before CNA renamed it)Not verified no fresh build and live 3D run yet

The three GL identities share one implementation inside CNA (EasyGL). CNA retired its VULKAN identity on 2026-10-13; Vulkan is reached through SDLGPU now. CNA's cmake/RendererSelection.cmake is the authority for the list. See Platform Support and Configuration.

The web build

cna-engine

cna-engine is a library of helper modules over CNA. The game builds only its strict modules, CnaEngine::Core and CnaEngine::Graphics, which use plain XNA 4.0 types; the modules that need CNA's extensions are switched off (CNA_ENGINE_WITH_EXTENDED=OFF, CNA_ENGINE_WITH_ENHANCED_GRAPHICS=OFF), as are its tests and examples. The game links CnaEngine::Graphics and includes only its Graphics headers:

TypeHeaderUsed for
CameraCnaEngine/Graphics/Camera.hppthe game's camera (camera_: SetLookAt, Position, View, Projection), which the editor also flies and draws its markers with
MeshBuilder, MeshData, BuilderVertex, VertexAttributeMeshBuilder.hpp, MeshData.hppbuilding every CPU-side mesh: terrain, cubes, billboards, HUD and editor quads
BoxFaces, BoxFace, BoxFaceStyle, PlateAxisMeshBuilder.hppboxes with a texture region per face, plates
GpuMesh, VertexLayoutGpuMesh.hpp, VertexLayouts.hppuploading a mesh and drawing it with the caller's effect
UvRectTextureAtlas.hpptexture regions: tiles in object-m.png, sprite icons, scene quads
DebugDrawDebugDraw.hpplines: the editor's highlight edges (where the device has no wireframe mode) and the position axes

Every Galaxy mesh is positions plus one texture coordinate (with normals where it is lit). The helpers that wrap cna-engine for this are in src/GalaxyEggbert/Game/TexturedMesh.hpp:

// src/GalaxyEggbert/Game/TexturedMesh.hpp
[[nodiscard]] inline CnaEngine::Graphics::MeshBuilder MakeTexturedMeshBuilder()
{
    return CnaEngine::Graphics::MeshBuilder(
        {CnaEngine::Graphics::VertexAttribute::Position, CnaEngine::Graphics::VertexAttribute::TexCoord0});
}
...
[[nodiscard]] inline std::unique_ptr<CnaEngine::Graphics::GpuMesh> UploadTexturedMesh(
    Microsoft::Xna::Framework::Graphics::GraphicsDevice& device, const CnaEngine::Graphics::MeshData& mesh)
{
    return std::make_unique<CnaEngine::Graphics::GpuMesh>(
        CnaEngine::Graphics::GpuMesh::Upload(device, mesh, CnaEngine::Graphics::VertexLayout::PositionTexture()));
}

The SceneFrame draw list uses cna-engine's CPU-side UvRect and box faces too, so it holds no GPU object (Architecture).

Which revisions

From CURRENT.md's "Verification baseline": the current source and CI use CNA 594eb28dc (with the optional KeyDownEXT), which includes ee3235984 (texture mip levels restored after a lost context, UB-009). The handbook states that the source needs 594eb28dc or newer.

.github/workflows/cna-ci.yml pins all siblings to exact commits: cna, cna-engine, easy-gl, sharp-runtime, meta-gl, and the worlds/ directory of mobile-eggbert (read by two verifiers). python3 tools/ci_pin_siblings.py moves the pins to the local siblings' HEADs, and --check reports drift.

Upstream bugs

The game does not patch its siblings. A defect found in CNA, cna-engine or another sibling is written down in upstream_bugs.md as UB-NNN: what was observed, how to reproduce it, how Galaxy copes until it is fixed, and the fixing commit. A workaround is removed only after the fix is re-verified. As of 2026-11-10 every entry is marked fixed upstream:

IDWhereProblem
UB-001 - UB-004CNA avatar converter and loadermesh primitives overwriting each other, material colours lost, inside-out winding, unescaped part names
UB-005cna-engineAddPlate/AddCrossedPlates drew their region upside down
UB-006CNA on Androidevery missed content name listed the APK's assets through JNI
UB-007CNA EasyGLthe skinned shader needed more vertex uniforms than OpenGL ES 3.0 guarantees
UB-008CNA audioa missing #include <algorithm> broke the web build
UB-009CNA EasyGLonly a texture's base level came back after a context loss (F9)
UB-010CNA working treesampler collections bound before a device member existed

The file also indexes problems recorded elsewhere (an easy-gl test failure, the retired Vulkan backend).

History

The project chose direct CNA on 2026-07-05, replacing an earlier plan to move through other engines. The earlier target GalaxyEggbertSimple3D was retired once CNA reached parity and removed from the tree on 2026-07-25 (recoverable at commit 4afd53e). Until 2026-10-20 the CNA target used a helper library called easy-3d (Easy3D:: camera and cube meshes); it was then replaced by cna-engine's Camera, MeshBuilder and GpuMesh, with golden frames and traces byte-identical before and after. Comments that still mention Easy3D describe that past.

Related pages