0
\$\begingroup\$

I am trying to come up with a terrain visualization system and I am writing a simple graphics engine for this. For some reason, I wanted to try a new idea. Rather than using dynamic polymorphism and derive from a GameObject like class for all the entities in the screen, I go for something like this.

First, I want to have a very simple scene graph, very non-sophisticated. For this, I am using a heterogeneous vector like given here. My scene graph will be just a heterogeneous vector of possible renderable items. I will iterate through each item using visitors.

For example my Terrain class looks like following

template <typename RenderAPI>
class HeightmapTerrain
{
public:
    // constructor like things
    void render();
    void input();
    // imagine the rest
private:
    Heaightmap hm;
    Transformation tm;
    TerrainInputHandler tih;
};  

Then I will have a specialization of this for OpenGL as follows

template <> class HeightmapTerrain<OpenGLAPI> {
public:
    // as an example
    void render()
    { 
        BasicTerrainGLSLShader ts;
        ts.bind();
        hm.generateVBO();
        // etc.
    }
private:
};

typedef Terrain<OpenGLAPI> HeightmapOpenGLTerrain;

Assume all of these are in a file called Terrain.h. I am planing to include that file and I will add an instance of HeightmapOpenGLTerrain to my scene graph. This way, I can call render_visitor() in the render function of my scene graph and since it is a heterogeneous vector all will work, I hope.

The reason I am doing this is because if I want to extend the system with new graphics APIs, I will just create a new specialization and implement render etc. methods as

template <> class HeightmapTerrain<VulkanAPI> {
public:
    // as an example
    void render()
    { 
        // Vulkan implementation
    }
private:
};
typedef Terrain<VulkanAPI> HeightmapVulkanTerrain;

With this, I can use same scene graph to render and input handle etc. but now using Vulkan. It feels like I can easily change the Input mechanism as well, with this system.

Now, what I am asking is this; I am not really that great at template programming. Is this approach feasible? What are the performance and design disadvantages compared to dynamic polymorphism approach we all know and love? Is this design downright obtuse? Lastly, can I have different private members for different specializations? Let's say I wanted to also have HeightmapTerrain<OfflineRendering> to render using path tracing. Can I add an extra private member to this specialization, like Canvas cs?

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

The templates here buy you nothing. You can achieve the same effect by writing one HeightmapTerrain class that has the data you need, using the preprocessor to cordon off Vulkan variables from OpenGL variables:

struct HeightmapTerrain {
  void render();
private:
  #ifdef USE_VULKAN
     // Vulkan-specific variables here.
  #endif

  #ifdef USE_OPENGL
     // OpenGL-specific variables here.
  #endif
};

You can then implement render() in a source file; either using the preprocessor again to control which actual code gets compiled, or by putting the Vulkan implementation in one file and the OpenGL implementation in another, and feeding the appropriate one to the compiler based on build configuration.

Not only is the same net effect (compile-time selection of the rendering backend used by HeightmapTerrain), you're also gaining the advantage of having the bulk of the implementation tucked away in a source file like normal, and you don't have to duplicate the API-agnostic parts of the class either.

The template approach requires all your code to be in a header file (if you want anybody else to be able to use it), which means everything referencing that header file recompiles any time you change the implementation. In a larger project or over a long enough time, this can really add up.

The template approach also requires you to fully re-implement the specialized version of the class. You don't get to "inherit" anything from the non-specialized template when you specialize, so if all you're changing is the render() function, you're really duplicating a lot of code.


The idea of compile-time selection of your rendering API is fine; in fact, I tend to always argue in favor of that over the "polymorphism approach we all know and love," which I find absurd and overly complex in nearly all cases. But you don't need to involve templates to make it work. The preprocessor, and/or leveraging your build system, is a much simpler way to achieve the same goal.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I knew I was doing something wrong. Yet, isn't it better to have them fully typed like HeightmapOpenGLTerrain rather than have preprocessor elifs all around? I am trying to have a header only library in the end btw. The thing is, I don't like deriving everything from a GameObject like base abstract class and maintain pointers to base class. Game objects can be very varied and in this case leads to huge inheritance trees. \$\endgroup\$ – meguli Oct 27 '17 at 11:46
  • \$\begingroup\$ @meguli The method I'm proposing still creates "strongly typed" classes; templates don't make things more or less so. I'm also confused about your comments about GameObjects because they seem completely irrelevant. There is no inheritance involved in either your template-based approach or mine without templates, and no need for it. \$\endgroup\$ – user1430 Oct 27 '17 at 15:21
  • 1
    \$\begingroup\$ @meguli I would personally argue that trying to make somethign of this apparent scope header-only is not helpful. There are also ways to minimize or avoid the preprocessor clutter. I'd be happy to discuss those further in the Game Development Chat, if you are interested. \$\endgroup\$ – user1430 Oct 27 '17 at 15:22

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.