1

私は現在、レンダリングに 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関数を に提供することもできますが、これは、基になるレンダリング ライブラリを変更すると、その関数によって返される型も変更する必要があり、したがって のパブリック インターフェイスを変更する必要があることを意味します。さらに悪いことに、新しいライブラリは同じように構成されていない可能性があります。つまり、まったく新しい機能を提供する必要があります。TexturemTextureTexture

私が考えることができる最善の解決策は、のメンバーに直接アクセスできるようにModelの友達を作ることです。ただし、. 私は友情をあまり使ったことがないので、これが許容できる使用例であるかどうかはわかりません.TextureTextureTexture

だから、私の質問は次のとおりです。

  • 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

DirectXTextureを使用して、既定のテンプレート パラメーターでテンプレートを作成します。

だからあなたはこれを行うことができます:

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 つの API を切り替える必要はおそらくないため、基になる API ごとに 1 つずつ、2 つの異なる実行可能ファイルを作成することを目指すべきだと思います。つまり、次を使用することを意味します。

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

これは、あなたが探していたセクシーなソリューションかもしれませんし、そうでないかもしれません. しかし、これがよりクリーンでシンプルなソリューションだと思います。テンプレートを頻繁に使用すると (ポリモーフィックな振る舞いが多い場合)、#if ソリューションよりもコードが肥大化する可能性が高く、コンパイル (実行) も遅くなります。:)

つまり、必要な 2 つの動作を 2 つの異なる実行可能ファイルに含める余裕がある場合は、これがソフトウェア アーキテクチャに影響を与えることを許すべきではありません。1 つのファットなソフトウェア ソリューションではなく、2 つの魅力的なツイン ソフトウェア ソリューションを構築するだけです。:)

于 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++ 継承を使用すると、非常に複雑で保守不可能なプロジェクトが終了することがよくあります。

基本的に 2 つの解決策があります。

  1. すべてのデータ型を抽象化し、レンダリング レイヤーにまったく依存しないようにします。レンダリング レイヤーからいくつかのデータ構造をコピーする必要がありますが、レンダリング コードを置き換えるだけで済みます。
  2. ポータブル レンダー レイヤー (OpenGL) を選択し、それに固執します。
于 2013-06-05T19:57:36.930 に答える