Small C++17 game engine for learning. OpenGL 3.3, GLFW, GLEW, Bullet, glm, imgui.
./compile.sh # debug build + run
./compile.sh --release # release build + runBinary lands in bin/<type>/monad-engine. The script cds to the repo root so shaders under assets/ resolve.
CMake also writes compile_commands.json for clangd.
source/ app entrypoint + sandbox (Game, Player, main.cpp)
engine/thirdparty/ vendored GLFW + GLEW
engine/source/ engine library (Engine, Scene, render, physics, input, io)
assets/
shaders/ GLSL pairs (.vert + .frag)
scenes/ JSON scene files
models/ glTF models
materials/ material JSON
textures/
source/main.cpp creates an ENG::Application subclass, hands it to the Engine singleton, and runs the main loop. Swap the app by editing one line in main.cpp.
Subclass mnd::Application, then point source/main.cpp at it.
class MyApp : public mnd::Application {
public:
bool Init() override { return true; }
void RegisterTypes() override {}
void Update(float dt) override {}
void Destroy() override {}
};Scenes live in assets/scenes/*.json. Load once in Init().
auto scene = mnd::Scene::Load("scenes/scene.json");
mnd::Engine::GetInstance().SetScene(scene.get());
m_scene = scene; // keep the shared_ptr aliveIn Update(dt), call m_scene->Update(dt).
auto *obj = scene->CreateObject("Crate");
obj->SetPosition(vec3(0, 1, -3));
obj->AddComponent(new mnd::MeshComponent(material, mesh));Child objects: scene->CreateObject("Camera", parentObj);.
Use the COMPONENT(T) macro and register the type in RegisterTypes().
class HealthComponent : public mnd::Component {
COMPONENT(HealthComponent)
public:
void Update(float dt) override {}
};
void MyApp::RegisterTypes() {
HealthComponent::Register();
}Same pattern for game-object subclasses (see source/Player.h — GAMEOBJECT(T) + Player::Register()).
auto &input = mnd::Engine::GetInstance().GetInputManager();
if (input.IsKeyPressed(GLFW_KEY_W)) { /* forward */ }
if (input.IsMouseButtonPressed(GLFW_MOUSE_BUTTON_LEFT)) { /* fire */ }Mouse position: glfwGetCursorPos on the current context.
auto *l = scene->CreateObject("Sun");
l->SetPosition(vec3(10, 20, 5));
auto *lc = new mnd::LightComponent();
lc->SetColor(vec3(1.0f, 0.95f, 0.8f)); // warm white
l->AddComponent(lc);Multiple LightComponents are collected into the per-frame uniform array.
Easiest path: declare it in the scene JSON — PhysicsComponent reads
collider + body blocks and registers itself with PhysicsManager.
{
"type": "PhysicsComponent",
"collider": { "shape": "box", "halfExtents": [0.5, 0.5, 0.5] },
"body": { "mass": 1.0 }
}In code:
auto body = std::make_shared<mnd::RigidBody>(/*...*/);
obj->AddComponent(new mnd::PhysicsComponent(body));The component copies the simulated transform back onto its GameObject each frame.
auto *gltfRoot = mnd::GameObject::LoadGLTF("models/scene.glb", scene);
gltfRoot->SetPosition(vec3(0, 0, -5));Builds a GameObject hierarchy mirroring the file's node tree, with
MeshComponent + AnimationComponent attached where present.
if (input.IsKeyPressed(GLFW_KEY_ESCAPE)) SetNeedsToBeClosed(true);cmake --build bin/debug --target engine-docs
# open bin/debug/docs/html/index.htmlEducational. No warranty.