views:

301

answers:

2

Currently my app uses just Direct3D9 for graphics, however in the future I' m planning to extend this to D3D10 and possibly OpenGL. The question is how can I do this in a tidy way?

At present there are various Render methods in my code

void Render(boost::function<void()> &Call)
{
    D3dDevice->BeginScene();
    Call();
    D3dDevice->EndScene();
    D3dDevice->Present(0,0,0,0);
}

The function passed then depends on the exact state, eg MainMenu->Render, Loading->Render, etc. These will then oftern call the methods of other objects.

void RenderGame()
{
    for(entity::iterator it = entity::instances.begin();it != entity::instance.end(); ++it)
        (*it)->Render();
    UI->Render();
}

And a sample class derived from entity::Base class Sprite : public Base { IDirect3DTexture9 *Tex; Point2 Pos; Size2 Size; public: Sprite(IDirect3DTexture9 *Tex, const Point2 &Pos, const Size2 &Size); virtual void Render(); };

Each method then takes care of how best to render given the more detailed settings (eg are pixel shaders supported or not).

The problem is I'm really not sure how to extend this to be able to use one of, what may be somewhat diffrent (D3D v OpenGL) render modes...

+3  A: 

Define an interface that is sufficient for your application's graphic output demands. Then implement this interface for every renderer you want to support.

class IRenderer {
  public:
    virtual ~IRenderer() {}
    virtual void RenderModel(CModel* model) = 0;
    virtual void DrawScreenQuad(int x1, int y1, int x2, int y2) = 0;
    // ...etc...
};

class COpenGLRenderer : public IRenderer {
  public:
    virtual void RenderModel(CModel* model) {
      // render model using OpenGL
    }
    virtual void DrawScreenQuad(int x1, int y1, int x2, int y2) {
      // draw screen aligned quad using OpenGL
    }
};

class CDirect3DRenderer : public IRenderer {
  // similar, but render using Direct3D
};

Properly designing and maintaining these interfaces can be very challenging though.

In case you also operate with render driver dependent objects like textures, you can use a factory pattern to have the separate renderers each create their own implementation of e.g. ITexture using a factory method in IRenderer:

class IRenderer {
  //...
    virtual ITexture* CreateTexture(const char* filename) = 0;
  //...
};

class COpenGLRenderer : public IRenderer {
  //...
    virtual ITexture* CreateTexture(const char* filename) {
      // COpenGLTexture is the OpenGL specific ITexture implementation
      return new COpenGLTexture(filename);
    }
  //...
};

Isn't it an idea to look at existing (3d) engines though? In my experience designing this kind of interfaces really distracts from what you actually want to make :)

Tobi
Because I already have the d3d9 stuff for everything I need, I just want the option to extend to other API's. Since I already have classes wrapping most d3d stuff (eg Texture, Sprite, Mesh), I could just overload those, leaving the actaul objects with API independent stuff.
Fire Lancer
A: 

I'd say if you want a really complete answer, go look at the source code for Ogre3D. They have both D3D and OpenGL back ends. http://www.ogre3d.org Basically their API kind of forces you into working in a D3D-ish way, creating buffer objects and stuffing them with data, then issuing draw calls on those buffers. That's the way the hardware likes it anyway, so it's not a bad way to go.

And then once you see how they do things, you might as well just just go ahead and use it and save yourself the trouble of having to re-implement all that it already provides. :-)

Baxissimo