views:

124

answers:

3

Hey guys, I was reading about creating abstraction layers to make it easy to switch between platforms. They did not go into much detail but here is what I got out if it. Is it just something like this?

void pushMatrix(){
  if (directx){
     // do directx function

  }else if (opengl){
     // do opengl function
  }

}

Is this how it works and if so can anyone point me to some examples of things that do this or some sample code?

+1  A: 

That would end up messy and inefficient. I would abstract via a scene graph, i.e. have a generalized high-level representation of the "scene". This scene would then be rendered by a "renderer" instance, which could be implemented using either OpenGL or Direct3D.

I suggest you take a look at a cross-platform graphics engine such as Ogre3d.

mhutch
Hmm that seems pretty inefficient seeing how I would have to rewrite most of my classes but I do understand what you mean by having higher level functions / classes.
Justin Meiners
The problem with your method is that you'll add a ton of unnecessary branches and scatter the different implementations all over your code. It gets worse if you have to write to more than 2 APIs - consider Direct3D 9, Direct3D 10/11, OpenGL 2.x, OpenGL 3/4, OpenGL ES 1, OpenGL ES 2, Playstation, Wii etc. By abstracting breaking out the rendering logic into separate pieces it becomes much more maintainable.
mhutch
You could also use inheritance to share logic between similar renderers, e.g. Renderer is subclassed by Direct3DRenderer and OpenGLRenderer, OpenGLRenderer is subclassed by OpenGLES2Renderer, etc.
mhutch
What do you think about a hybrid approach where I did if else for common functions that work the same in both. Then I write completely different rendering code for things like rendering models?
Justin Meiners
You could, it's really up to you, but I would try to fully encapsulate all the rendering logic inside the renderer. As an example, here's OGRE's renderers - http://bitbucket.org/sinbad/ogre/src/tip/RenderSystems/
mhutch
Looking at Ogre it looks like they just create a separate class for the core items like VBO, Texture, and FBO.
Justin Meiners
+1  A: 

Yes, that is how it works. By abstracting the graphics API you use, you are removing it from what the programmer needs to worry about. This is similar to the way GDI abstracts the device being written to, such as that the programmer needn't worry about it.

In this sense, your library of functions similar to the one above would be like the HDC. It is the interface of this abstraction.

It would be more difficult then a simple check for API, then calling the appropriate function(s). The two API's (DirectX and OpenGL) are very different, and abstracting them both into a single interface would not be easy, especially if you want to cover most of the functionality.

You would need to more abstraction then simply making universal functions. You would need to create more complex structures.

Say hypothetically that OpenGL required you to call function A, then function B to make something happen, but DX required that you call func B, then func A. To accomodate this, you would need to do something similar to this:

void functionA(...) {
  if (OpenGL) {
    glFuncA();
  } else {
    //store parameters
  }
}

void functionB(...) {
  if (OpenGL) {
    glFuncB();
  } else {
    dxFuncB();
    dxFuncA( saved params );
  }
}

It is not a very good example, but it demonstrates the principal. Creating an abstraction for to very big and different API's would require much thought, and far more effort then wrapping each function.

Alexander Rafferty
What about for things like OpenGL to OpenGL ES 2.0 ?
Justin Meiners
@Justin: OpenGL to OpenGL ES is a much smaller change. DirectX and OpenGL are fundamentally different, OpenGL and OpenGL ES are very similar (compared). That might be possible with just `if else` blocks.
peachykeen
@peachykeen Ok thank you that was what I was planning on doing.
Justin Meiners
OpenGL to OpenGL ES would be a smaller change, yes. But you would be lucky to get off with just if else blocks.
Alexander Rafferty
+2  A: 

What is usually done is to have an interface to a "generic" renderer :

class RendererInterface{
    virtual DrawMesh() = 0;
    virtual SwapBuffers() = 0;
    /// etc
}

with one implementation for each lib :

class OpenGLRenderer : public RendererInterface{
    virtual DrawMesh(){... }
    ....
}

But the concept is the same as Alexander's answer.

Calvin1602