1

我目前正在使用 DirectX 进行渲染的 C++ 小游戏引擎项目。Model引擎的渲染部分由和等类组成Texture。因为我想让它(相对)简单地切换到另一个渲染库(例如 OpenGL)(并且因为我认为它只是很好的封装),我想让这些类的公共接口完全没有对 DirectX 的任何引用类型,即我想避免提供诸如ID3D11ShaderResourceView* GetTextureHandle();.

然而,当诸如此类的类需要用于执行其任务Model的内部纹理句柄时(例如在实际渲染模型时),这就会成为一个问题。Texture为简单起见,让我们将 DirectX 替换为任意 3D 渲染库,我们将其称为 Lib3D。这是一个演示我面临的问题的示例:

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函数,但这意味着如果我更改底层渲染库,我还必须更改该函数返回的类型,从而更改. 更糟糕的是,也许新库的结构甚至不同,这意味着我必须提供全新的功能!TexturemTextureTexture

我能想到的最好的解决方案是交Model朋友,Texture这样它就可以Texture直接访问 的成员。然而,这似乎有点笨拙,因为我添加了更多使用Texture. 我从来没有过多地使用友谊,所以我不确定这是否是一个可以接受的用例。

所以,我的问题是:

  • 将 Model 声明为 Texture 的朋友是否可以接受友谊的使用?这会是一个很好的解决方案吗?
  • 如果没有,你会推荐什么?我需要完全重新设计我的班级结构吗?在这种情况下,任何提示?

PS:我意识到标题不是很具有描述性,对此我深表歉意,但我真的不知道该怎么说。

4

5 回答 5

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 评论,也是个好主意。

于 2013-06-05T18:52:43.907 回答
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
};

我认为这可能是解决该问题的一种干净的方法,并且可以轻松地允许切换出底层库。

于 2013-06-05T18:53:05.363 回答
0

由于这 2 个 API 是互斥的,并且您可能不需要在运行时在 2 个之间切换,我认为您应该针对构建 2 个不同的可执行文件,每个底层 API 一个。我的意思是使用:

#if OpenGL_implementation
  ...
#else // DirectX
  ...
#if

这可能是也可能不是您正在寻找的性感解决方案。但我相信这是更清洁、更简单的解决方案。与 #if 解决方案相比,使用大量模板(resp.大量多态行为)可能会导致更多的代码膨胀,并且它也会编译(resp.run)更慢。:)

换句话说,如果您有能力在 2 个不同的可执行文件中拥有您想要的 2 种行为,那么您不应该允许这对您的软件架构产生影响。只需构建 2 个性感的双胞胎软件解决方案,而不是 1 个胖子。:)

于 2013-06-05T18:53:20.430 回答
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();
}
于 2013-06-05T19:39:42.643 回答
0

根据我的经验,使用 C++ 继承来解决这类问题通常会结束一个相当复杂且无法维护的项目。

基本上有两种解决方案:

  1. 抽象所有数据类型,使其完全不依赖于渲染层。您将不得不从渲染层复制一些数据结构,但您只需要替换渲染代码。
  2. 选择一个可移植的渲染层(OpenGL)并坚持下去。
于 2013-06-05T19:57:36.930 回答