0
\$\begingroup\$

I'm currently writing a very small game engine for learning purposes, and hit a block when trying to separate the engine's logic from the "rendering" module.

My current approach is to feed my renderer with a pointer to a "Scene", iterate through its nodes, test them with a static_cast(yeah you read that right) to a "Renderable" object (for example a static mesh class that contains a material and a vertex array) and draw.

I'm aware that this approach is terrible but I can't think of an alternative. At this point I feel like my architecture is complete spaghetti. Any indication would be much appreciated.

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

I'm aware that this approach is terrible but I can't think of an alternative.

Design-wise, your approach is fine. A rendering interface should know how to consume objects that describe a single (or set of) drawing requests and translate those into state configuration and draw calls for the underlying graphics API. For your renderer, it sounds like your Renderable objects are exactly that: a representation of something to draw. So this is fine.

Implementation-wise, however, your approach has some minors flaws.

My current approach is to feed my renderer with a pointer to a "Scene", iterate through its nodes, test them with a static_cast(yeah you read that right) to a "Renderable" object (for example a static mesh class that contains a material and a vertex array) and draw.

It sounds like you are coupling the renderer to the scene this way (by having the renderer "know about" scene types, such as Scene and Node; it sounds like Renderable is a child of the latter and that the former is a broader type of scene than just a rendering one (that is, that you have Node types that are not rendering-related).

Furthermore, static_cast isn't a good way to "test" nodes. It will always "succeed" in performing a downcast (take a look at point #2 in under the Explanation section in the above link about static_cast), but using the resulting Renderable may result in a crash because the Node really was not a Renderable. dynamic_cast would be the right tool here.

But there's no reason to involve down-casting at all. Rather than give you renderer a Scene directly, just change its interface so it gets a set of Renderable objects, such as via a const std::vector<Renderable*>& parameter. Now the Renderer doesn't "know about" the Scene.

Renderable itself doesn't need to derive from Node, it can just be a base class on its own. Now your renderer is decoupled entirely from Scene and its related higher-level API.

In the Scene itself, store the array of Renderable objects for the scene separately from the nodes in the scene. You can have nodes in the scene fill this list up with Renderable objects they'd like to have drawn as part of the routine processing of your logic update. Then, at the start of rendering for that frame, you just give the renderer the list of Renderable objects that the scene has built up for that frame, and let it do its job.

The key idea here is that the "scene" is a high level concept and the renderer is a lower-level one. High level APIs can know about, and use, lower-level ones but such visibility should not go the other way. By enforcing that kind of unidirectional data flow you can keep code less coupled and simpler.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.