Archived

This topic is now archived and is closed to further replies.

Multi-renderer support

This topic is 6079 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m thinking of ways to implement multiple renderers in my game. The engine has thus far been written using Direct3D, but I am adding OpenGL (and eventually Glide support when I get my blasted Banshee to work) support. The graphics class layout before was as follows: CGraphics - a class, accessed Direct3D directly, handles renderstates and page flips / updates. CSprite3D - also a class, also used the D3D renderer directly, but used managed textures and stored colors, positions, alpha, etc. Also renders triangles / quads. CTexture - a texture class and texture-manager linked list that (again) used D3D. What I''m trying to figure out is the best way to support multiple renderers using higher-level code. What I''ve come up with: CGraphics - takes on the ability to render quads and polys (from CSprite3D), as well as manage textures in a dynamic array or linked list. CSprite3D - only manage sprite coordinates and "texture references" (name or index or serial code or something similar). Makes calls to CGraphics to render quads, set renderstates, etc. CTexture - manages higher-level access to textures, such as name, file size, and void pointers to CGraphics texture data. In addition, a pair of .cpp/.h files per renderer, that manage how function calls are made. Methods stored in CGraphics are actually function pointers, that are initialized when the CGraphics object is allocated, and are called at a higher-level. All API-specific functions and object accesses are stored herein. Comments? Ideas? MatrixCubed
http://MatrixCubed.org

Share this post


Link to post
Share on other sites
I'm trying to achieve a 3D_API-independant 3DEngine. It's only the beginning of the engine, but the ideas are there.

I'm using a virtual base class to define a rendering interface,
and derived classes to implement different APIs.

My interface looks like (that's the basic version, I stripped a few things out, and haven't yet implemented so many things ...)
    
class IVirtualRenderer
{
public:
// Object Management

virtual unsigned long ObjectDeclare(unsigned long *a_pulHandle,
const Vertex *const a_Vertices, unsigned long a_ulVrtxCount,
const unsigned long *const a_Triangles, const unsigned long a_ulTriCount)=0;
virtual unsigned long ObjectRelease(const unsigned long a_ulHandle)=0;

//Scene Rendering

virtual unsigned long Begin() = 0;
virtual unsigned long End() = 0;
virtual unsigned long SetCamera(const GmtMatrix3D &) = 0;
virtual unsigned long ObjectDraw(const unsigned long a_ulHandle,
const GmtMatrix3D a_mtxWorld)=0;
};

// And :

class CRdrD3D8 : public IVirtualRenderer
{ };

class CRdrOpenGL : public IVirtualRenderer
{ };


So at the beginning, I call ObjectDeclare for each 3D object, get a handle back.

And the rendering loop goes like :
Begin
SetCamera
ObjectDraw(handle0, matrix0);
ObjectDraw(handle1, matrix1);
...
End

Declaring the objects before I start (i.e. list of vertices and points) lets the class transforming my own format to what is most appropriate (VBs for DX, arrays for OpenGL).
I'll do the same for texture management, Renderstates will be included in the object types, etc ...

And there won't be any API specific functions in the derived classes, except for initializations. Standard C++ polymorphism will handle everything.


Edited by - Thibault on April 24, 2001 8:34:08 AM

Share this post


Link to post
Share on other sites
I''m trying to achieve the same thing, and as far as classes go I have about the same thing as you, MatrixCubed, but with the addition of a font class.

However, I''m taking a slightly different approach. At first I was doing it like I assume you are doing it. Having full fledged class implementations for both OpenGL and DirectX. This ended up causing a few problems tho, as I ended up duplicating a lot of code in both classes, and the DLL''s were now tied directly to the engine itself, which they really shouldn''t be.

So, I decided to take a slightly different approach. Instead of making my API specific classes heavy duty, I''m keeping them absolutely as small and bare as possibly. All of my API specific classes are no more than wrapper classes. All they do is map API specific functions to a generic interface. They don''t contain any non-API specific functions or variables.

For instance my base texture class basically looks like this:

NOTE: These are not my actual classes. I have more functions and error checking, but simplicity sake (and the fact I don''t have my code here) I''m keeping them out.


class TextureBase
{
public:
void Load(int iWidth, int iHeight, Str sFilename) = 0;
};


All of the graphics pure virtual base classes I put in a foundation library. My DirectX and OpenGL classes are dependant on that one small library, rather than my whole engine. I base my engine off of the foundation library as well, so now my renderers can be used for any engine I base off of the foundation library.

My DirectX implementation is rather simple and basically looks like so:


// dxtexture.h
class DxTexture : public TextureBase
{
protected:
LPDIRECT3DTEXTURE8 m_pd3dTexture;

public:
void Load(int iWidth, int iHeight, Str sFilename);
};

// dxtexture.cpp
void DxTexture::Load(int iWidth, int iHeight, Str sFilename)
{
D3DXCreateTexture(iWidth, iHeight, sFilename, m_pd3dTexture);
}


Notice the DirectX texture class contains only DirectX variables. It doesn''t even store width, height, or filename information. It doesn''t check if the texture has been previously loaded. It''s just pure and simply maps API specific functions to generic functions.

Now, in my engine I actually have a higher level texture class that checks if textures are previously loaded in its internal linked list, stores width, height, filename information, etc, etc, etc. The high level texture class actually contains an instance of TextureBase, which can be instantiated as a OpenGLTexture or DxTexture. When the class needs to load a texture, it calls the Load function in the TextureBase instance, like so:


void Texture::Load(int iWidth, int iHeight, Str sFilename)
{
// Store width, height, filename information
// If this texture has been previously loaded use it and return

// Otherwise...
m_pTexture->Load(iWidth, iHeight, sFilename);
}



Basically, I''m using the Bridge pattern (I believe that''s the correct pattern). I do end up using an extra level of indirection, but when it comes to loading a texture, or drawing something to the screen an extra function call is not going to make a difference.

What doing this DOES gives me, over putting everything in the same class is:

1) No code duplication. Since I''m only including API specific code in the DxTexture class I don''t have to worry about duplicating code. IE, I don''t have to check for previously loaded textures in Load() for both the DxTexture class and the OpenGLTexture class.

2) Less dependancy. Since my rendering dlls are now dependant only on a small foundation library, I can reuse them for other engines that use the same foundation library (which is generic enough to be used in every engine).

3) Easier 3rd party enhancements. If someone wants to create a Glide wrapper for my engine, they don''t have to worry about calling functions to check for previously loaded texture, etc, etc. They won''t even be able to SEE any of my engine code. All they have to do is write a simple wrapper to map the Glide functions to my functions. Easy.

4) API switching on the fly! Since I store all state information in the higher level class and the API specific class is stored inside that class, I can actually instantiate a DxTexture class and then delete the class and instantiate a OpenGLTexture class repassing it the stored information to recreate the texture. This means my engine can easily switch from DirectX to OpenGL in the middle of the game!


- Houdini


Share this post


Link to post
Share on other sites