A c++ game engine using OpenGL and SDL2 based on ECS architecture.
Read the Doxygen documentation »
View Demo
·
Report Bug
·
Request Feature
Table of Contents
⬆️⬆️⬆️ Click on the gif to see the full video presentation of the engine ⬆️⬆️⬆️
GLESC is a game engine implemented in C++ using OpenGL and SDL2. It is a final degree project for the Bachelor's
Degree in Computer Engineering at the University of Oviedo. It's development lasted 1 year and a half of
full-time work of a single person - me.
The engine allows 3D rendering, shaders, physics, collisions, inputs and audio while also taking advantage of
ECS architecture (good caching).
The project was developed with some principles in mind:
- Modularity or Separation of Concerns (SoC): The engine is divided into modules, each one with a specific purpose. This allows for easy maintenance and scalability.
- Performance: The engine is designed to be as fast as possible. It uses ECS architecture, which is cache-friendly.
- DRY: The engine is designed to be as pretty DRY, but sometimes there is code repetition, and it is not as DRY as it should be. This is because of the lack of time and experience of the developer. And sometimes for readability reasons.
- KISS: The engine is designed to be as simple as possible. This does not mean it is simple. It means that it is not more complex than it needs to be. Unfortunately, by definition, a game engine is a complex piece of software.
- SOLID: The engine is designed to be as SOLID as possible. This means that the code is easy to maintain and extend. It takes advantage of c++'s OOP and polymorphism to improve SOLID principles.
- Documentation: The engine is well documented, as it is a requirement for the assignment of the degree project.
Note: It's important to take into consideration the huge constraints of the project:
- The lack of experience of the developer
- The lack of time and the time restrictions
- The lack of resources (hardware and software)
- The lack of knowledge of the developer (therefore a lot of time was spent learning).
This project is not perfect and has some ugly workarounds, some poor design decisions, some bugs, and some performance issues. But it has also some great design decisions, some great code, and some great performance that took a lot of time and effort to achieve.
It should not be taken as a reference for a game engine, but as a learning experience.
The following instructions will help you get a copy of the project up and running on your local machine for development and testing purposes.
Here is a list of things you need to use the software and how to install them.
-
Boost is a set of libraries for the C++ programming language that provides support for tasks and structures such as linear algebra, pseudorandom number generation, multithreading, image processing, regular expressions, and unit testing. It contains over eighty individual libraries.
It is used several times in the project, and can be used for more things if needed.
-
To compile and edit the code CLion has been used. It facilitates all the tools needed for the project, such as CMake, ninja, mingw, the compiler, and the debugger. It is not necessary to use CLion, but it is highly recommended.
-
To execute our code, we will require GNU utilities. This contains numerous compilers for various languages ( e.g. Fortran, C++ etc.) classified as the GNU Compiler Collection (GCC), and their respective debuggers.
MinGW includes a port of the GNU Compiler Collection (GCC), GNU Binutils for Windows (assembler, linker, archive manager), a set of freely distributable Windows specific header files and static import libraries which enable the use of the Windows API, a Windows native build of the GNU Project's GNU Debugger, and miscellaneous utilities.
IMPORTANT: MUST SELECT x86_64 (which is 64 bits) NOT i686 (which is 32 bits)
In case the installer doesn't work (which is likely):
Download link (folder with 64 bits version)
HOW TO INSTALL:
- Unzip the folder, copy it somewhere logical (for example
C:/MinGW
orC:/Program Files/MinGW
).- Add to the path of the Environment Variables the bin folder.
-
CMake is an open-source, cross-platform family of tools designed to build, test, and package software. CMake is used to control the software compilation process using simple platform and compiler-independent configuration files and generate native makefiles and workspaces that can be used in the compiler environment of your choice.
Here is a step-by-step guide on how to install the project.
- Clone the repo
git clone https://github.com/valydumitru01/GLESC.git
- Open the project with CLion
- Prepare the project dependencies with CLion or your preferred IDE
- Build the project with CMake
- Run the project
The following section is dedicated to explaining how to use the engine. I will be trying to exemplify how to create a videogame using the engine using the current implemented game (Shoot the chicken).
Important Note: This engine is hardcoded, there is absolutely no Data-Driven programming involved. This was a conscious choice given the constraints. So one big issue of using this engine is having to wait for all the compile times for each minor change.
Important Note 2: There is heavy use of strings in the engine. This is because of the lack of time to implement a better system. And it's very useful for debugging and for UX. But it is not the best practice due to efficiency reasons. In a normal, data-oriented engine, strings would be just data and would not be part of the compiled code or logic.
The Game
class only use is to handle the scenes. So you don't really need to touch the Game
class more
than just insert the scenes you need.
To create a new scene, you need to create a new class that inherits from the Scene
class. You can see an example of
this in the ShootTheChickenGame
class.
How to register a scene:
void Game::init() {
registerScene<Default>(Default::getSceneName());
registerScene<TerrainGeneratorGame>(TerrainGeneratorGame::getSceneName());
registerScene<ShootTheChickenGame>(ShootTheChickenGame::getSceneName());
sceneManager.switchScene(ShootTheChickenGame::getSceneName());
}
Example of a scene class:
class ShootTheChickenGame : public GLESC::Scene::Scene {
public:
SCENE_DEFINITION(ShootTheChickenGame)
...
};
Once you have your scene defined and registered, you can start customizing it. You can add entities, components, systems, etc. The next sections will explain how to take advantage of all the GLESC engine features.
Entities are the main objects in the engine. They can seem like "containers" for components but in reality they are just
wrapped IDs. You create them with a name and a metadata. That name can then be retrieved to get the entity.
You can only set the metadata of instance, this way the name of the entity gets automatically increased by one, so you
don't have to worry about the name of the entity. It has no other purpose than that.
Example of entity creation: (Extracted from ShootTheChickenGame, how chickens are created)
void ShootTheChickenGame::generateChickenEntities() {
int numChickens = 10;
chickens.clear();
for (int i = 0; i < numChickens; i++) {
Transform::Position position = generateChickenPosition();
ECS::Entity chicken = createEntity("chicken", {EntityType::Instance});
chicken.addComponent<ECS::TransformComponent>();
chicken.addComponent<ECS::RenderComponent>();
chicken.addComponent<ECS::PhysicsComponent>();
chicken.addComponent<ECS::CollisionComponent>();
auto& transform = chicken.getComponent<ECS::TransformComponent>().transform;
chicken.getComponent<ECS::TransformComponent>().transform.setPosition({position});
chicken.getComponent<ECS::RenderComponent>().copyMesh(chickenMesh);
chicken.getComponent<ECS::CollisionComponent>().collider.setBoundingVolume(
chicken.getComponent<ECS::RenderComponent>().getMesh().getBoundingVolume());
chicken.getComponent<ECS::PhysicsComponent>().physics.setAffectedByGravity(true);
transform.setPosition({transform.getPosition().getX(), 100, transform.getPosition().getZ()});
chicken.getComponent<ECS::PhysicsComponent>().physics.setMass(5);
chickens.push_back(chicken.getID());
}
}
To create an entity, you need to call createEntity("name", EntityMetadata)
. After that, you just attach the components
and modify them directly.
Example of retrieving an entity: (Extracted from ShootTheChickenGame, how the player entity was retrieved)
ECS::Entity player = getEntity("player");
To retrieve an entity, you just need to call getEntity("name")
. This will return the entity with that name.
Currently, the engine has the following components:
- TransformComponent
- CollisionComponent
- InputComponent
- LightComponent
- PhysicsComponent
- RenderComponent
Note: It also has these other components but should not be used as the engine don't currently support more than one of each type, and they are hardcoded in the engine.
- CameraComponent
- SunComponent
- FogComponent Each one defines a different aspect of the entity.
The TransformComponent
is used to define the position, rotation, and scale of the entity. It is used to define the
entity's position in the world.
The CollisionComponent
stores the collider of the entity. You can define the bounding volume of the entity, and the
engine will handle the collisions for you. You can also set the collision callback function to handle the collision
events. And you can even set collision callback for a specific collider (a specific entity).
Note: You can set the bounding volume of the collider to fit the mesh of the entity using:
chicken.getComponent<ECS::CollisionComponent>().collider.setBoundingVolume( chicken.getComponent<ECS::RenderComponent>().getMesh().getBoundingVolume());
Example of collision component creation: (Extracted from ShootTheChickenGame, how the bullet collision was created)
bullet.addComponent<ECS::CollisionComponent>();
bullet.getComponent<ECS::CollisionComponent>().collider.setBoundingVolume(
bullet.getComponent<ECS::RenderComponent>().getMesh().getBoundingVolume());
bullet.getComponent<ECS::CollisionComponent>().collider.setCollisionCallback(
[bulletID, this](Physics::Collider& otherCollider) {
collisionCallback(bulletID, otherCollider);
});
The PhysicsComponent
is used to define the forces, acceleration, velocity, mass, etc. of the entity. What really is in
charge is to update the position of the entity based on the forces applied to it. And you can set the forces,
acceleration, etc. of the entity at any point.
The RenderComponent
stores the mesh and the material of the entity. You can build the mesh and set the material
properties of the entity. The engine will handle the rendering for you. The mesh is a bit complicated, so I will explain
it in the next section.
The mesh is a class that stores the vertices, normals, indices, and textures (not yet implemented) of the entity. Building a mesh
Example of mesh creation: (Extracted from ShootTheChickenGame, how the player mesh was created)
Render::ColorMesh playerGun = Render::MeshFactory::cuboid(0.5f, 0.5f, 5, Render::ColorRgb::Brown);
Render::ColorMesh playerGunHandle = Render::MeshFactory::cuboid(0.5f, 0.5f, 3, Render::ColorRgb::DarkBrown);
Transform::Transformer::translateMesh(playerGunHandle, {0, -0.5, 1.5});
Render::ColorMesh playerHand = Render::MeshFactory::cuboid(0.5f, 0.5f, 5, Render::ColorRgb::ClearSkin);
Transform::Transformer::rotateMesh(playerHand, {-25, -45, 0});
Transform::Transformer::translateMesh(playerHand, {-0.3, -0.55, 1});
playerGun.startBuilding();
playerGun.attachMesh(playerGunHandle);
playerGun.attachMesh(playerHand);
playerGun.finishBuilding();
// Rotate the gun to the left
Transform::Transformer::rotateMesh(playerGun, {25, 25, 0});
// Lift the gun to the player's hands
Transform::Transformer::translateMesh(playerGun, {1.3, -1.5, -3});
playerMesh.startBuilding();
playerMesh.attachMesh(playerGun);
playerMesh.finishBuilding();
To build a mesh you need to use the MeshFactory
class to create basic shapes (or building blocks). Then you can use
the Transformer
class to translate or rotate the mesh. Finally, you can attach the meshes to each other to create a
more complex mesh.
Note: Attaching meshes like this is a bit complicated and not user-friendly, but this is the only way to do it for now. Mesh loaders or builders are not yet implemented. As it was previously stated, there is no data-driven programming in the engine.
Materials are fairly easy to understand and use. Each render component has one material, and each material has a set of function members that define the material properties. Material properties:
void setDiffuseColor(const ColorRgb& diffuseColor) { this->diffuseColor = diffuseColor; }
void setDiffuseIntensity(const float diffuseIntensity) { this->diffuseIntensity.set(diffuseIntensity); }
void setSpecularColor(const ColorRgb& specularColor) { this->specularColor = specularColor; }
void setSpecularIntensity(const float specularIntensity) { this->specularIntensity.set(specularIntensity); }
void setEmissionColor(const ColorRgb& emissionColor) { this->emissionColor = emissionColor; }
void setEmissionIntensity(const float emmissionIntensity) { this->emissionIntensity.set(emissionIntensity); }
void setShininess(const float shininess) { this->shininess.set(shininess); }
Note: Not all properties do something. Emission intensity and color do not work. The rest should work fine.
The InputComponent
is used to define the inputs of the entity. It is quite intuitive and easy to use.
You basically subscribe the entity to a key (with it's action) and define a callback function to handle the input event.
You can also unsubscribe the entity from a key.
Example of input component creation: (Extracted from ShootTheChickenGame, how the player input was created)
getCamera().getEntity().getComponent<ECS::InputComponent>().input.unsubscribeKey(
{Input::Key::SPACE, Input::KeyAction::ONGOING_PRESSED});
getCamera().getEntity().getComponent<ECS::InputComponent>().input.unsubscribeKey(
{Input::Key::LEFT_SHIFT, Input::KeyAction::ONGOING_PRESSED});
getCamera().getEntity().getComponent<ECS::InputComponent>().input.subscribeKey(
{Input::Key::LEFT_CLICK, Input::KeyAction::ONCE_PRESSED}, shootBulletAction);
getCamera().getEntity().getComponent<ECS::InputComponent>().input.subscribeKey(
{Input::Key::SPACE, Input::KeyAction::ONCE_PRESSED}, jumpAction);
getCamera().getEntity().getComponent<ECS::InputComponent>().input.subscribeKey(
{Input::Key::R, Input::KeyAction::ONCE_PRESSED}, {
[&]()-> void {
if (getWindow<ShootTheChickenHUD>(statsWindow).getAmmunition() == 0) {
switchScene<ShootTheChickenGame>();
}
}
}
Note: You will probably need to unsubscribe from the current keys of the camera entity. This is because the camera has a set of default behaviors that you might not want. You can also just make a new input component and replace the old one.
The LightComponent
gives a point-light to the entity. The point light will be rendered at the position of the transform
of the entity. If the entity also has a mesh, the light will be rendered at the center of the mesh. So it would probably
not be seen. You can set the color, intensity and radius of the light.
Example of light component creation: (Extracted from ShootTheChickenGame, how the bullet light was created)
bullet.addComponent<ECS::LightComponent>();
bullet.getComponent<ECS::LightComponent>().light.setColor(Render::ColorRgb::Yellow);
And you can see, pretty simple.
As I noted previously, there is no need to use these components. But you do need to understand how to use them to change the camera, sun and fog.
The sun component is used to define the sun diffuse and ambient diffuse. Example of sun component modification (Extracted from ShootTheChickenGame, how the sun was modified)
getEntity("sun").getComponent<ECS::SunComponent>().sun.setIntensity(0.4f);
getEntity("sun").getComponent<ECS::SunComponent>().sun.setColor({255, 255, 200});
getEntity("sun").getComponent<ECS::SunComponent>().sun.setDirection({-0.4, -1, -0.4});
getEntity("sun").getComponent<ECS::SunComponent>().globalAmbientLight.setIntensity(0.5);
getEntity("sun").getComponent<ECS::SunComponent>().globalAmbientLight.setColor({255, 180, 165});
Those are predefined entities called "sun" and "fog" that you can modify.
The camera component defines the camera properties such as the field of view, the near and far planes, and the aspect ratio. You can define them pretty easily.
Example of camera component modification (Extracted from ShootTheChickenGame, how the camera was modified)
getCamera().getEntity().getComponent<ECS::CameraComponent>().perspective.setFovDegrees(90);
CameraComponent only contains perspective, there you get the attributes to modify.
The fog component defines the fog properties such as the color, the density, and the gradient. It is also easy to modify.
Example of fog component modification (Extracted from ShootTheChickenGame, how the fog was modified)
getEntity("fog").getComponent<ECS::FogComponent>().fog.setDensity(0.3);
getEntity("fog").getComponent<ECS::FogComponent>().fog.setColor({200, 200, 170});
getEntity("fog").getComponent<ECS::FogComponent>().fog.setEnd(60);
Systems are the classes that handle the logic of the game. They are in charge of updating the entities, handling the collisions, updating the renderer, etc. You don't need to understand the current systems, but you can create your own systems to handle the logic of your game.
Each system iterates over the entities that have the subscribed components for that system. So if you create a system
called DeathSystem
and you subscribe the CollisionComponent
and the HealthComponent
, the system will iterate over
all the entities that have both components and will handle the logic of the death of the entity.
To create the system, you will need to inherit from the System
class and implement the update
function. The update
function is called every frame and is in charge of updating the entities.
You also need to modify the class GLESC
inside the engine to register the system.
Note: Ugly, I know, but I didn't have time to implement a system registration system.
How to register a system: (Extracted from the engine, all the systems are registered here)
std::vector<std::unique_ptr<ECS::System>> Engine::createSystems() {
std::vector<std::unique_ptr<ECS::System>> systems;
systems.push_back(std::make_unique<ECS::RenderSystem>(renderer, ecs));
systems.push_back(std::make_unique<ECS::TransformSystem>(ecs));
// Physics system must update before the physics collision system
systems.push_back(std::make_unique<ECS::PhysicsSystem>(physicsManager, ecs));
systems.push_back(std::make_unique<ECS::PhysicsCollisionSystem>(physicsManager, collisionManager, ecs));
systems.push_back(std::make_unique<ECS::InputSystem>(inputManager, ecs));
systems.push_back(std::make_unique<ECS::CameraSystem>(renderer, windowManager, ecs));
systems.push_back(std::make_unique<ECS::LightSystem>(ecs, renderer));
systems.push_back(std::make_unique<ECS::SunSystem>(ecs, renderer));
systems.push_back(std::make_unique<ECS::FogSystem>(renderer, ecs));
Example of system creation: (Extracted from the engine, how the Transform system was created)
namespace GLESC::ECS {
class TransformSystem : public System {
public:
explicit TransformSystem(ECSCoordinator& ecs);
void update() override;
};
} // namespace GLESC::ECS
Example of system implementation: (Extracted from the engine, how the Transform system was implemented)
TransformSystem::TransformSystem(ECSCoordinator& ecs) : System(ecs, "TransformSystem") {
addComponentRequirement<TransformComponent>();
}
void TransformSystem::update() {
for (auto& entity : getAssociatedEntities()) {
auto& transform = getComponent<TransformComponent>(entity);
transform.transform.setOwnerName(getEntityName(entity).c_str());
Transform::Rotation rotation = transform.transform.getRotation();
// Use of fmod to avoid floating point errors
if (rotation.getX() < -360.0f)
transform.transform.setRotation(Transform::RotationAxis::Pitch, 360.0f);
if (rotation.getY() < -360.0f)
transform.transform.setRotation(Transform::RotationAxis::Yaw, 360.0f);
if (rotation.getZ() < -360.0f)
transform.transform.setRotation(Transform::RotationAxis::Roll, 360.0f);
if (rotation.getX() > 360.0f)
transform.transform.setRotation(Transform::RotationAxis::Pitch, -360.0f);
if (rotation.getY() > 360.0f)
transform.transform.setRotation(Transform::RotationAxis::Yaw, -360.0f);
if (rotation.getZ() > 360.0f)
transform.transform.setRotation(Transform::RotationAxis::Roll, -360.0f);
}
}
As you can see, you call addComponentRequirement to subscribe the component to the system, then all the entities that have the component will be updated by the system. To update the entities, you iterate over getAssociatedEntities() and update the entities.
The HUD is a system that is in charge of rendering the HUD of the game. It uses ImGUI, which is a very powerful library. My implementation only facilitates the window separation and implements a layout for the HUD.
To create a HUD window, you need to create a class that inherits from the InGameWindow
and
override void windowContent()
with the actual content of the window. The window content will be rendered by the engine
for you.
Example of HUD window creation: (Extracted from the ShootTheChickenGame, how the stats window was created)
class ShootTheChickenHUD : public GLESC::InGameWindow{
public:
...
private:
void windowContent(float timeOfFrame) override;
}; // class ChickenKillCountHUD
Example of HUD window implementation: (Extracted from the ShootTheChickenGame, how the stats window was implemented)
ShootTheChickenHUD::ShootTheChickenHUD() {
this->setSizeFraction({0.3f, 0.1f});
this->setTitle("Chicken Kill Count");
isVisible = true;
this->setLayoutPosition(GLESC::LayoutPos::BottomRight);
this->setCenter(GLESC::WindowCenter::BottomRight);
this->addFlag(ImGuiWindowFlags_NoResize);
this->addFlag(ImGuiWindowFlags_NoMove);
this->addFlag(ImGuiWindowFlags_NoCollapse);
this->addFlag(ImGuiWindowFlags_NoTitleBar);
this->addFlag(ImGuiWindowFlags_NoScrollbar);
this->addFlag(ImGuiWindowFlags_NoSavedSettings);
this->addFlag(ImGuiWindowFlags_NoBringToFrontOnFocus);
this->addFlag(ImGuiWindowFlags_NoFocusOnAppearing);
this->addFlag(ImGuiWindowFlags_NoInputs);
this->addFlag(ImGuiWindowFlags_NoBackground);
}
void ShootTheChickenHUD::windowContent(float timeOfFrame) {
ImGui::PushFont(HudLookAndFeel::get().getFont(HudLookAndFeel::get().getDefaultFont(), 30));
// Push text color
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); // Red color
ImGui::Text("Chicken Kill Count: %d", chickenKillCount);
ImGui::Text("Ammunition: %d", ammunition);
ImGui::PopStyleColor();
ImGui::PopFont();
}
It is necessary to add the ImGui flags to give the window the desired look. And also, it is necessary to set the custom layout functions for proper window placement. These are all the layout functions and enums:
enum class LayoutPos {
TopLeft,
CenterLeft,
BottomLeft,
TopCenter,
Center,
BottomCenter,
TopRight,
CenterRight,
BottomRight,
Custom
};
enum class WindowCenter {
TopLeft,
CenterLeft,
BottomLeft,
TopCenter,
Center,
BottomCenter,
TopRight,
CenterRight,
BottomRight
};
/**
* @brief Set the max size of the window
* @details If the window gets bigger this value, it will be clamped to this size
*/
void setMaxSize(ImVec2 size);
/**
* @brief Set the min size of the window
* @details If the window gets smaller this value, it will be clamped to this size
*/
void setMinSize(ImVec2 size);
/**
* @brief Set the size of the window as a fraction of the screen size
* @details The fraction is clamped between 0.0f and 1.0f
*/
void setSizeFraction(ImVec2 fraction);
/**
* @brief Set the position of the window as a fraction of the screen size.
* @details The fraction is clamped between 0.0f and 1.0f. The center of the window is set by the set center
* function. The position will modify that center.
*/
void setPositionFraction(ImVec2 fraction);
/**
* @brief Set the center of the window
* @details The center is used to calculate the position of the window.
*/
void setCenter(WindowCenter center);
/**
* @brief Set the layout position of the window
* @details The layout position is used to calculate the position of the window.
*/
void setLayoutPosition(LayoutPos position);
During the execution of the game (in debug mode), you will see the debug HUD. It includes a console, a list of entities, an inspector of the selected entity and a stats text window. These are very useful to edit and see what is the best way to implement the game. But as I said, it is hardcoded, so you can't change it, editing the entities mid-game is not possible.
To be able to toggle the debug windows the camera entity is subscribed to the LEFT CTRL
key. It is also subscribed by
default (even if not in debug) to the 1
key to toggle relative mouse.
The engine has a simple sound system that allows playing sounds and music. You can play music in a loop, switch it off or change music. You can also play global sounds or even play sounds in a specific position, making them 3D sounds.
Note: The engine uses the SDL2 library to play sounds.
Note 2: The 3D sound was not enough tested, so it might not work as expected.
Example of music and sound loading: (Extracted from the ShootTheChickenGame, how the sounds were loaded)
SoundPlayer::loadSong("Chicken_Blasters.mp3", "main_song");
SoundPlayer::loadSound("chicken_shot.mp3", "chicken_shot");
SoundPlayer::loadSound("shoot.mp3", "shoot");
SoundPlayer::loadSound("chicken_idle.mp3", "chicken_idle");
Example of music and sound playing: (Extracted from the ShootTheChickenGame, how the sounds were played)
SoundPlayer::setMusic("main_song");
SoundPlayer::playMusic();
SoundPlayer::playSound("shoot");
To play a song you need first to set the music and then play it. To play a sound you just need to play it by name.
- Implement math
- Create matrix class abstraction
- Create vertex class abstraction
- Create implementation methods for the classes
- Use SIMD for the implementation
- Create documentation
- Generate doxygen documentation
- Generate folder documentation
- Generate CppDepend report
- Create developer manual (README)
- Crete installation manual (README)
- Implement CMake builder
- Implement Debug build
- Implement Release build
- Add graphic api abstraction layer
- Add Vulkan support
- Add DirectX support
- Add Metal support
- Add OpenGL ES support
- Add OpenGL support
- Add WebGL support
- Create window abstraction
- Create custom math implementation
- Create vector class
- Create matrix class
- Create geometry
- Create line class
- Create polyhedron class
- Create plane class
- Add custom ECS architecture
- Add renderer
- Add rendering interpolation
- Add frustum culling
- Add occlusion culling
- Add mesh abstraction
- Add mesh class
- Add assimp mesh loader
- Add mesh builder
- Add shaders
- Add reflections
- Add ambient diffusion
- Add sun diffusion
- Add point light diffusion
- Add spotlight diffusion
- Add shadows
- Add post-processing
- Add skybox
- Add textures
- Add physics
- Forces, acceleration, velocity, etc
- Rigid body physics
- Rag-doll physics
- Soft body physics
- Fluid dynamics
- Cloth physics
- Add HUD window system (using imgui)
- Add custom window abstraction
- Support custom layout
- Add collisions
- Add AABB
- Add convex colliders with SAT
- Add GJK for convex colliders
- Add quadtree or octree for broad-phase
- Add mesh shape colliders
- Add inputs
- Add sounds
- Add music
- Add plain sounds
- Add 3D sounds
- Add sound loader
- Add scene management (currently is hardcoded)
- Add scene serialization
- Add scene deserialization
- Add scene loading
- Add scene saving
- Add scene editor
- Add in-game debug
- Add in-game debug hud
- Console
- Entity list
- Entity inspector
- General debug info (fps, render calls, etc)
- Add collider rendering
- Add bounding volume rendering
- Add normals rendering
- Add profiler
- Add memory debugger
- Add memory leak detector
- Add in-game debug hud
- Create tests
- Unit tests
- Math
- ECS
- Physics
- Collisions
- Window
- Renderer
- Integration tests
- ECS
- Unit tests
See the open issues for a full list of proposed features ( and known issues).
For now, I am the sole contributor to this project, as this is a final degree project. One of the requirements of the final degree project is that it must be done by a single person. However, if you want to contribute to the project, contact me, and we can discuss it once the project has been presented.
Note: The current list of contributors of GitHub is not accurate. No one has contributed to this project yet. The reason there are contributors is that I have reused the repository from another project in which I have collaborated with other people. Nothing serious, but I wanted to clarify it.
Distributed under the MIT License. See LICENSE.txt
for more information.
Valentin Dumitru - Twitter: @valentindmtr115 - email: [email protected]
Project Link: https://github.com/valydumitru01/GLESC/
I would like to thank the following people and resources for helping me with this project:
- The Cherno - OpenGL series
- Professor Keenan Crane - Computer Graphics course (CMU 15-462/662)
- Austin Morlan - What is an ECS
- Joey de Vries - LearnOpenGL
- Jason Gregory - Game Engine Architecture
- Bob Nystrom - Game Programming Patterns
- Google stylesheet
- CPP Core Guidelines
- CPP best practices
- Best-README-Template
- And me! (For being able to handle all the stress, hard work and dedication needed to finish this project)