The only real reason to write API code in classes vs. free functions (I hope you're grouping them in namespaces!) is so that you can swap in concrete API code if you decide to switch implementations.
To do this, you have interfaces for all your API-specific objects (renderer, mesh, texture, etc.).Program all your high-level code (like models, lights, materials) to these interfaces.
// The renderer interface.
// Your game code should work with this and not the concrete implementations.
class IRenderer
{
public:
virtual void Render() = 0;
};
Now, write concrete implementations for these interfaces, one implementation per API.
// The Direct3D 11 renderer.
class D3DRenderer : public IRenderer
{
public:
virtual void Render()
{
// Clears the screen.
context->ClearRenderTargetView(renderTargetView, { 0.0f, 0.5f, 1.0f, 1.0f });
// Render the rest of the scene...
}
private:
ID3D11DeviceContext *context;
ID3D11RenderTargetView *renderTargetView;
// Other API-specific data...
};
// The OpenGL renderer.
class OpenGLRenderer : public IRenderer
{
public:
virtual void Render()
{
// Clears the screen
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render the rest of the scene...
}
};
Somewhere in your engine, you also have some sort of abstract factory that is able to create all of these concrete implementation objects for you. When initializing the game, you pick which API to use and set up your factory to create the objects that conform to that API. Your high-level code can now work with objects like meshes and textures without needing to know which specific API you're using.
// The abstract factory.
class IEngineFactory
{
virtual IRenderer *CreateRenderer() = 0;
};
// The D3D11 factory.
class D3DEngineFactory
{
virtual IRenderer *CreateRenderer()
{
return new D3DRenderer();
}
};
// The OpenGL factory.
class OpenGLEngineFactory
{
virtual IRenderer *CreateRenderer()
{
return new OpenGLRenderer();
}
};
This is useful for porting your code to other platforms. For example, your Windows binary uses DirectX and your Unix binary uses OpenGL.