В моем коде рендеринга у меня очень большой блок преткновения для дизайна. В основном, что это такое, не требуется API-код (например, OpenGL-код или DirectX). Теперь я подумал о многочисленных способах решения проблемы, однако я не уверен, какой из них использовать, или как я должен улучшить эти идеи.
Чтобы привести краткий пример, я буду использовать текстуру в качестве примера. Текстура представляет собой объект, который представляет собой текстуру в памяти графического процессора, причем реализация мудрая может быть похожа каким-либо конкретным образом, то есть ли реализация использует GLuint
или LPDIRECT3DTEXTURE9
, чтобы напоминать текстуру.
Теперь вот как я думал о том, чтобы на самом деле реализовать это. Я совершенно не уверен, есть ли лучший способ, или какой путь лучше другого.
Метод 1: Наследование
Я мог бы использовать наследование, это кажется самым очевидным выбором в этом вопросе. Однако для этого метода требуются виртуальные функции, и для создания объектов Texture требуется класс TextureFactory. Для чего потребуется вызов new
для каждого объекта Texture
(например, renderer->getTextureFactory()->create()
).
Вот как я думаю об использовании наследования в этом случае:
class Texture
{
public:
virtual ~Texture() {}
// Override-able Methods:
virtual bool load(const Image&, const urect2& subRect);
virtual bool reload(const Image&, const urect2& subRect);
virtual Image getImage() const;
// ... other texture-related methods, such as wrappers for
// load/reload in order to load/reload the whole image
unsigned int getWidth() const;
unsigned int getHeight() const;
unsigned int getDepth() const;
bool is1D() const;
bool is2D() const;
bool is3D() const;
protected:
void setWidth(unsigned int);
void setHeight(unsigned int);
void setDepth(unsigned int);
private:
unsigned int _width, _height, _depth;
};
а затем для создания текстур OpenGL (или любых других API), должен быть создан подкласс, например OglTexture
.
Способ 2. Используйте "TextureLoader" или какой-либо другой класс
Этот метод так же прост, как кажется, я использую другой класс для обработки загрузки текстур. Это может или не может использовать виртуальные функции, в зависимости от обстоятельств (или я считаю, что это необходимо).
например. Полиморфный загрузчик текстур
class TextureLoader
{
public:
virtual ~TextureLoader() {}
virtual bool load(Texture* texture, const Image&, const urect2& subRect);
virtual bool reload(Texture* texture, const Image&, const urect2& subRect);
virtual Image getImage(Texture* texture) const;
};
Если бы я использовал это, объект Texture
был бы только POD-типом. Однако для того, чтобы это сработало, в классе Texture
должен присутствовать объект /ID дескриптора.
Например, это то, как я бы более чем мог реализовать его. Хотя, я могу обобщить всю идентификационную вещь, используя базовый класс. Например, базовый класс Resource
в этом случае содержит идентификатор графического ресурса.
Метод 3: Идиома Pimpl
Я мог бы использовать идиому pimpl, которая реализует загрузку/перезагрузку/etc. текстуры. Это более чем вероятно потребует абстрактного класса factory для создания текстур. Я не уверен, как это лучше, чем использование наследования. Эта идиома pimpl может использоваться в сочетании с методом 2, т.е. Объекты текстуры будут иметь ссылку (указатель) на свой загрузчик.
Способ 4. Использование концепций/полиморфизм времени компиляции
Я мог бы с другой стороны использовать полиморфизм времени компиляции и в основном использовать то, что я представил в методе наследования, кроме как без объявления виртуальных функций. Это сработает, но если бы я хотел динамически переключиться с рендеринга OpenGL на рендеринг DirectX, это не было бы лучшим решением. Я бы просто поместил специальный код OpenGL/D3D в класс Texture, где было бы несколько классов текстур с каким-то одним и тем же интерфейсом (load/reload/getImage/etc.), Завернутым в какое-то пространство имен (похожее на тот API, который он использует, например, ogl
, d3d
и т.д.).
Метод 5: Использование целых чисел
Я мог бы просто использовать целые числа для хранения дескрипторов для объектов текстуры, это кажется довольно простым, но может создать какой-то "грязный" код.
Эта проблема также присутствует и для других ресурсов графического процессора, таких как Geometry, Shaders и ShaderPrograms.
Я также подумал о том, как сделать класс Renderer обработкой создания, загрузки и т.д. графических ресурсов. Однако это нарушит SPR. например
Texture* texture = renderer->createTexture(Image("something.png"));
Image image = renderer->getImage(texture);
Может ли кто-нибудь, пожалуйста, направить меня, я думаю, что слишком много думал об этом. Я пробовал наблюдать за различными механизмами рендеринга, такими как Irrlicht, Ogre3D и другие, которые я нашел в Интернете. Ogre и Irrlicht используют наследование, однако я не уверен, что это лучший путь. Поскольку некоторые другие просто используют void *, целые числа или просто добавляют API-код (в основном OpenGL) в свои классы (например, GLuint непосредственно в классе Texture). Я действительно не могу решить, какой дизайн будет наиболее подходящим для меня.
Платформами, на которые я нацелен, являются:
- Windows/Linux/Mac
- IOS
- Возможно, Android
Я рассматриваю просто использовать специальный код OpenGL, поскольку OpenGL работает для всех этих платформ. Тем не менее, я чувствую, что если я это сделаю, мне придется очень сильно изменить свой код, если я хочу портировать другие платформы, которые не могут использовать OpenGL, например, PS3. Любые советы по моей ситуации будут очень признательны.