1

I write my own 3D-graphics engine for education and have some difficulties with it's architecture. I wrote classes like OpenGLTexure, OpenGLMaterial, OpenGLGpuProgram, etc. I also wrote a class ResourceManager for loading textures, shaders and materials.

Now I want to separate OpenGL graphics component of my engine in order to add DirectX support too. I have realized interfaces for graphics classes (ITexture, IMaterial). But my resource manager uses OpenGL functions to load and initialize resources (for example, shaders) and I don't know how add DirectX support with this architecture right. I think I can write factory class to create objects for every graphics API, but how can I choose what class should I use.

How can I solve this problem right?

asked Jan 6, 2018 at 15:18

1 Answer 1

1

There are a few ways you can control which type of resources to return. It will depend on how a user of your library uses the library. If they include the full source (like Unity and Unreal Engine do), the easiest way is to require a user of your library to define which system they're using - OpenGL or DirectX - at compile time. You might require the user to define some preprocessor macros like this:

#define IMPLEMENTATION_OPENGL 1

for OpenGL and

#define IMPLEMENTATION_DIRECTX 1

for DirectX.

You'd require that a user of your library defines which of those to use in some pre-compiled header. When the compiler got to the files of your library it would check which one to build, like this:

class ITextureLoader {
 public:
 ITextureLoader(const char* imagePath);
 ~ITextureLoader();
 // ... etc.
};
#if IMPLEMENTATION_OPENGL
class TextureLoaderOpenGL : public ITextureLoader {
 public:
 TetxureLoaderOpenGL(const char* imagePath);
 ~TextureLoaderOpenGL();
 //... etc.
};
#elif IMPLEMENTATION_DIRECTX
class TextureLoaderDirectX : public ITextureLoader {
 public:
 TetxureLoaderDirectX(const char* imagePath);
 ~TextureLoaderDirectX();
 //... etc.
};
#endif

Then you'd have a TextureLoaderFactory that would return an ITextureLoader object like this:

ITextureLoader* CreateTextureLoader(const char* imagePath)
{
 #if IMPLEMENTATION_OPENGL
 return new TextureLoaderOpenGL(imagePath);
 #elif IMPLEMENTATION_DIRECTX
 return new TextureLoaderDirectX(imagePath);
 #endif
}

If you aren't including the source for users, then you need to decide at runtime. It might be easier in that case to just build 2 different libraries for the user. It's not very frequently that a user wants to use both OpenGL and DirectX in their application.

You could also have them create some sort of context object that defines things like which renderer to use. They'd then pass that context object to the factory methods that create the concrete objects. Something like this:

enum Technology {
 Technology_OpenGL = 1,
 Technology_DirectX = 2
} Technology;
struct TechnologyContext {
 Technology tech;
 int majorVersion;
 int minorVersion;
};

Then your factory would look like this:

ITextureLoader* CreateTextureLoader(const TechnologyContext& ctx, const char* path)
{
 if (ctx.tech == Technology_OpenGL)
 {
 return new TextureLoaderOpenGL(path);
 }
 else
 {
 return new TextureLoaderDirectX(path);
 }
}

As a user, I would find this more cumbersome, personally.

answered Jan 6, 2018 at 16:49
1
  • Thank you very much. The second variant seems good for me :) Commented Jan 6, 2018 at 17:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.