Search code examples
c++rendereropengl-3

OpenGL Renderer class design: flexible and simple?


I'm working on designing a rendering system for OpenGL 3.1 ( limited to just 2D for now ). What I'd like to do is really think about elegant design patterns so that I don't have to worry about some hacked up mess which is a pain to maintain and debug.

At first, I was thinking something along the lines of having a templated base class with functions which accepted different typenames as arguments. The child classes would then inherit from the base class, sending in which types they would be using as template arguments upon derivation.

Then I found out this was basically impossible.

So, I'm a bit stuck as how I should design this properly without just hacking things away. The thing is that I'm using my own vertex structures which wrap around glm vector classes, so I can have vertex pairs in a structure and stride them with memory offsets when I call glVertexAttribPointer().

In other words,

typedef struct colorVertex_d
{
    glm::vec3 Position;
    glm::vec4 Color;
}
colorVertex_t;

typedef struct textureVertex_d
{
    glm::vec3 Position;
    glm::vec2 UV;
}
textureVertex_t;

Ideally, what I'd like to do is limit the actual types available for use in this Renderer to be the vertex types which have been defined.

One way of doing this I think was by doing something along the lines of this, and then calling a Shutdown method ( kind of like an exception, just not an exception... ) if the type sent to the class isn't valid.

Still, I'm not even sure if this could be considered a good implementation in this context.

What I'm really looking for is a means for which I can support multiple buffer types ( e.g. vertex, index, normal, color, etc. ) which conforms to types like the vertex structures shown above.

Original Source

/**
  * Is an abstract class to inherit from for defining custom buffer handler classes,
  * the type name specified is the type of data stored in the buffer specifically, e.g. textureVertex_t or colorVertex_t
  */

template < typename DataT, typename ContainerT >
class RenderBuffer
{
public:
    enum RenderMethod
    {
        Stream,             // used for writing data into the buffer once per render
        Dynamic,            // values in the buffer are changed from time to time
        Static              // data is set only once, then rendered as many times as it needs to be
    };

    enum DrawMode
    {
        Triangle,
        TriangleStrip,
        Line,
        LineStrip
    };

    enum BufSetType
    {
        Alloc,
        Update
    };

    RenderBuffer( RenderMethod rm, DrawMode dm )
    {
        switch( rm )
        {
        case Stream:    mRenderMethod = GL_STREAM_DRAW;     break;
        case Dynamic:   mRenderMethod = GL_DYNAMIC_DRAW;    break;
        case Static:    mRenderMethod = GL_STATIC_DRAW;     break;
        }

        switch( dm )
        {
        case Triangle:      mDrawMode = GL_TRIANGLES;       break;
        case TriangleStrip: mDrawMode = GL_TRIANGLE_STRIP;  break;
        case Line:          mDrawMode = GL_LINES;           break;
        case LineStrip:     mDrawMode = GL_LINE_STRIP;      break;
        }
    }

    virtual ~RenderBuffer( void )
    {
    }

    virtual void Reserve( size_t sz ) = 0; // Reserve space for whatever container used to store the buffer data.

    virtual void Bind( void ) const = 0;

    virtual void UnBind( void ) const = 0;

    template < typename DataT, ContainerT >
    virtual void SetBufferData( const ShaderProgram& program, const ContainerT& data, BufSetType m )
    {
    }

    template < typename DataT, ContainerT >
    virtual void SetBufferData( const ShaderProgram& program, const DataT* data, const size_t len, BufSetType m )
    {
    }

    virtual void Render( void ) const = 0;

    size_t VertexCount;

protected:
    GLenum mDrawMode, mRenderMethod;
};

Solution

  • I typed up a bunch of stuff, but I'm still not sure what your goal or question is. You seem to be trying to create one object that encapsulates everything that OpenGL can do. My first suggestion is to not do that.

    I suggest a Mesh class, which contains a VAO and a program and knows how to render itself:

    glUseProgram(mProgramID);
    glBindVertexArray(mVAO);
    glDrawArrays(mDrawMode, 0, mNumPrimitives);
    

    I think that's what you're trying to achieve.

    Rather than templates for everything, I suggest subclasses. There's a lot of work (and possible different subclasses, parameterized functions, or overloading) to set one up, but once it's set up the rendering is simple.

    If you're dead-set on using templates, then template specialization might help you bridge the gap. But I don't really see a good use for templates here; it feels like you're trying to shoehorn them in.