
Overview
Project
- OpenGL rendering + physics demo
- Made for final year engine programming assignment
- Individual project
- 75% grade
Features
- Built-in level editor
- Upgraded rendering
- Optimised physics system
- Streamlined API
- UI framework
Tech
- C++
- OpenGL 4.3/GLSL
- SDL 2, glew
- Rendering pipeline an evolution of my OpenGL demo
The "Stirling" game engine was developed for a third year uni assignment: We were tasked with creating a 3D engine in the form of a C++ library and a small game to demonstrate its features.
I decided to use my OpenGL rendering engine as a starting point, and a lot of the graphics-related code persists in the engine, albeit heavily modified. My design requirement for the rest of the engine was for it to be useful in a game jam scenario: each decision was driven by the question "if I had to use this in a jam, what would I want it to do?". To this end all the functions within the engine are quite high-level, completely detaching the user from the underlying OpenGL and other complex subsystems: it only takes 6 lines of code to create a window, load a model and render it to the screen.

Simplicity is great, but to implement games you also need to have control over the behaviour of the engine. That's why I built the API to be able to configure almost every aspect of the engine's classes. Want to scale the physics time steps? Sure, you can do that. Want to temporarily hide your GUI? Go ahead. Want to set your near/far planes? Yep. Toggle shadows on individual object? Of course. You get the idea: for practically all the functionality that I developed, I made sure the user could control its behaviour.

The features that really shine in a jam scenario, when you just want to get things done quickly, are the editor, scene save/load system and physics systems. Level design through code (i.e. hard-coding positions, rotations and scales) is a nightmare, and ridiculously time consuming, so I built a level editor using the engine's existing systems that can spawn and manipulate objects, generate AABB colliders on the fly and, most importantly, use the save system. This packages almost the entire scene into binary files, ready to be loaded again with a single function call, potentially in a future instance of the application. The physics system uses a grid based spatial partitioning system to reduce collision checks between objects, which lets you throw as many objects at each other as you want (within reason) with minimal performance impact. The grid by default will translate and resize itself on each axis based on the distribution of objects in the scene, but like everything else this can be configured to accommodate the game's requirements.

Below I've written a small demo application along with two screenshots for comparison, that demonstrates Stirling's automatic LOD system (did I mention it does that too?) as well as some more base features like normal mapping, lighting and the Text GUI element.
1#include "Renderer.h"
2
3int main()
4{
5 //Create a new renderer
6 stirling::Renderer r("LOD test", 720, 720);
7
8 //Create a new mesh
9 stirling::MeshPtr mesh = std::make_shared<stirling::Mesh>();
10 //Load a sphere model into the base LOD
11 mesh->loadModelFromFile("resources/models/sphere.obj");
12 //Load brick textures into the base LOD
13 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/brickwall_diffuse.png"));
14 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/brickwall_normal.png", stirling::TextureAsset::TEXTURE_NORMAL));
15 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/brickwall_specular.png", stirling::TextureAsset::TEXTURE_SPECULAR));
16
17 //Create a new LOD level in the mesh
18 int lod = mesh->addLOD(10.0f);
19 //Load a cube model into the new LOD
20 mesh->loadModelFromFile("resources/models/cube.obj", "", lod);
21 //Load wood textures into the new LOD
22 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/planks_diffuse.png"), lod);
23 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/planks_normal.png", stirling::TextureAsset::TEXTURE_NORMAL), lod);
24 mesh->addTexture(stirling::TextureAsset::createFromFile("assets/planks_specular.png", stirling::TextureAsset::TEXTURE_SPECULAR), lod);
25
26 //Add the mesh to a new object
27 stirling::ObjectPtr object = std::make_shared<stirling::Object>();
28 object->setMesh(mesh);
29
30 //Create the distance text
31 stirling::TextPtr text = std::make_shared<stirling::Text>(4, 6, 24, "resources/UI/arial.ttf");
32
33 //Add everything to a scene
34 stirling::ScenePtr scene = std::make_shared<stirling::Scene>();
35 scene->addObject(object);
36 scene->addPanel(text);
37 scene->addLight(std::make_shared<stirling::Light>(glm::vec3(1.0f, 3.0f, 1.0f), glm::vec3(1.0f), 5.0f));
38
39 r.addScene(scene);
40
41 while (r.update())
42 {
43 //Slowly move the camera up and back
44 scene->translateCamera(glm::vec3(0.0f, r.getDelta(), r.getDelta()));
45 //Fix the camera to the object
46 scene->cameraLookAt(object->getPos());
47 //Update the text
48 text->setText(std::to_string(glm::distance(object->getPos(), scene->getCameraPos())));
49 }
50
51 SDL_Quit();
52
53 return 0;
54}Result:
