ゆとりーなの日記

日記的な事を書いて行くと思はれる

頂点バッファの構造体にBoost.Fusionを使う

頂点バッファとかに使う頂点の構造体の記述をBoost.Fusionを使って楽しようという企画です。
べた書きすればよくないかという話もありますが、

// 頂点と基本色だけ
struct diffuse_vertex {
  float x, y, z;
  std::uint32_t color;
};

// 頂点とライティングするぜ
struct diffuse_vertex {
  float x, y, z;
  float nx, ny, nz;
};

// 頂点とテクスチャマッピングする
struct diffuse_vertex {
  float x, y, z;
  float u, v;
};

// 頂点とテクスチャマッピングだけではは飽きたらずライティングもする
struct diffuse_vertex {
  float x, y, z;
  float nx, ny, nz;
  float u, v;
};

// 等々他にもいろんな組み合わせがある

DirectXだけでも組み合わせは沢山ありますし、厄介なことに、OpenGLやPSPGuを使う場合ではメンバの順番や構造が違ったりします。

// OpenGLで頂点と基本色だけ
struct open_gl_diffuse_vertex {
  float r, g, b, a;
  float x, y, z;
};

// PSPGuで頂点と基本色だけ
struct psp_gu_vertex {
  std::uint32_t color;
  float x, y, z;
};

こんな具合では何個似たような構造体書けばええんやってことになってきますね。そこでBoost.Fusionの出番です。
先ず、各環境の差を消すためにメンバとなる構造体を用意しておきます。取り敢えず今回は簡単にするために頂点と基本色に限ってやっていくことにします。

// 頂点座標位置
struct position_t {
  float x, y, z;
};

// 基本色
#ifdef USE_OPENGL
struct diffuse_t {
  // 差を消すためにメンバには直接触らず
  // コンストラクタやアクセッサを用意しておくのがいい
  float r, g, b, a;
};
#else
struct diffuse_t {
  // 差を消すためにメンバには直接触らず
  // コンストラクタやアクセッサを用意しておくのがいい
  std::uint32_t color;
};
#endif

で、実際に使う構造体はfusionのtypedefにします。

#ifdef USE_DIRECTX
typedef boost::fusion::vector<position_t, diffuse_t> diffuse_vertex;
#else
typedef boost::fusion::vector<diffuse_t, position_t> diffuse_vertex;
#endif

fusionはドキュメントを軽く見た感じだと、中身は単純な構造体と差はないらしいのでメモリ配置も元の構造体と変らないと思います。多分。これに関しては私の英語力は以下略なんでお察しください。
で、この構造体のメンバアクセスにはBoost.Fusionのfindを使ってやります。

void foo() {
  diffuse_vertex v;
  (*boost::fusion::find<position_t>(v)).x = 0.f;
};

これでDirectXでもOpenGLでもPSPGuでも共通で使える頂点と基本色だけの構造体が出来ました。これを使えば他の組み合わせの構造体もメンバとなる構造体をそれぞれ作ってBoost.Fusionのvectorの要素に各API群の指定する順番に入れてtypedefしてやればいいだけになるので構造体をいちいち書くのに比べればいいかなぁとか思ったりしたのです。それだけです。
因みにDirectXでしか動作を試してないので悪しからず。
追記:
PSPGuでも一応動きました。
追記2:
恐らくboost::fusion::vectorの構造に依存するのでそこらへんだけは注意が必要です。現状では取り敢えず動くみたいな感じですので悪しからず。