Olivier Allard
← Go Back

FireGL

Cross-platform rendering library focused on safe resource handling, modern C++ abstractions, and practical OpenGL workflows.

  • C++20
  • OpenGL
  • CMake
  • GLFW

Motivation

I wanted to learn how rendering systems work under the hood by building one myself. Coming from Unreal Engine, I was curious to recreate a simplified version of a rendering workflow to better understand what happens behind high-level engine abstractions.

Approach

I explored how a modular OpenGL rendering architecture can be structured in C++ by separating rendering logic, resource management, and application-level code.

The goal was not to build a production engine, but to understand how core graphics systems are designed: how GPU resources are created, managed, and used in a rendering loop.

Technology used

  • C++20 — used to practice modern C++ design patterns and structured resource management concepts.
  • OpenGL — learned the graphics pipeline (shaders, buffers, draw calls, state management).
  • GLFW — window creation, OpenGL context setup, and input handling.
  • GLAD — OpenGL function loading.
  • GLM — math for transforms, vectors, and camera operations.
  • STB Image — texture loading and image decoding.
  • Assimp — importing and handling 3D model data.
  • CMake — cross-platform build system and dependency management.

Key things I worked on / learned

  • How an OpenGL context is initialized and tied to a windowing system (GLFW).
  • How shaders are compiled, linked, and used in a rendering pipeline.
  • How GPU resources (buffers, textures, meshes) are created, bound, and managed.
  • How a basic render loop is structured (input → update → render).
  • How to design a simple abstraction layer over raw OpenGL calls.
  • How asset loading pipelines work using STB Image and Assimp.
  • How to structure a small engine-like architecture separating core rendering from application logic.
  • How external C++ graphics dependencies integrate through CMake.

Engine usage (application layer)

A simple FireGL application that sets up a window, loads assets, builds a scene, and runs a real-time rendering loop.

#include <FireGL/FireGL.h>

int main()
{
  fgl::AssetPathManager Assets(PROJECT_ROOT + "Config.ini");

  fgl::BaseWindow Window;
  Window.Initialize(4, 1, "Test123", fgl::WindowType::Windowed, false, 800, 700);
  Window.SetWindowIcon(Assets.GetPath("Troll"));

  fgl::TimeManager Timer;
  Timer.Initialize();

  auto Camera = std::make_shared<CustomCamera>();
  Camera->SetPerspective(45.f, Window.GetAspectRatio(), 0.1f, 1000.f);

  CustomInputManager Input(Camera);
  Input.Initialize();

  fgl::Shader Shader(
    Assets.GetPath("BaseLightingVertex"),
    Assets.GetPath("BaseLightingFragment")
  );

  fgl::Texture Texture;
  Texture.LoadTexture(Assets.GetPath("Wood"));

  auto Material = std::make_shared<fgl::LightingMaterial>(&Shader);
  Material->SetTexture("normal", &Texture);

  auto Model = std::make_shared<fgl::Model>(
    Assets.GetPath("BackpackModel")
  );

  Model->SetupMaterial<fgl::LightingMaterial>(&Shader);

  auto Entity = std::make_unique<fgl::Entity>(Model);

  fgl::Scene Scene(Camera);
  Scene.AddObject(std::move(Entity));

  fgl::Renderer Renderer(fgl::RenderingMode::Default);

  while (!Window.ShouldClose())
  {
    Timer.Update();
    Input.ProcessInput();

    Scene.Process();
    Renderer.Render(&Scene);

    Input.FinalizeInput();
  }

  Window.Terminate();
  return 0;
}

Engine internals (Entity system)

Custom Entity system inspired by game engine architecture (Unreal-style lifecycle + component model)

class Entity : public SceneObject
{
public:
  template <typename T,
            std::enable_if_t<std::is_base_of_v<SceneObject, T>, bool> = true>
  Entity(std::shared_ptr<T> Object);

  void SetMaterial(std::shared_ptr<Material> Material) override final;
  const std::shared_ptr<Material> GetMaterial(size_t MeshIndex = 0) const override final;
  std::vector<BaseMesh>& GetMeshes() override;
  size_t GetHash() const override;

  template <typename T> T* CreateComponent();
  template <typename T> T* GetComponent();
  void RemoveComponent(size_t ID);

protected:
  virtual void OnTick(float DeltaTime);
  virtual void OnBeginPlay();
  virtual void OnDestroyed();
  virtual void OnPrepareRender() const;
  virtual void OnPostRender() const;

private:
  void Render(size_t NumberInstance) const override final;
  void Tick(float DeltaTime) override final;
  void BeginPlay() override final;
  void Destroy() override final;

private:
  std::shared_ptr<SceneObject> m_Object;
  std::unordered_map<size_t, std::unique_ptr<Component>> m_Components;
};

Note

This project was valuable for learning core rendering and graphics programming concepts in OpenGL and C++. However, I also realized that I over-abstracted parts of the rendering architecture too early, which made the system more complex than necessary for its scale.

As a result, while it works well as an experimental learning project, it would require significant redesign to become a truly scalable or production-ready rendering engine.