Illusion Engine 05 - Layer System

Introduction

The layer system is a crucial aspect of game engine design that helps separate and manage the rendering of debugging information and gameplay content. The system comprises of layers and a layer stack, with each layer containing all the items that need to be rendered. Items that are logically related are grouped together in the same layer to promote better organization and management.

The layer stack is created by embedding all the layers in a particular order, with a pointer used to differentiate between the gameplay layers and the overlay layers (debugging layers).

Structure of Layer system

It is important to note that the layer system used in this game engine is not the same as the layers used in other game engines for scene management. In this case, the layer does not dictate the rendering relationship between objects. However, objects in a scene are only logically grouped into layers for easier management and visibility control, such as grouping all lights into a Lighting Layer, grouping all animals into an Animal Layer, or grouping all vehicles into a Vehicle Layer. These scene-level layers do not determine the order in which objects are rendered, but serve more as a logical grouping mechanism.


Implementation

Layer Class

The Layer class would be an abstract class since we have no idea how game developers will design their own layers. Inside Illusion/src/Engine/Core/Layer/… folder, create two files: Layer.h/.cpp and enter:

Layer.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma once

#include "Engine/Core/Core.h"
#include "Engine/Event/Events.h"

//--------------------namespace: Illusion starts--------------------
namespace Illusion
{
// Abstract Layer class
class Layer
{
public:
Layer(const std::string& name = "Layer");
virtual ~Layer();

// Call when it is put into the stack
virtual void OnAttach() {}
// Call when it is take out from the stack
virtual void OnDetach() {}
// Call when it is asked to update
virtual void OnUpdate() {}
// Call when it receives events
virtual void OnEvent(Event& event) {}

virtual void OnImGuiRender() {}

inline const std::string& GetName() const { return m_DebugName; }

protected:
std::string m_DebugName;
};
//--------------------namespace: Illusion ends--------------------
}

Layer.cpp:

1
2
3
4
5
6
7
8
9
10
#include "pch.h"
#include "Layer.h"
//--------------------namespace: Illusion starts--------------------
namespace Illusion
{
Layer::Layer(const std::string& debugname)
: m_DebugName(debugname) {}
Layer::~Layer() {}
//--------------------namespace: Illusion ends--------------------
}
  • The Layer class would maintain a string which is its own name.
  • OnImGuiRender() is used to render the ImGui windows when we need to debug with ImGui library.

LayerStack Class

The LayerStack class contains all layers and manage the update, adding, and removal of them. Inside Illusion/src/Engine/Core/Layer/… folder, create two files: LayerStack.h/.cpp and enter:

LayerStack.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma once

#include "Engine/Core/Core.h"
#include "Layer.h"
#include <vector>
//--------------------namespace: Illusion starts--------------------
namespace Illusion
{
class LayerStack
{
public:
LayerStack();
~LayerStack();
// Put layer into the layerstack
void PushLayer(Layer* layer);
// Put overlay layer into the layerstack
void PushOverlay(Layer* overlay);
// Take the layer out of the layerstack
void PopLayer(Layer* layer);
// Take the overlay layer out of the layerstack
void PopOverlay(Layer* overlay);
// The beginning positon of the stack
std::vector<Layer*>::iterator begin() { return m_Layers.begin(); }
// The ending position of the stack
std::vector<Layer*>::iterator end() { return m_Layers.end(); }
private:
// The layerstack
std::vector<Layer*> m_Layers;
// The position of the line between layer and overlay layer
// All the layers beneath this are layers
// All the layers above this are overlay layers
unsigned m_LayerInsertIndex = 0;
};
//--------------------namespace: Illusion ends--------------------
}

LayerStack.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "pch.h"
#include "LayerStack.h"
//--------------------namespace: Illusion starts--------------------
namespace Illusion
{
// Initialize the LayerStack
LayerStack::LayerStack() {}
// Delete all the layers and layerstack
LayerStack::~LayerStack()
{
for (Layer* layer : m_Layers)
{
layer->OnDetach();
delete layer;
}
}
// Put the layer into the layerstack
// It is put into the "layer zone"
void LayerStack::PushLayer(Layer* layer)
{
m_Layers.emplace(m_Layers.begin() + m_LayerInsertIndex, layer);
m_LayerInsertIndex++;
}
// Take the layer out from the layerstack
// Update the "layer/overlay layer line"
void LayerStack::PopLayer(Layer* layer)
{
auto it = std::find(m_Layers.begin(), m_Layers.end(), layer);
if (it != m_Layers.end())
{
m_Layers.erase(it);
m_LayerInsertIndex--;
}
}
// Put the overlay layer into the layerstacj
// It is put into the "overlay layer zone"(above the pointer)
void LayerStack::PushOverlay(Layer* overlay)
{
m_Layers.emplace_back(overlay);
}
// Take the overlay layer out from the layerstack
// It is above the pointer so it don't have to be updated
void LayerStack::PopOverlay(Layer* overlay)
{
auto it = std::find(m_Layers.begin(), m_Layers.end(), overlay);
if (it != m_Layers.end())
m_Layers.erase(it);
}
//--------------------namespace: Illusion ends--------------------
}
  • The LayerStack is implemented with a vector. The vector stores pointers of every layers.
  • The LayerStack also has an index that represents the position of the last gameplay layers. The layers before that index are gameplay layers and layers after that index are overlay layers. The index would increase everytime a new layer is pushed in and decreases everytime a layer is poped out.

Conclusion

We explored the layer system in game engine design and its role in separating debugging information from gameplay rendering. We discussed the structure of the system, including the layers and layer stack, and highlighted the differences between this system and scene-level layers in other game engines.

In conclusion, the layer system is a key component of game engine design that helps improve organization and efficiency. In the next article, we will dive into the utilization of this system by rendering the first actual window.


Illusion Engine 05 - Layer System
https://rigel.github.io/LayerSystem/
Author
Rigel
Posted on
August 15, 2022
Licensed under