I am writing graphics engine for educational pursoses and have some problems.

I need to have ability to use different vertex formats (for example position+normal+uv or position+normal+uv+bones+weights). I have different structs for these formats and have a class HardwareBuffer that stores OpenGL vertex and index buffers. The problem is to give this class the ability to work with different types of vertices. There are a way to make this class template and use like this:

HardwareBuffer<VertexPosNormUV> buffer;
buffer.addVertex(VertexPosNormUV(vector3(1, 1, 1), vector3(1, 0, 0), vector2(1, 0)));

But in my engine there is an interface IRenderer that have virtual method drawDataFromHardwaveBuffer. Also there is a class OpenGLRenderer that is inherited from it. And now I can't pass my hardware buffer object to this method because there isn't a way to use template virtual methods.

Renderer renderer = createRenderer(GraphicsDriver::OpenGL3);
renderer->drawDataFromHardwaveBuffer(buffer); // can't make this method template

I can write functions for every type of vertex but this is not a solution.

How can I solve the problem?

  • \$\begingroup\$ Some questions beforehand: - Which openGL version? - Aren't you using VBO's? \$\endgroup\$ – MABVT Mar 6 '18 at 20:29
  • \$\begingroup\$ I use OpenGL 3.3 and I use VBO for storing vertex data. \$\endgroup\$ – Nikolai Paukov Mar 8 '18 at 13:51

I'm not personally a fan of this architectural direction (giving the buffers strong vertex types and using runtime polymorphism in graphics abstractions). That said, a simple solution to this kind of problem is to hide the template argument by working with a base interface class.

In this solution your HardwareBuffer<T> type inherits from a BasicHardwareBuffer base, which provides the basic methods needed for the Renderer to use it. These basic methods are generally just ones that return a pointer to the buffer data (as a void* or similar), the size in bytes of the buffer, and some format enumeration that you can map to the underlying format enumeration for the concrete API.

In other words:

struct BasicHardwareBuffer {
  void* getBufferData();
  std::size_t getBufferSizeInBytes();
  BufferFormat getBufferFormat();

HardwareBuffer then is-a BasicHardwareBuffer:

template<typename VertexType>
struct HardwareBuffer : BasicHardwareBuffer {

drawDataFromHardwaveBuffer can take a pointer or reference to a BasicHardwareBuffer:

void Renderer::drawDataFromHardwaveBuffer(BasicHardwareBuffer& buffer) {

You can implement BasicHardwareBuffer's methods by requiring the child types pass information about the buffer size and format up from their own constructors:

HardwareBuffer<VertexType>::HardwareBuffer(std::size_t elementCount)
: BasicHardwareBuffer(elementCount * sizeof(VertexType), VertexType::Format) {
| improve this answer | |

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.