1
\$\begingroup\$

I'm coding a simple game engine using DirectX11, and I'm stuck on how to implement a kind of ECS. Now my rendering system look like this (sorry if the diagram is not real UML, it's just for description):

enter image description here

The architecture is really naive, I wanted to keep things as simple as possible, for learning purposes:

  • Every entity is modeled as a new class (no, inheritance, no composition), and is added to a master Renderer class with a AddEntityXXX() method, which adds it to a vector of that kind of entity.

  • Every ShaderXXX inherits from a base Shader class, which takes care of the common functionality of a shader (compiling the bytecode, binding the shader program, seting the input layout ecc..), while every subclass defines some type members (struct XXXConstantBuffer {...};) that contain shader parameters (constant buffers) to be uploaded/updated before rendering (UpdateXXXConstantBuffer() method).

  • The renderer keeps a reference to a camera object, which contains the view and projection matrices, and uses some instances of a FrameBuffer class for offscreen rendering (shadow mapping, water reflections/refractions, postprocessing).

  • Finally, the renderer renders each EntityXXX by calling a specific RenderXXX() member function and performing the appropriate work for each type of game object, like binding the specific shader, updating the constant buffers and issuing a draw call.

Now, since my engine will be more than a showcase of graphics features, I want to make each entity abstract, and I tried inheritance, but since each entity has its own "identity" it's not viable approach: an entity representing a player needs a Move() method, position and velocity state variables, while a terrain entity doesn't move ecc.. So I learned about the Component pattern and Entity Component System architecture / Game Actors. What I find most difficult in abstracting the various kinds of entities is how to manage the rendering: How can a renderer differentiate between them and update the shader parameters (constant buffers) accordingly? In some cases I need to pass to the UpdateXXXConstantBuffer() method a WorldViewProjection matrix only, in others (shadow mapping) I need to pass a LightViewprojection matrix as well, or an array of bone tranform matrices for animation.

How can a rendering engine be structured to address these issues? Please, don't give general pattern advices, I know the existence of the Component pattern, and the Entity Component System, but I'd like to learn some practical implementation for it. I mean how should an entity be structured so that a rendering process can be performed abstracting away the specific implementation of an entity/actor?

EDIT maybe my uber-question could be rephrased like this: how can I structure a simple but efficient RENDERING ENGINE? I know that there's no such thing as a generic rendering engine, but which are the guidelines (and by that I mean a foundation for an architecture/patterns) to implement one and how does this translate to a component based GAME ENGINE?

\$\endgroup\$
2
  • \$\begingroup\$ In which language are you developing? C++ templates, for example, could be very useful here. \$\endgroup\$
    – Quentin
    Commented Jun 20, 2018 at 11:31
  • \$\begingroup\$ @Quentin I'm using C++ \$\endgroup\$
    – Luca
    Commented Jun 20, 2018 at 15:54

2 Answers 2

2
\$\begingroup\$

Entities nor components need to know about how they should be rendered. The (sub)system should take care of it.

In the picture below the render system collects the data but doesn't render them yet. But rather collects them first, it's basically prepping the commands it needs to render. The collected data can be sorted if need be so objects sharing the same specifics are rendered in sequence to prevent overhead from changing textures/shader programs.

After collecting the data the rendersystem performs the actual calls to the gpu iterating over the array containing all data. The entity at this point is no longer relevant.

Since you're using c++ having simple structs should be sufficient to copy over the data from the components relevant to rendering. You shouldn't need to reference entities/components directly unless you are doing some special cases ( what they might be is entirely up to your implementation)

Here is an article that does something similar with bitkeys

enter image description here

The render struct contains meshID, MaterialID, etc. Which are just index pointers to some static collections of each loaded asset respectively. Which are then plugged into your draw calls.

Of course how you manage and reference assets is up to you. The article I've linked goes into more of the details how they used simple integers to leverage accessing assets.

\$\endgroup\$
4
  • \$\begingroup\$ I learned about the component pattern here: gameprogrammingpatterns.com/component.html I was thinking that a component should contain behaviour as well as data/state, and that a RenderComponent should encapsulate all the render data needed by the rendering subsystem (as it pertains to the same domain). You're saying that I should separate the rendering data in more components. Where are the shaders? How can the renderer choose which shader to use? Coming from my simple brute force implementation this answer is rather obscure to me. \$\endgroup\$
    – Luca
    Commented Jun 21, 2018 at 9:08
  • \$\begingroup\$ I guess I assumed a bit about your structure. You could definitely couple some things into one component. However it makes more sense to have different rendering components for each task( mesh rendering, 2d rendering, rigged mesh rendering) which contains all the data and functionality they need to render. As for shaders/materials/meshes. You want to load them into a central place respectively so your components can point to the same items. If you have a central place for shaders then it's also easier to map uniforms to them. \$\endgroup\$
    – Sidar
    Commented Jun 21, 2018 at 13:06
  • \$\begingroup\$ It's just up to you when in your update loop you decide to update the constant buffers. If you want to brute force the rendering logic you could go for a base class with a virtual "render" function and then write the logic in the sub classes directly. Just don't try to cram everything into one monster class. \$\endgroup\$
    – Sidar
    Commented Jun 21, 2018 at 13:15
  • \$\begingroup\$ I'm reading a lot of different sources, each one explains a different flavour of ECS. Some put an Update() method into the component, which a centralized Update() function calls for each entity/component. Some others put only data into components, and use a (sub)system to manage it. I need a practical implementation, even if only in pseudocode or class diagrams, I can't wrap my head around it. \$\endgroup\$
    – Luca
    Commented Jun 23, 2018 at 8:05
0
\$\begingroup\$

Im not 100% sure you really understand ECS.

Your renderer shouldn't have all the entities, as other manager systems might also work with the entities (Physics Manager moving entities, influencing your bone transformation, the position in general, etc.), while other aspects of your entities are only used by your renderer (e.g. textures) and others only by other managers (sounds, inventory...).

Now, your entites are only containers or identifier / handler for a group of components. These components are what are interesting for your renderer and managers.

For example you have a shaderComponent, saving your shader informations, a modelComponent, for 3d models, a textureComponent, saving your texture information, a positionComponent, a soundComponent, a inventoryComponent and some other components.

Your entites are handler for all of those compoents and has the option to at least have one those components, maybe even 3 or 4 for some components.

Now, your renderer interates on the entities, but only if it has the components the renderer works with: shaderComponent, textureComponent, positionComponent... Your renderer than does its thing: if an entity has a shaderComponent, load this shader. If an entity has an animationComponent, apply the bone transformation on the model. Then if the entity has a model, render the model with the textures it may have, use the position from the positionComponent and so on.

Your renderer needs to know, what components to use and what to do with it. If an entities doesn't have a component, it doesn't try to work with those.

If some components need other components to work, your renderer either needs to know this, or your components have functions on their own to deal with that. In the case, what informations to send to the UpdateXXXConstantBuffer, i would suggest to have a worldViewMatrixComponent, when your shader needs it for an entity, and other components for other cases.

Edit: So first, the renderer iterates over every entity and asks what components it has. Then it calls those components in its wished order. Now, what it does depends on the next thing.

There are basically two different approaches to components in an ECS.

  1. Components are function carrier
  2. Components are Value-Only

The first approach has the components be called in the corresponding manager. In your example, the shaderComponent loads the shader into the GPU to render the next object, then the renderComponent is called to render the object, using the loaded shader.

As you can see, the shaderComponent doesnt care, what component comes next, as the manager has to know, when to call what component type of an entity.

For example, an entity has a corresponding renderingComponent, but no shaderComponent, the rendering manager would first try to look for the shaderComonent, then, if it cant find it, just use the standard shader instead. If there is no rendererComponent either, the entity will not be drawn. An Entity with only a shader could be something like water effects or other shader effects, that are not directly drawn. Im not that deep in graphics programming, so i may lack better examples

Pros:

  • components are way more versatile, as you can implement what you want
  • Components act in a void, they dont need other components to work (basically atoms), except maybe input parameters for the update function (rendererComponent need the current position and the current deformation matrix and so on.
  • the renderer doesnt need to know anything other than the order and what informations to keep

Cons:

  • As components know nothing about other components (could be dealt with, but is bad practice), the renderer needs to do the order. If something changes in a component, it might influence the order of component calls (no loose coupling between components and manager)
  • Components NEED to have update(): Even if your component does nothing but save something, it does need to have an update function. This update() can be empty and your renderer may decide to call it, if it knows what type of component it is (like a positionComponent only containing a position), but that would mean stronger coupling of components and renderer.

The other method is to just save data in your components. I prefer this type, as it comes with some benefits to it. You can save shaders, textures and models by reference, others by value.

Pros:

  • Loose coupling: Only the renderer knows the values of the components. The components themselves dont care, what the informations are. So if you change something in the renderer, the components couldn't care less.
  • Loading from files: If you create a new type of enemy, just load the file with his texture, his model... no need to save functions in a scripting language (good for modding aswell)
  • Informations from one component can easily be used in another. No need to call an update() with different parameters, just to give position and texture and so on.

Cons: - less flexible: Your renderer needs a one-fits-all update-function. If the Enttiy has component a, b, c, do this, if it has a and b, not c, do this, and so on.

So, what do you want to do, to start a Graphics Engine. I go the "components have values only"-way

  1. Make a general Manager class, that renderer inherits from. If you want to expande on this basic graphis engine, you want to do that. TRUST ME! And do the same for components.
  2. Think of every aspect an Entity can have. Position and direction, Texture, Model... do that as atomic as possible. Working with 2d Sprites. have 2DTextureComponent and a spriteComponent. 3D? modelComponent, 3dTextureComponent and so on.
  3. Add some helper classes, like a camera (saves viewMatrix, helps with smooth camera movements and so on) and a texture manager (so textures get loaded into the gpu memory only once and return that texture ID in gpu)
  4. Now imagine a Master Manager class calls this Renderer class to create a graphical entity. It does that by givin all required informations (lets assume 2d): an Entity (lets assume by ID (a number)) with an exisiting positionComponent, a texture path and a sprite size (64 x 64 pixels or something like that. The renderer creates a spriteComponent (sprite size, no idea what other informations) and a textureComponent (texture by reference or id or whatever). It loads the texture into the gpu and is done for now.
    1. Your update( deltaTime ) of the renderer gets called. It gets (or calls for) every entity, that has corresponding components (why care for an object, if it has no spriteSize). It then iterates other every component of the wished type and does its thing: Read its positionComponent(mandatory for every Entity), calculate viewMatrix postition, then draw the entity with the given spriteSize and given texture. If it has a spriteComponent, but no textureComponent (or texture coulnt be loaded) you can fill in a default texture. If you have a shaderComponent, you can even order them and only do entities with shader A, then entities why shader b. Samething with textures (Entities with texture A -> Entities with texture B -> ...)

And that is basically it. You cant expend your Game Engine by adding more managers with their own components. Physics Component adds velocityComponent, physicalAttributCompnents (Weight and stuff, if you want it that sophisticated).

\$\endgroup\$
7
  • \$\begingroup\$ I'm trying to understand the whole picture but everyone seems to provide a slightly different implementation. Can you provide some examples/code/pseudo code/ class diagrams? \$\endgroup\$
    – Luca
    Commented Jun 23, 2018 at 8:08
  • \$\begingroup\$ You mean for the ECS? There are some good descriptions out there. gameprogrammingpatterns.com/component.html for example. This is a great book, that is easy to understand. If you want to know something specific, i might try to create a diagram or example implementation for that. \$\endgroup\$
    – PSquall
    Commented Jun 23, 2018 at 9:48
  • \$\begingroup\$ I just saw your comment on Sidars answer, so you already know about the book! :D \$\endgroup\$
    – PSquall
    Commented Jun 23, 2018 at 9:51
  • \$\begingroup\$ yes, I got the book and I find it very enlightening. But, for example, it puts Update() functions in components, which are to be called from GameObject's Update(). First, other implementations of ECS seem to put only data in components and then systems manage the data for their purposes (The details I cannot understand though). Second, these GameObjects/Entities/Actors cannot exist in a void. I need to understand the framework that binds all together, or I can't understand how to implement it. (And I need particular attention to the render System/Components) \$\endgroup\$
    – Luca
    Commented Jun 23, 2018 at 14:53
  • 1
    \$\begingroup\$ It's worth noting that the first type of ECS (Components carry functionality, not just data) is how Unity is structured. \$\endgroup\$
    – Stephan
    Commented Jun 26, 2018 at 21:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .