3
\$\begingroup\$

I have a SceneNode class which contains a Mesh instance. The Mesh class stores client side information such as vertex and index arrays (before they're uploaded to the GPU). I also have an abstracted Renderer class, for GL and D3D, which render the SceneNodes. However, I'm not sure where I should store the API specific variables, e.g. GLuint via glGenBuffers for GL, and an ID3D11Buffer for D3D.

The few options I've considered are:

  1. Create a derived Mesh class for each API, e.g. GLMesh / D3DMesh
  2. Create a derived MeshData class for each API, which is stored in the main Mesh class
  3. Store a map of Mesh to API variables in each renderer, e.g. perform a lookup of Mesh to GLuint / ID3D11Buffer for each object that is rendered (variables would have to be generated after the scene had been updated, but before rendering).
  4. Separate the logic of rendering from scenes, by visiting the SceneGraph after update, and generating a RenderGraph of all renderable nodes in the scene.

What's the recommended way of doing this?

\$\endgroup\$
4
  • 2
    \$\begingroup\$ "What's the recommended way of doing this?" ... if you can think of a way that might just barely work, somebody has done it and somebody probably thinks its the best ever way... :-) I've gone with sort-of #1 or #2 (which are variations of the same idea, composition vs inheritance). But I have the abstraction in the Shader and Framebuffer, where my Geometry class (mesh) is just data. [edit -- which is closer to your #4] \$\endgroup\$ Commented Dec 20, 2015 at 19:49
  • \$\begingroup\$ Do you need the renderer swappable at run time or compile time? \$\endgroup\$
    – Steven
    Commented Dec 20, 2015 at 22:46
  • \$\begingroup\$ @Steven compile time - but I'm not looking for solutions that involve #ifdef of member variables. \$\endgroup\$ Commented Dec 21, 2015 at 7:44
  • \$\begingroup\$ @davidvanbrink Who is responsible for loading your mesh data? And how is it placed into the Framebuffer? \$\endgroup\$ Commented Dec 21, 2015 at 9:57

1 Answer 1

2
\$\begingroup\$

In my experience, the right place to make the API break is on the model data, augmented by providing some cross-API methods to generate dynamic vertex content.

I would create a MeshData class - but I wouldn't use virtual methods. As you have indicated, your can only have one API active at a time through compilation. I would simply put a MeshData class in your DirectX class and a MeshData class in your OpenGL class. Client code can hang onto a MeshData instance and access API-specific operations when you need them without arduous forwarding of functions via base classes, or casting. Selection of which header/cpp to include will be done via your makefile and a preprocessor macro - including global/MeshData.h will either include dx/MeshData.h or ogl/MeshData.h - but the #ifdef is then limited to writing it once in the MeshData header.

As for performing the rendering, as you have indicated you can either visit the whole scene (if you need to worry about concatenation every frame) or deposit instances of each Mesh in a list from your update. If every object truly needs updating, then I would simply call a function 'QueueForRendering( MeshData *data,params... ) during the update. But that may include a lot of redundant updates on nodes that don't need it. SceneGraph issues are a different topic really, but I would be inclined to keep my list of active 'models' somewhere else so the renderer only has to traverse those models, and it can be written in an API-specific way if there are considerations there (shared VB, material batching, etc).

\$\endgroup\$

You must log in to answer this question.

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