Draco を C++ と DirectX11 で触ってみる 前編

Google の Draco が公開されています。これは 3D データ圧縮ライブラリでして、 Zip よりも効率よく圧縮ができるというのが特徴です。公開されたのは2017年1月ごろの話なので、ずいぶんと出遅れている状態ですがちょっと触ってみました。

効果的に使える場面は WebGL の環境だと思いますが、あえてデスクトップPC の DirectX11 でデータを描画するといったことを目標にします。前編では Draco の準備と初めてのモデルデータの読み込み、を行います。なお、 DirectX11 のプログラミングにおいて3角形は描画できるというという状態までは仕上がっているものとします。

Draco 準備

Google Draco は GitHub で公開されています。
https://github.com/google/draco

このプロジェクトを Clone して、手元でライブラリのビルドを行う必要があります。このときに CMake が要求されるので、開発環境にインストールされていない場合には整える必要があります。

このディレクトリでコンソール画面を開きます。

そして Visual Studio 2017 環境で、x64 のライブラリを準備するためには、以下のようにコマンドを実行します。

x:\> mkdir build
x:\> cd build
x:\> cmake ..\ -G "Visual Studio 15 2017 Win64"


正常に実行完了すると、 build フォルダで sln ファイルや vcxproj ファイルらが生成されています。
必要になるのは、 draco.sln です。このファイルを開いて、ソリューションをビルドすればライブラリが出来上がります。

バッチビルドで、 ALL_BUILD の Debug, Release 構成にビルドチェック入れてビルドするのが楽だと思います。
なお、この作業には少し時間がかかります。最後に見てみたら、ビルドしたプロジェクトは 60 個になっていました。正常に処理が終わった後、 build/Debug, build/Release のフォルダの中には、 draco のツールとライブラリが出来ているのが確認できます。

モデルデータ読み込み

今回モデルとして使用するのは、プロジェクトに入っていた bunny.drc です。javascript/example/models に入っていました。このモデルの制約条件として以下のようなものがありました。

  • 頂点情報として位置情報のみ. 法線、色は持っていない
  • マテリアル情報も保持していないよう

このような状況なので、位置情報のみを取り出して、ワイヤフレームで描画してみたいと思います。

読み込み

draco のヘッダファイルがある場所をプロジェクトに登録しておきます。そして、以下のようなコードで draco のモデルをロードします。エラー処理は省略しているので、実際に使う際にはご注意ください。

#include "draco/compression/decode.h"

void loadDraco(const char* filename)
{
  std::ifstream infile(filename, std::ios::binary);
  std::string data;
  data.assign(std::istreambuf_iterator(infile), std::istreambuf_iterator());
  
	draco::DecoderBuffer buffer;
	buffer.Init(data.data(), data.size());
	auto typeStatusOr = draco::Decoder::GetEncodedGeometryType(&buffer);
	const draco::EncodedGeometryType geom_type = typeStatusOr.value();
	if (geom_type == draco::TRIANGULAR_MESH)
	{
     // 描画モデルデータの構築
  }
}

draco::DecoderBuffer が std::string を要求するようなのでこのようなコードで読み込みました。
また、ジオメトリのタイプが、3角形メッシュのもののみ描画データを作るようにしました。

続いて頂点データ(位置情報)の取り出しについてです。
3角形メッシュであることが分かった後のコードブロックに以下の内容を実装しました。

draco::Decoder decoder;
auto statusOr = decoder.DecodeMeshFromBuffer(&buffer);

std::unique_ptr mesh = std::move(statusOr).value();
auto attr = mesh->GetNamedAttribute(draco::GeometryAttribute::POSITION);

// 頂点の取り出し.
std::array value;
for (draco::AttributeValueIndex i(0); i < attr->size(); ++i)
{
  attr->ConvertValue(i, &value[0]);
  
  DirectX::XMFLOAT3 vtx;  
  vtx.x = value[0];
  vtx.y = value[1];
  vtx.z = value[2];

  vertices.push_back(v);
}

ここで vertices という std::vector にひたすら追加しています。続いて頂点インデクスのほうも同様にして取り出し、std::vector に追加していきます。

for (draco::FaceIndex i(0); i < mesh->num_faces(); ++i)
{
  auto f = mesh->face(i);
  for (int j = 0; j < 3; ++j)
  {
    auto val = attr->mapped_index(f[j]);
    indices.push_back(val.value());
  }
}

やっていることは明快で、面の情報にアクセスして、その頂点番号を入れているだけです。ここでは元データは3角形のみで構成されているという前提のため3頂点決め打ちで入れています。

indices, vertices が揃ったら、ここから DirectX11 の ID3D11Buffer を生成します。

// 頂点バッファ
D3D11_BUFFER_DESC descBuffer{};
D3D11_SUBRESOURCE_DATA subres{};
descBuffer.Usage = D3D11_USAGE_DEFAULT;

descBuffer.BindFlags = D3D11_BIND_VERTEX_BUFFER;
descBuffer.ByteWidth = UINT(sizeof(XMFLOAT3) * vertices.size());
subres.pSysMem = vertices.data();
subres.SysMemPitch = descBuffer.ByteWidth;
hr = m_d3d11->CreateBuffer(&descBuffer, &subres, m_vb.GetAddressOf());

こうやって準備したインデックスバッファと頂点バッファで描画を行うと以下のようになりました。

結構モデルの大きさが小さいため、カメラの位置について手間取りました。カメラ位置 0.0f, 0.1f, 0.25f あたりから 原点に向かって見るという感じで描画しました。

まとめ

とりあえずモデルを読み込んで DirectX11 を使用して描画するということを確認できました。次回はもう少し頂点属性があったりマテリアル情報があったりするものを描画したいと思います。

DirectXプログラミング
すらりんをフォローする
すらりん日記

コメント

タイトルとURLをコピーしました