我目前正在使用DirectX进行渲染,在C ++中开发一个小型游戏引擎项目。引擎的呈现部分包含Model
和Texture
等类。因为我想保持(相对)简单切换到另一个渲染库(例如OpenGL)(并且因为我认为它只是很好的封装),我想保持这些类的公共接口完全没有任何对DirectX的引用类型,即我想避免提供ID3D11ShaderResourceView* GetTextureHandle();
等公共功能。
但是,当Model
等类需要Texture
使用的内部纹理句柄来执行其任务时 - 例如实际渲染模型时,这就成了一个问题。为简单起见,让我们将DirectX替换为我们称之为Lib3D的任意3D渲染库。这是一个展示我面临的问题的例子:
class Texture {
private:
Lib3DTexture mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
};
class Model {
private:
Texture* mTexture;
Lib3DModel mModel;
public:
Model(std::string pFileName, Texture* pTexture)
: mTexture(pTexture), mModel(pFileName)
{
}
void Render()
{
mModel.RenderWithTexture( /* how do I get the Lib3DTexture member from Texture? */ );
}
};
当然,我可以在GetTextureHandle
中提供一个公共Texture
函数,只返回指向mTexture
的指针,但这意味着如果我更改底层渲染库,我会还必须更改该函数返回的类型,从而更改Texture
的公共接口。更糟糕的是,也许新的库甚至没有相同的结构,这意味着我必须提供全新的功能!
我能想到的最佳解决方案是让Model
成为Texture
的朋友,以便它可以直接访问Texture
的成员。然而,这似乎有点笨拙,因为我添加了更多使用Texture
的类。我从来没有过多地使用友谊,所以我不确定这是否是一个可以接受的使用案例。
所以,我的问题是:
PS:我意识到标题不是很具描述性,我为此道歉,但我真的不知道怎么说。
答案 0 :(得分:0)
你需要抽象这个mo-fo。
class TextureBase{
public:
virtual Pixels* getTexture() = 0;
virtual ~TextureBase(){}
};
class Lib3DTexture: public TextureBase {
private:
Lib3DTexture mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
Pixels* getTexture(){ return mTexture.pixels(); }
};
class Renderable{
public:
virtual void render()const = 0;
virtual ~Renderable(){}
};
class ModelBase: public Renderable{
public:
virtual ModelData* getModelData() = 0;
virtual ~ModelBase(){}
};
class Lib3DModel : ModelBase{
private:
TextureBase* texture;
ModelBase* model;
public:
Lib3DModel(std::string pFileName, Texture* pTexture): mTexture(pTexture), mModel(pFileName){}
void render()const{
model.renderWithTexture( texture.getPixels() );
}
};
class World: public Renderable{
private:
std::vector< std::shared_ptr<Renderable> > visibleData;
public:
void render()const{
for_each(visiableData.begin(),visiableData.end(),std::mem_fun(Renderable::render));
}
};
你得到了这个想法,不是保证它可以编译,而只是为了给你一个想法。另外请查看user2384250评论,也是个好主意。
答案 1 :(得分:0)
使用DirectX使Texture
模板具有默认模板参数。
所以你可以这样做:
template<typename UnderlyingType = Lib3DTexture> class Texture {
private:
UnderlyingType mTexture;
public:
Texture(std::string pFileName)
: mTexture(pFileName)
{
}
UnderlyingType UnderlyingTexture(); //returns UnderlyingType, no matter which library you use
};
我认为这可能是一种解决该问题的简洁方法,并且可以轻松地切换出底层库。
答案 2 :(得分:0)
由于2个API是互斥的,因为您可能不需要在运行时在2之间切换,我认为您应该针对构建2个不同的可执行文件,每个底层API一个。 我的意思是使用:
#if OpenGL_implementation
...
#else // DirectX
...
#if
这可能是您正在寻找的性感解决方案,也可能不是。但我相信这是更简洁的解决方案。使用大量模板(相应的重型多态行为)可能会导致比#if解决方案更多的代码膨胀,并且它也会更慢地编译(运行)。 :)
换句话说,如果您能够在2个不同的可执行文件中获得所需的2个行为,则不应允许这对您的软件体系结构产生影响。只需构建2个性感的双胞胎软件解决方案,而不是1个胖子解决:)
答案 3 :(得分:0)
是否可接受使用友谊是值得商榷的。对于您使用的每个功能,甚至是好的功能,您都有可能在代码中形成反模式。因此,只需适度使用它,并对反模式保持谨慎。
虽然您可以使用友谊,但您也可以简单地使用继承,即IGLTexture:ITexture,并在需要访问实现细节的任何地方转换为适当的接口。例如,IGLTexture可以公开与opengl相关的所有内容。
甚至还有另一种可以使用的范例。 pimpl代表 私人实施。简而言之,而不是暴露实现细节 在类中,您只需在未公开指定实现的类中提供所有实现细节。我自己一直在使用这种方法,只有一点遗憾。
//header
class Texture
{
int width, height, depth;
struct Impl;
char reserved[32];
*Impl impl;
Texture();
...
};
//cpp
struct Texture::Impl
{
union
{
int myopenglhandle;
void* mydirectxpointer;
};
};
Texture::Texture()
{
impl = new (reserved) Impl();
}
答案 4 :(得分:0)
根据我的经验,对于那些问题使用C ++继承通常会导致一个相当复杂且不可维护的项目。
基本上有两种解决方案: