Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I've written a Vbo template class to work with OpenGL.

I'd like to set the type from a config file at runtime. For example:

<vbo type="bump_vt" ... />

Vbo* pVbo = new Vbo(bump_vt, ...);

Is there some way I can do this without a large if-else block such as:

if( sType.compareTo("bump_vt") == 0 )
    Vbo* pVbo = new Vbo(bump_vt, ...);
else if
    ...

I'm writing for multiple platforms in C++.

share|improve this question
3  
Google "factory pattern". –  Tetrad Sep 24 '12 at 14:51

3 Answers 3

up vote 5 down vote accepted

Yes, this can be done for your case using any kind of associative mapping data structure, such as C++'s standard std::map container. At the most general level, you'll be building a implementation of a design concept known as a factory.

The general concept, as I describe in this similar answer, is to use an associative mapping of key values to either the desired type of data or a function that will produce the desired type of data (via a function pointer, or similar).

In you case it looks like you have constants for the VBO types (such as bump_vt). In other words it looks like you have:

enum BufferType {
  bump_vt,
  whatever_vt,
  another_vt,
};

Since you don't have any particularly complex types or inheritance, as far as I can tell, you can implement this factory in a rather straightforward fashion using a simple wrapper around C++'s built-in std::map class:

struct VboFactory {
  void AddType (const std::string & key, BufferType type) {
    m_types[key] = type;
  }

  Vbo * CreateVbo (const std::string & key) {
    return new Vbo(m_types[key]);
  }

private:
  std::map<std::string, BufferType> m_types;
};

To make use of this class, you instantiate an instance of VboFactory somewhere and register the known set of buffer types with it; this is typically done early in the initialization process for your rendering system (the factory should probably also be a member variable of some managing subsystem, such as the renderer itself, instead of a local variable as my sample code here implies):

VboFactory factory;
factory.AddType("bump_vt", bump_vt);
factory.AddType("whatever_vt", whatever_vt);
// ...etc...

Then, you can read the VBO type from your data file as a string and send that to the factory for creation of the VBO itself:

std::string type = ReadVboTypeFromFile();
Vbo * vbo = factory.CreateVbo(type);

In a more complex world, of course, you could replace the mapping from string to buffer type constant with a mapping from string to function pointer and register function pointers with the factory instead, allowing more involved customization of the VBO construction process.

For example, your original question indicates your VBO class is a template, and template parameters (even non-type parameters, as you appear to be using) must be specified at compile time, so my simple example isn't entirely sufficient. You also have to deal with the issue of the return type of the function, and so you'll need a common base class for all VBO template classes:

struct IVbo {
  // ...
};

template<BufferType T>
struct Vbo : IVbo {
  // ...
};

typedef IVbo * (*VboCreationFunction)();

struct VboFactory {
  void AddType (const std::string & key, VboCreationFunction creationFunction) {
    m_types[key] = creationFunction;
  }

  IVbo * CreateVbo (const std::string & key) {
    return m_types[key]();
  }
}

// ...elsewhere..
IVbo * CreateBumpVbo() {
  return new Vbo<bump_vt>();
}

// ...elsewhere...
VboFactory factory;
factory.AddType("bump_vt", CreateBumpVbo);

std::string vboType = ReadVboTypeFromFile();
IVbo * vbo = factory.CreateVbo(type);

That's the gist, at least. Obviously there will be other things you'll want to handle or define yourself, such as the interface for VBOs themselves (what is and isn't in the templated version versus the base class) and error handling for the factory itself.

I should also note that this kind of approach is highly-generalized -- possibly too generalized to be the most ideal solution for your problem. If you actually have a very small VBO type set and very simple construction semantics for them, there isn't anything wrong with an if-else chain. The primarily advantage of abstracting this out into a factory comes from the ability to extend the set of created types from client code without having to modify the library code, which is a huge benefit on larger code bases... but may not be applicable to you.

share|improve this answer

Rather than hard-coding it to specific vertex types I'd be inclined to make things a little more general. After all, VBO creation is not hard-wired to any vertex type, is it? All you need is to provide a total buffer size (in bytes) in that initial glBufferData call, and using vertex types will limit you to those you've pre-defined: if you ever need to add another you'll need to go through your code and fix things up.

So, let's look at how we can create this. An obvious candidate is the parameters you'll be passing to your glVertexAttribPointer calls - these, and not VBO creation, are what really define the vertex type, so building something around them seems a more sensible approach.

So in your config you'll define a vertex type, define the number of attribs it has, and for each attrib you define it's GL type (GL_FLOAT, GL_UNSIGNED_BYTE, etc) and the number of components it uses (1, 2, 3 or 4). Then when time comes to create the VBO you walk through this list adding things up as you go until you get the vertex size; multiply that by the number of verts you want in the buffer, and hey presto - out comes the buffer size.

As a bonus this kind of setup can also be used for auto-generating the glVertexAttribPointer calls needed, so you get a really nice flexible and extensible setup with a low level of interdependency.

share|improve this answer

What's wrong with if-else blocks? If you don't have dynamic VBO types (the post didn't suggest anything like it), it's essentially the best solution. Don't fall in temptation to overdesign the solution - if it works, it's good. Everything else will just equal more code (more work / time spent) with the same results - and I don't think that is what you want.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.