A modular, open-source 2D and 3D game engine written in C++20.
Fork Engine is an attempt to build a serious, long-lived engine from first principles: a clean architecture, a high-performance runtime, and an editor-centric workflow. It is not a clone of any existing engine. Where an established design is the right answer, it is adopted deliberately and documented; where there is a real benefit to doing something different, that decision is recorded as an architecture decision record (ADR).
Three capabilities shape how the engine is designed, not just what it eventually features:
- Performance is a product feature. The engine is fast by construction and by measurement: an authoring model that stays human-friendly and a processing model that stays cache-friendly, with a benchmark that guards both.
- Games ship anywhere. The goal is that a game built in Fork Engine reaches every platform its players use — desktop today, web and mobile next, consoles when the SDKs allow — not merely that the editor runs on several OSes. The supported target today is desktop (Linux and Windows); see ADR-0032 for why the wider claim is sequenced rather than asserted.
- Editing is collaborative. Several people open the same project, edit it live, see each other's changes and presence, and never run a manual merge.
The plan for these is in docs/STRATEGY.md. The order the
next work is taken in — a vertical slice, scripting, then the pillars — is in
docs/ROADMAP.md.
The project is young and honest about it. The repository currently contains the
engine core through its first several milestones: a buildable library, working
2D and 3D renderers, a scene/resource/serialization/reflection core, 2D and 3D
physics, audio, an editor foundation, and a runnable example, all covered by
tests. Everything else is planned in docs/ROADMAP.md.
| Area | State |
|---|---|
| Build system, repository layout, CI | Working |
| Platform layer (window, input, context) | Working (desktop, GLFW) |
| Logging, assertions, error handling | Working |
| RHI abstraction + OpenGL 3.3 core backend | Working |
| 2D renderer (batched sprites, camera, textures) | Working |
| 3D renderer (lit meshes, directional/point/spot lights, 3D camera, frustum culling, directional shadows) | Working (first pass) |
| Mesh resources (primitives, OBJ loader) | Working |
| Scene graph (nodes, transforms, components) | Working |
| Resource manager (load, cache, release) | Working |
| Scene serialization (JSON) | Working |
| Reflection and component serialization | Working |
| Systems and the frame schedule | Working |
| 2D physics (colliders, rigid bodies, solver, queries, contact events) | Working |
| 3D physics (box/sphere colliders, linear solver, broad phase, distance joints, queries, contact events) | Working (first cut, see ADR-0031) |
| Audio (device seam, WAV loader, pure mixer, mixing system) | Working (first pass) |
| Editor (docking shell, hierarchy, inspector, asset browser, 2D viewport, play-in-editor, undo/redo) | Working (foundation) |
| Project file (settings, asset root, startup scene) | Working |
| Runtime frame loop + sandbox example | Working |
| A complete example game (breakout, the M2.5 vertical slice) | Working (see ADR-0036) |
| Scripting (C++ native modules first, language-agnostic seam) | Not started (see roadmap) |
| Build/export | Not started (see roadmap) |
| Performance tooling (profiler, benchmark, CI regression gate) | Working (see ADR-0033) |
| Extra platforms, collaborative editing | Planned (see roadmap) |
Unit, editor, and graphics tests pass locally; see Building and testing.
- Composition over inheritance. Gameplay objects are composed from components attached to nodes. There is one obvious place for a transform, and no deep class hierarchy to fight.
- Hybrid architecture, chosen per subsystem. The scene graph is the editor-facing authoring model. Systems that are performance-critical are free to consume that data in a data-oriented way without the hierarchy dictating how they execute. Not every subsystem is forced into the same model.
- Measure before optimizing. Performance work starts with a benchmark and ends with a regression guard, never with clever code that cannot show a win.
- Errors are values. Recoverable failures travel through
fe::Errorandfe::Result<T>, not exceptions. Assertions are for programming errors only. - The editor is a client of the runtime, not a fork of it. The editor talks to the engine through public interfaces and drives frames itself, which is what makes play-in-editor possible without a second, divergent code path.
- Small and working beats large and broken. A few genuinely finished systems are worth more than a long list of half-features.
The full design is in docs/ARCHITECTURE.md. Decisions
with real trade-offs are recorded in docs/adr/.
The engine is built from modules that depend only "downwards":
core <- platform <- rhi <- resources <- scene <- renderer2d
core <- reflect <- scene, resources
core <- serialization <- scene, resources, reflect
core, scene, resources <- systems
core, scene, systems, reflect <- physics2d
core, scene, systems, reflect <- physics3d
core, scene, resources <- mesh <- renderer3d
core, scene, resources <- scene3d
core, scene, systems, resources <- audio
everything <- runtime
- core — types, math, error handling, logging, assertions, UUIDs, filesystem.
- platform — window creation, input, and the graphics context (GLFW).
- rhi — a small graphics device abstraction; the OpenGL 3.3 core backend is one implementation of it. Nothing above this layer includes a backend header.
- resources — loading, caching, and lifetime of assets (images, textures, meshes, audio clips).
- scene — the node/component hierarchy and transforms.
- reflect — component property descriptions and the type registry.
- serialization — JSON persistence of scenes and components, driven by reflection.
- systems —
System,SceneSchedule,FrameContext, and the concrete gameplay systems. - physics2d — 2D colliders, a rigid-body solver, queries, and contact events.
- physics3d — 3D box/sphere colliders, a linear rigid-body solver with a broad phase and distance joints, queries, and contact events. A hand-written first cut behind a component-and-query boundary, so a third-party solver can replace it later (ADR-0031).
- mesh — CPU-side geometry:
Vertex3D,Mesh, primitive generators, and an OBJ loader. - scene3d — 3D scene components (
Camera3D,MeshRenderer3D,DirectionalLight3D,PointLight3D,SpotLight3D) and their reflection. - audio — audio device seam, decoded clips, and a mixing system.
- renderer2d — batched 2D rendering on top of the RHI.
- renderer3d — 3D mesh rendering with a forward lit pass (directional, point, and spot lights) and a directional shadow map.
- runtime —
Engine(subsystem lifetime) andApplication(frame loop).
The editor lives under editor/, outside the engine library; it depends on the
engine, never the reverse.
Public headers live under engine/include/fe/, so consumers write
#include <fe/core/Log.h>.
engine/ the engine library (the only thing a game links against)
include/fe/ public headers, one directory per module
src/ module implementations
editor/ the visual editor, built on the engine (core, UI, shell)
examples/sandbox/ a small runnable program that exercises every layer
examples/demo_project/ a minimal project to open in the editor shell
tests/ doctest-based unit, editor, and graphics test targets
benchmarks/ the performance benchmark and its CI regression gate
cmake/ dependency policy, build settings, warning interface
third_party/ vendored sources (glad, stb) with their own licenses
assets/ sample assets used by the example
tools/ small development scripts
docs/ architecture, strategy, roadmap, and ADRs
Requirements:
- A C++20 compiler (GCC 12+, Clang 15+, or MSVC 19.3+)
- CMake 3.24+
- On Linux, the X11 or Wayland development headers that GLFW needs
Dependencies (GLFW, nlohmann/json, doctest) are fetched and pinned by
cmake/FEDependencies.cmake, so a clean checkout builds with one command.
Pass -DFE_USE_SYSTEM_DEPS=ON to prefer system-installed packages instead.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -jRun the tests:
cd build && ctest --output-on-failureRun the example (from the repository root, so its relative asset paths resolve):
./build/bin/fork_sandboxHeadless runs, used by CI and useful on machines without a display:
./build/bin/fork_sandbox --headless 120 # render 120 frames, then exit
./build/bin/fork_sandbox --save-scene out.json # build the demo scene, save, exit
./build/bin/fork_sandbox --scene out.json # load a saved sceneInteractive controls: WASD/arrows move the camera, Q/E zoom, Esc quits.
The editor shell opens a project, loads its startup scene, and shows the scene
hierarchy, inspector, viewport, and asset browser. A minimal project ships in
examples/demo_project, so you can try it without authoring anything first:
./build/bin/fork_editor_shell --project examples/demo_projectOn a machine without a display, open the same project headlessly and exit after a few frames, which is enough to confirm the project and scene load:
./build/bin/fork_editor_shell --project examples/demo_project --headlessA gameplay module can be loaded and hot-reloaded while the editor runs: pass its
shared library with --module (repeatable) and rebuild it from your build
directory, and the editor picks the new code up on the next frame without a
restart:
./build/bin/fork_editor_shell --project examples/demo_project --module ./build/bin/my_game.soSee examples/demo_project/README.md for what the project contains.
fork_engine_tests— pure-logic tests (math, filesystem, scene, components, resources, serialization, reflection, physics, mesh, systems). Run anywhere.fork_editor_tests— headless editor logic (hierarchy view model, asset browser view model, document, play-in-editor, command stack, property helpers, viewport maths). Link onlyfork_editor_core, never the UI toolkit.fork_graphics_tests— create a hidden window and a GL context, render a scene, and read the framebuffer back to assert what was actually drawn. They skip automatically when no context is available, socteststays green on machines without a GPU.
The editor is built with -DFE_BUILD_EDITOR=ON (on by default).
On a headless Linux machine the graphics tests need a virtual display and a software renderer, for example:
xvfb-run -a -s "-screen 0 640x480x24" ./build/bin/fork_graphics_testsfork_bench runs a synthetic 2D scene through the real frame loop and reports
frame-time percentiles, and benchmarks/check_regression.py compares a run
against the committed baseline so CI fails on a regression. It needs a GL
context, so run it under a virtual display on a headless machine:
xvfb-run -a -s "-screen 0 1280x720x24" ./build/bin/fork_bench --scene sprite_heavySee benchmarks/README.md for the scenes, options, and
the gate's tolerances, and
ADR-0033 for the design.
Contributions are welcome. Start with CONTRIBUTING.md for
the build workflow, coding conventions, and review expectations. In short: keep
changes small and reviewable, add tests for behaviour, update the docs when an
interface changes, and never claim something works without having run it.
By participating you agree to the CODE_OF_CONDUCT.md.
Fork Engine is released under the MIT License. Vendored third-party
code under third_party/ keeps its own license; see the notices in each
subdirectory.