Api independent engine design

Started by
13 comments, last by Alan Kemp 21 years, 6 months ago
I am designing my 3d engine to be as flexible as possible, and part of this includes loading the renderer as a dll so its simple to write a new renderer (and port to another os) at a later date. The problem I have, is how to pass data through this abstraction layer as efficiently as possibly. I want to support static meshes and 'vertex caches' (where by a vertex buffer is created and polys are added to it in small chunks before rendering when the buffer is full). My current idea is to have an IRenderBuffer class, that is subclassed by each of the renderer dll's. The IRenderer class will be resonsible for creating IRenderBuffer's, so as to allow it the chance to set up any internal data that is specific to that renderer (for example, the d3d renderer will store a pointer to the LPDIREC3DDEVICE8 inside each IRenderBuffer). Anyway, this is the way I have sketched out the code so far. I am looking for feedback as to if this is a good way of passing the data/storing the data. I would also be very intrested in hearing how other people have solved this problem.
        
class IRenderBuffer
{
protected:
    RENDEROP_TYPE RenderOpType;
    DWORD VertexSize;
public:
    bool UsesIndicies;

    unsigned int NumVerticies;
    unsigned int NumIndicies;

    unsigned int MinIndex;
    unsigned int PrimitiveCount;

    void Create(int NumVerticies, int NumIndicies, unsigned int FVF);
    void Clear();
    void Lock();
    void Unlock();
    void Insert(void * VertexData, int NumVerticies,  void * IndexData, int NumIndicies);
    void Render();
};

class IRenderBufferDirect3D8 : IRenderBuffer
{
protected:
    LPDIRECT3DDEVICE8		lpDevice;
    LPDIRECT3DVERTEXBUFFER8 	VertexBuffer;
    LPDIRECT3DINDEXBUFFER8 		IndexBuffer;
public:
    void Render()
    {
        lpDevice->SetStreamSource(0, VertexBuffer, VertexSize);

        if (UsesIndicies)
        {
            lpDevice->SetIndicies(IndexBuffer, 0);
            lpDevice->DrawIndexedPrimitive(RenderOpType, MinIndex, 
					NumVerticies, 0, PrimitiveCount);
        }
        else
        {
            lpDevice->DrawPrimitive(RenderOpType, StartVertex, PrimitiveCount);
        }
    }
};
        
Thanks for your time, Alan EDIT: Just sorting out some code formating issues. [edited by - AlanKemp on September 10, 2002 8:16:25 PM]
"There will come a time when you believe everything is finished. That will be the beginning." -Louis L'Amour
Advertisement
Ah, come on :-)

I''m sure there are people reading these forums who have abstracted their renderers out into dlls. How did you handle the problem of passing data to the renderer efficiently, but maintaining the abstraction?

Ala
"There will come a time when you believe everything is finished. That will be the beginning." -Louis L'Amour
*bump*

/me finds this one interesting...
I have never done this, but I have been planning to do it for my next engine (when I get round to starting). What I do know is that there is a rather good open source object orientated graphices engine which loads its renders from dlls, this engine is quite a long way into development so I reckon its probably worth a quick scan through the code for ideas on this.

http://ogre.sourceforge.net/

Good luck

X2K
Thanks for the tip, but unfortunatly I am alrady familiar with Ogre. Their method of passing data to the render is not very efficient once you want to use vertex buffers. They basically pass a pointer to an array of xyz coorindates, and array of indicies, and array of colours etc, and then the renderer puts these together in what ever format that render likes.

Unfortunatly this method is not very hardware friendly, you in effect end up using DrawPrimitveUP(..) for every call to the renderer - not really what nvidia would recommend.

I am going to implement the outline I presented this weekend, unless someone can give me a hint as to a better method?

Alan
"There will come a time when you believe everything is finished. That will be the beginning." -Louis L'Amour
You''re going for independence but the base class uses things like FVFs and D3DPRIMITIVETYPEs which are obviously d3d specific.

In say the OpenGLRenderer class would you then convert the FVF to some OGL friendly thing as well as the PRIMITIVETYPE?

I don''t know much OGL but know d3d so it seems a problem you''d have to solve.

Otherwise it looks like an OK way to go.
Keep in mind though that if YOU''RE not likely to implement the opengl renderer, is it really going to happen? and therefore, is there any point making your engine so called "api idependent"?

Any PC that supports OGL will support Direct3D so unless you make all the rest of your code platform independent which to me rules out dlls, then is there too much point?

just something to consider.
Toby

Gobsmacked - by Toby Murray
Thanks for your comments, they have given me a lot to think about...

quote:
You''re going for independence but the base class uses things like FVFs and D3DPRIMITIVETYPEs which are obviously d3d specific.

In say the OpenGLRenderer class would you then convert the FVF to some OGL friendly thing as well as the PRIMITIVETYPE?


I am planning on mapping my RENDEROP_TYPE enumeration to directly correspond to D3DPRIMITIVETYPE for speed. In the direct 3d renederer the value will go straight though, but in the OpenGL renderer I am going to have to switch any way to call glBegin(..) with the right value. However, I had not thought about the FVF''s. To be honest I am not that familiar with OpenGL, I know they have a vertex cache concept, but I dont really know how you use it (and therefore what data it need avaible for creation etc).

I need to look into how OpenGL handles rendering of large batches of polygons, so if anyone has any links I would be greatful.

quote:
Otherwise it looks like an OK way to go.
Keep in mind though that if YOU''RE not likely to implement the opengl renderer, is it really going to happen? and therefore, is there any point making your engine so called "api idependent"?


Yes, I am am going to write an OpenGL renderer. At the moment I use Direct3D for most things, so writing an OpenGL renderer will be a learning excercise for me. Having the Direct3D renderer written first will help me a lot as it will provide a base reference to check results against.

quote:
Any PC that supports OGL will support Direct3D so unless you make all the rest of your code platform independent which to me rules out dlls, then is there too much point?


All that will be stored inside the dll (or the .so if its linux) will be the implimentation of the class, and two functions CreateRenderer(..) and DestroyRenderer(..). The code that loads the dll/so and calls these functions will be different for different platforms, but this can be handled quite easily with #ifdef _WIN32/#ifdef _LINUX preprocessor switches.

Thanks for you comments,

Alan
"There will come a time when you believe everything is finished. That will be the beginning." -Louis L'Amour
Ok.. so I could be entirely incorrect, however I would do the following.

For both the d3d and ogl render, create a vector ( or two for indices ) and store your vertex/index buffers in there. And when you create a vertex or index buffer, return out the number assigned to the vertex buffer in your vector and store that number with your associated mesh or whatever.

Elegant no?
- Just my perspective - Hope this helps.
Andy

- edit : is this clear?

[edited by - skillfreak on September 12, 2002 5:55:57 PM]
quote:Original post by AlanKemp
I need to look into how OpenGL handles rendering of large batches of polygons, so if anyone has any links I would be greatful.


Look up glVertexPointer() in the MSDN, that should give you a start.
Thanks for the tip!

I should probably ask this in its own thread in the OpenGL forum, but how do glVertexPointer()/glNormalPointer() etc compare to use Vertex Arrays or Display Lists?

Also, I belive that gl lets you allocate memory on the graphics card that you can manually transfere vertex data into? Is this a good idea (speed/stability wise)?

Alan
"There will come a time when you believe everything is finished. That will be the beginning." -Louis L'Amour

This topic is closed to new replies.

Advertisement