Impl: Overview - little-dragons/Siedler GitHub Wiki

The place where developers discuss the implementation of this project.

Capabilities of the engine

The engine is currently not able to implement custom rendering. Rendering can be parameterized but for example no custom shader can be provided.

Storing the Scene

Requirements

Storing the scene in memory requires much thought, as there are many different things to consider:

  • Updating the scene in accordance with user-provided logic
    • This means that each component has to be looked at
    • Several thousand components could be present, each updating fast
    • This update process should happen concurrently
  • Rendering the scene in accordance with engine-provided logic
    • This means that there will have to be multiple passes (e.g. a geometry pass for lighting)
    • Only those components which can be rendered should be looked at
    • The scene should be iterated in such a way that there are little switches in shader programs, textures etc.
    • In OpenGL, as rendering happens on a single thread, iterating this graph should have a high density of OpenGL calls

Both updating and rendering cannot be reconciled in an efficient manner. The only suitable solution is to store two different graphs. For user-logic, ECS is used, for rendering, a custom Render Tree is used.

Layers

For rendering, but also for logic and input, it makes sense to split up a scene into different layers. There are only a few layers required and as such there could be made an enum describing them all. The scene then stores the layers and all layers each have a root entity. Each layer has its own systems and input handling. A layer can mark an event as handled, thus not appearing further layers. Each layer is updated front-to-back but layers are rendered back-to-front.

Active camera

Each layer has exactly one active camera. It is stored in the layer as instance data and it that value can be changed using a system.

ECS

The ECS is described in another article.

Render Tree

The Render Tree is designed to enable fast rendering. Traversing it is done on the render thread and is designed in such a way that switching shaders and textures and so forth can be done easily and also other optimizations like instanced rendering can happen efficiently.

Since UI for example UI should always be rendered after the world, it definitely needs its own rendering. One could introduce a new render tree for all layers, or one could introduce layers into the same tree. It is not really important how this is done, as this is mostly syntax. Important is the implication that every entity needs to be part of a layer.

Updating the Render Tree

Since all components which can do rendering are known (because only those provided by the engine can render), it is enough to listen to any changes of those components on all entities: Adding these components, removing these components, changing these components. Additionally, the transform of each entity may have an impact and thus should be respected.

These updates should be packed into a yet unknown format and (probably by a different thread) inserted into the Render Tree.

Layout

The tree can be traversed multiple times during the same frame. Each time, a different program may be bound, and different render tasks are given. Since the developers of this project know only extraordinarily little about a real and efficient rendering pipeline, the traversals and bound programs are for now limited to:

  • Geometrical pass for shadows
  • Mesh pass with textures
  • Transparency will have to follow eventually?