6
\$\begingroup\$

My current architecture for me game engine looks like this, though it is not accurate:

diagram Everything graphics related is done in by GraphicsEngine, and through its components, like Material, Mesh, etc). My problem is that I want to store the pointers in RenderData, but I have to include the Mesh, Material etc header files, which have included glew.

I currently change an objects material using GetRenderer().SetMaterial("xyz"), which sets a string in the renderData, to be processed by the graphics engine; then the correct pointer will be set, if it exists. This is not so modular, because the scene has graphics related files included, like glew. This is a problem.

My only solution is to store indices in RenderData. There wont be a material pointer, but instead, an index where the material is in the GraphicsEngines material store. This way, RenderData is just a "blind" integer and string store, in which the Renderer egy the GraphicsEngine works.

Is this a good solution?


Meshes have VertexData members (position, normal, texture). When I call GraphicEngine.CreateMesh(), passing the MeshName and FileName, where should the file processing go? I use Tiny Obj Loader, and I don't know where I should include it, and call its function.

I call the function from inside GraphicsEngine, then I transform the returned structures to my Mesh's structure, which I pass to the Mesh's constructor. The initialised list will assign it to the corresponding member variable. Inside Mesh, I pass the FileName to the Mesh constructor, and let it handle it all by itself. I think the first solution is better, but I don't really know why.

Maybe using GraphicsEngine to "create" assets is better than GraphicsEngine commanding assets to "be created"; but this is just a personal feeling. Which solution is better?

\$\endgroup\$
2
  • \$\begingroup\$ To solve your 'include' concerns, did you look into the pimpl idiom, where the classes only expose their public interface (they're actual pure virtual classes, or interfaces), and another class takes care of the implementation, including the storage of the data. This way you don't have to expose internal types. \$\endgroup\$
    – Vaillancourt
    Commented Dec 29, 2016 at 20:45
  • \$\begingroup\$ I feel like this question asks "is my way good? Why, why not? What are better ways?", which teeters the fence between too broad and opinion-based. \$\endgroup\$
    – Gnemlock
    Commented May 10, 2017 at 4:48

3 Answers 3

2
\$\begingroup\$

A solution is only perfect if it does everything you want it to do. If you've ever made a game before, then you'll realize that nothing is perfect based on this statement, because nothing satisfies all your needs.

Now that that's out of the way, let's speak about your current approach and a different approach to solve some of your problems.

If I understand your solution correctly, then it succesfully provides a layer of abstraction over the rendering library. If this means, that you only need to include some OpenGL headers to communicate with the library, then it's fine. Gameplay programmers aren't completely incompetent, they know how to include a simple header, even if they don't know what it's used for (and even if they don't, the IDE can do that). Some rendering engines (like LibGDX) still make the programmer use OpenGL functions to do some things (like clearing the screen). However, if you're able to abstract every piece of OpenGL and anything related to rendering away from the core library, then that'd be even better.

Here comes in the event pattern. Instead of relying on components, this pattern uses completely separate objects managed by a core class and the communication is handled by messaging.

When you for instance need to render something from the core gameplay object, then instead of telling the renderer directly to do it, you just create a small data structures containing the request, put them it a container and let the other objects read it. The renderer should take this message out and complete the request. It's similar to a bulletin board. Someone posts their request, someone else finds it, takes it and completes it.

There are multiple pros to this pattern. First of all, it's completely modular. Nothing should break if you add or remove an event reader. This way if the renderer fails, then the event handler manager class can save the players progress and turn the game off safely, instead of it pulling the whole game with itself. You can also change the components if you for example change your mind and start using DirectX instead of OpenGL.

There is one big con I can think of, and that is flexibility. If you want to make every single operation possible through messaging, then you'll need to create a protocol. For example, the rendering part should take every event, that has the headers "render", "change_material" and so on.

\$\endgroup\$
1
\$\begingroup\$

Indices is a good way to solve the problem as long as the Scene does not have to change any of the data, in which it can become quite tedious, I would even suggest actually moving the RenderData object to the GraphicsEngine and just keep an index to it in the Object instead.

One reason why it is good to use indices, is that you can handle memory more efficiently because it is easier, for example, to put the meshes after each other in memory which means access to them gets quicker, also you can move the data around in memory more easily. Another reason why indices are a good idea is that Scene gets completely agnostic for how the meshes etc. work and they can be changed without it affecting the scene in any way as long as something that does the same exists at the same index.

On the second question, I would suggest having a MeshFactory, a separate object dedicated to creating meshes. Having a separate factory would be good because you can more easily keep track of which files you have loaded so you don't load a file twice and also it makes it so that the GraphicsEngine does not have to know how the mesh loading works, only how your mesh class works and how the factory works which makes it easier to change the loading to support, for example .fbx files.

\$\endgroup\$
0
\$\begingroup\$

If you declare only pointers of Mesh etc. in RenderData's header file, I think you can use forward declaration for them?

And to let RenderData store ints and strings is not a good/bad thing, I'd rather to discuss if it's suitable. And if it's designed to overcome the include thing, I don't think it is.


For the Mesh creation question, I think to let Mesh handle the Tiny Obj is better, i.e. I think to let GameEngine command the Mesh to be created itself is better. Just think GameEngine as a general, and Mesh etc. are soldiers. The soldiers should be responsible for firing bullets, reloading etc., the general just give the order when needed. If the general has to retrieve the bullets from the armory and replace the clip for the soldiers then it would be too much isn't it?

I know sometimes it's difficult or vague to see which responsibility belongs to the "objcet manager" or "object", but I think the "object"s should at least have some ability to take care itself. For the Mesh and GameEngine architecture again, the inner implementation like Tiny Obj should be taken care by Mesh. Since GameEngine is a rather large module, it shouldn't know such low-level details. Also if you encapsulate the details in Mesh then it's easy to replace or modify them, and you won't have to touch the GameEngine when you do so.

\$\endgroup\$

You must log in to answer this question.

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