AA Engine
GitHub Link
A Journey of learning
Right from the moment I wanted to make a Game engine, it has been an amazing journey.
I am currently on the 2nd iteration of the N iterations of my Game Engine. The development is at full speed and below are the currently implemented features of my Engine!
The Headings below are drop-downs!
Rendering Engine
The Rendering Engine is abstracted from the User to help them render objects without the knowledge of Graphics APIs.
Rendering API and Renderer
The Rendering Engine has an abstracted API that is used by the Renderer to handle the Submitted render calls by the User. The calls are currently handled synchronously and instantly, but in the future, the functionality will expand to handling Rendering on a different thread called the Render Thread.
Shaders, Contexts, and Buffers
AA Engine abstracts Shaders, Buffers, and the Rendering contexts independent of the API and is implemented per API. AA Engine also supports a shader library that the user can use to store and manage all the shaders.
Forward Rendering
I have built a lighting system using the Phong Lighting system. I have also developed the code for shadows
Components of Phong lighting model using RGB model: OpenGL allows us to break this light's emitted intensity into three parts: ambient, diffuse, and specular. Each type of light component consists of these three major color components
Deferred Rendering
I built a deferred renderer that makes the rendering faster and more efficient at a slight cost to memory
Deferred Rendering is implemented in 2 passes, the G Buffer pass and the Lighting pass
G Buffer pass includes storing the information like position of vertices, diffuse, normals, texture coordinates
Lighting pass includes bringing all those values together and drawing them on the screen using blend nodes
Math with SIMD support
AA Engine has a Custom Math Library built from scratch which supports Vectors, Matrices, Euler Angles, Quaternions, etc.
There is also SIMD support for the Math Library which takes into consideration the Alignment of these structures in memory and maintaining cache coherency and spatial and temporal locality to speed up the performance of the Math by 3-4x times.
When measured against standard 3D Math libraries like GLM, it performed as fast if not faster than GLM.
Custom Containers and Data Structures
Static Array
Static Array class that is a wrapper for the usual static arrays we define by Array[Num];
Dynamic Array
Dynamic Array wrapper class that stores the elements of the Array on a Heap.
This class provides functionality similar to the std::vector class supporting Move Semantics to make sure we aren't copying unnecessary data around.
With profiling and tests, this class turned out slightly faster than the std::vector class.
Red Black Tree
Red Black Tree is a Balanced Binary Search Tree that guarantees Searching, Insertions, and Deletions in O(log(n)) Time.
I built the Red Black Tree using a Root Node and a Nil Node.
A Node contains a Pointer to the Parent, Left, and Right Nodes. It also contains information about whether is a Black Node (or Red) and whether it is a Nil Node.
Currently, the Tree has a Pointer to the Root and Nil Node and a Predicate function for Comparisons.
Search
Searching is similar to a Binary Search Tree.
Insertions
Inserting a node is similar to inserting a node in a Binary Search Tree, but at the end, we fix insertions based on where the Node is inserted.
Insertion fixes are based on 4 cases.
Z is root
Color Z BlackZ's Uncle is Red
Recolor Z's Parent = Black, GrandParent = Red, Uncle = BlackZ's Uncle is Black (Triangle)
Rotate Z's GrandParent.
(Z's Parent = Left then Right Rotate and vice-versa)Z's Uncle is Black (Line)
Rotate Z's GrandParent and recolor Parent = Black and GrandParent = Red.
(Z's Parent = Left then Right Rotate and vice-versa)
Fixes can shift up the tree, so we need to keep running for these cases until we reach an optimal condition.
Deletions
Deletion has 2 steps
The first step has 3 cases
When Node to removed has 2 Nil children
When Node to removed has 1 Nil and 1 non-Nil Child
When Node to removed has 2 non-Nil Children
The Second Step is fixing any violations and starts when we pick a Fix-up node and its parent to perform a fix-up operation to rebalance the Red-Black tree if necessary
ImGui
AA Engine currently uses ImGui, a graphical user-interface library to render UI elements on the screen.
There is a special type of Layer called the ImGui Layer on which all the ImGui elements are drawn. And Each specific layer provides an OnImGuiRender function to provide functionality to the users to draw ImGui elements on the screen
Layer System
AA Engine uses Layers to determine the order of various things like what needs to be drawn first/on top, and an order to Handle incoming Events. This gives the user an organized way to arrange things like Game Layers/ UI Layers, etc.
A Layer stack has Layers, and a Layer provides functionality for 4 things.
When a Layer is Attached/Detached
When a Layer is Updated Every Frame
When a Layer receives an Events
When ImGui is Updated Every Frame
Event System
AA Engine handles events using a base and derived event-type classes and an event handler that calls a function when an event needs to be handled.
The Events are categorized into
Application events - Ex: Window-based events
Input Events - Mouse, Keyboard, etc.
Logging
AA Engine has Custom Logging MACRO library to Log important events to the Console. The Engine supports colored Logs based on the verbosity.
The Logging library is thread-safe to support when the Engine becomes a multi-threaded Engine, which is the ideal end scenario.
Input
Input is Abstracted out as a Singleton Interface-type structure using pure virtual functions.
Currently, the Input is split based on Platforms. Ex: Windows Platform currently uses GLFW to handle input.
AA Engine provides easy-to-use functions to detect Key/Mouse Button Events
Project Setup
The Engine is a Dynamic Library to which the User can link their project.
It has an Entry Point to define a main() function for the Application. This is not visible to the User and the user needs to Derive from the Engine Application class to use Engine functionality.
Every Engine API call is abstracted to make sure the User doesn't need to call or have knowledge about Third Party APIs like GLFW or OpenGL to build a small game.
Abstractions
To support different APIs used for different purposes, Abstraction becomes very important for Engine development. This allows the use of particular Engine API calls without the User of the Engine having to worry about calling specific API/Platform functions.
In AA Engine, Abstractions are crseated for Windows (Viewports), Rendering (OpenGL/Vulkan), Input Handling (Tied to GLFW)
Custom Smart Pointers
Unique Pointer
A Scoped pointer class that handles freeing of memory without the user needing to do it.
It does not allow copying of the pointer around as that gives away the ownership of the memory which is the sole reason Smart Pointers were made.
Shared Pointer and Weak Pointer
Currently not supported.
Shared Pointers provide the functionality that Unique Pointers don't, copying or transfer or multiple ownership.
While the word multiple ownership sounds very bad, the idea is a reference counting system that keeps track of how many references of a particular memory are currently being used. The memory is freed only when all the references are deleted.
Weak Pointers are an extension of the Shared Pointer which does not have any ownership and has a separate count similar to shared pointers to make sure the User knows the shared pointer memory is freed. If the Weak Pointer doesn't know about it, the Pointer would be a dangling pointer pointing to random memory.
While these tie up to an overhead with the reference counts, etc. For most of the cases, the use of them outweighs the overhead cost.
Profiling
AA Engine has Custom Profiling tools like Scoped Timers and Performance Log Files.
When the performance of a function/tool/anything else has to be measured these tools can be used to do so. These tools have been used to make sure the Engine perform the best