Hiding the API from the engine

Started by
5 comments, last by The Beholder 19 years, 11 months ago
Hi. In my engine I have a Renderer interface which is supposed to abstract different APIs. This means that everything that is specific to an API is in the class inherited from the Renderer interface. This is good since I only have to change that class if I would like to support another API (like DX). I currently only support Opengl. What is bad with this approach is that a large number of classes needs to know about the Renderer. Currently I have implemented this by keeping a static pointer to the renderer in several classes (like Texture, Mesh etc.). I could solve this by making the renderer a singleton which would give cleaner code although I don''t like the idea of globals. Now I''m thinking of converting to DX but I''d still like to keep the Opengl code. I already have classes for vectors, matrices, quaternions etc. but DX9 already have these in D3DX. DX also has classes for meshes, bones etc. I will probably use these classes since it feels unnecessary to use my own when DX''s are better. Question is: How should I abstract the use of these classes so that the engine would still be API-independent? I would need to abstract the mesh, matrix, vector and quaternion-classes. Should I create a factory class to create the API-specific classes then, and how? Another option I have thought of is to skip the API-independance altogether and focus on DX. I, myself, wouldn''t have any problem with this BUT I would still like to hide DX''s classes so they stay in a deep level of the engine. How have you solved this? I am confused...
Advertisement
why does everything need a link to the renderinterface? why not the other way around

e.g.

// this is a unified way to store a mesh so
// the renderinterface knows how to render it.
class cMesh {
// some stuff
};

class RenderInterface
{

// functions to create context and render a mesh etc.

void BeginFrame(void); // clear color load identity matrix or?
void DrawMesh( cMesh *mesh ); // draw the unified mesh
void EndFrame(void); // do some stuff, flip buffers or so?
};


when you want to draw a mesh do something like this:

RenderInterface->BeginFrame();

RenderInterface->DrawMesh( mymesh );

RenderInterface->EndFrame();
If texture, mesh, etc have different implementations based on the renderer, then make them abstract classes with pure virtual methods (interfaces in Java/C#) and, in your renderer, have a CreateTexture, CreateMesh, etc which return new objects of type Texture, Mesh, etc. Thus each renderer can have its own implementations of textures and meshes, but you can use the functionality that all textures/meshes have in common in your application by using the Texture and Mesh classes, without knowing their exact implementations.

[edited by - Matei on May 20, 2004 4:49:13 PM]
quote:Original post by Anonymous Poster
why does everything need a link to the renderinterface? why not the other way around

e.g.

// this is a unified way to store a mesh so
// the renderinterface knows how to render it.
class cMesh {
// some stuff
};

class RenderInterface
{

// functions to create context and render a mesh etc.

void BeginFrame(void); // clear color load identity matrix or?
void DrawMesh( cMesh *mesh ); // draw the unified mesh
void EndFrame(void); // do some stuff, flip buffers or so?
};


when you want to draw a mesh do something like this:

RenderInterface->BeginFrame();

RenderInterface->DrawMesh( mymesh );

RenderInterface->EndFrame();



To take this one step further, why not make every drawable object derive from some base class - this way, you don't have to add a separate method in the RenderInterface for everything you're going to want to draw.


class IRenderable { // some stuff}; class CMesh : public IRenderable{// some mesh specific stuff};class CFunkyBlobLikeThing : public IRenderable{// some funky blob like thing specific stuff};class RenderInterface{// functions to create context and render void BeginFrame(void); // clear color load identity matrix or? void DrawMesh( IRenderable* RenderObject ); // draw the anything! void EndFrame(void); // do some stuff, flip buffers or so?};   


[edited by - Jx on May 21, 2004 9:25:21 PM]

[edited by - Jx on May 21, 2004 9:25:47 PM]


Have a base api class.


struct Param{  long dxvalue;  long openglvalue;}; //load a list of these out of some files, or hardcode them i dunnoclass cAPI{public:virtual long SetParam(Param param, long argcnt, void *args) = 0;virtual long BindTexture(long texture, Param param) = 0;}; 


Then inherit them for the differnt api''s you support

class cOGL : public cAPI{public:virtual long SetParam(Param param, long argcnt, void *args){ code;}virtual long BindTexture(long texture, Param param){ glBindTexture(param->openglvalue, texture);}}; 



Then in your loading code do like:

bool cGame::LoadAPI(char *api){   if (strcmp(api, "OpenGl") == 0)   {      api = new cOGL();   }   else if (strcmp(api, "DirectX") == 0)   {      api = new cDX();   }} 



Something like that.
quote:Original post by honayboyz


Have a base api class.

(snip..)

Something like that.


That''s if you want to take the time to learn both OpenGL and Direct3D....

quote:Original post by honayboyz


Have a base api class.

(snip..)

Something like that.


This kind of design is fine if you only use basic features, i.e. nothing beyond the core of openGL 1.1, however anything beyond that and you will start having problems in implementing certain features across both API's using that interface.

Jx's method is the one that I also use(although there is a bit more to it internally). The way I get things to render is by describing the geometry with a meta-description of how it should be rendered. Now this boils down to a single 32 bit int. The renderer then knows how to translate this into the appropriate calls for the API. Sure there are some components which can be shared internally, the way I achieve this is through interfaces and inheritance:

                  RenderContext (interface seen by outside world)                         |                         |                  RenderCore (shared data i.e. pointers to interfaces for textureManager, vertexbuffermanager, vertex/fragment program management etc..)                     |       |                  DirectX   OpenGL  (Specialised classes for each API, basically all these do is create the appropriate render states and instantiate the interfaces in the RenderCore and then pass off the geometry to the shader system)   


Hope this is helpful. Note the purpose of the extra layer of the RenderCore - this is to help with modularity between DLL and client code -i.e. it would be trivial to make the RenderContext a COM interface.

James

[edited by - jamessharpe on May 22, 2004 8:42:28 AM]

[edited by - jamessharpe on May 22, 2004 8:42:54 AM]

This topic is closed to new replies.

Advertisement