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\$
  • 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\$ – david van brink Dec 20 '15 at 19:49
  • \$\begingroup\$ Do you need the renderer swappable at run time or compile time? \$\endgroup\$ – Steven Dec 20 '15 at 22:46
  • \$\begingroup\$ @Steven compile time - but I'm not looking for solutions that involve #ifdef of member variables. \$\endgroup\$ – Mark Ingram Dec 21 '15 at 7:44
  • \$\begingroup\$ @davidvanbrink Who is responsible for loading your mesh data? And how is it placed into the Framebuffer? \$\endgroup\$ – Mark Ingram Dec 21 '15 at 9:57
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\$

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.