2016年09月27日

Alembic形式の読込みに関するメモ

MMDBridgeなどでAlembic形式が使われていますので、Alembicのライブラリのコンパイルとか使用方法のメモです。最初は[2]を参考に、HDF5を使ったAlembicファイルを読み込んでいたのですが、なぜか1000フレームを超えるデータを読み込もうとするとエラー終了してしまうので、諦めてOgawa形式のAlembicファイルを使用することにしました。MMDBridgeでは、mmdbridge_alembic_for_houdini.pyをそのまま使っています。

Alembicのホームページは[1]にあります。SourceをクリックするとGitHubのページに移動しますので、そこからソースコードをダウンロードします。必要なのかどうかわかりませんが、ilmbaseのライブラリも準備しました。これは以前OpenEXRの準備をしたときに作っていたものを使用しました。

Alembicのコンパイルは、CMakeを使ってやりました。generate先はbuild64というディレクトリを作成してそこにソリューションファイルを生成するようにしました。設定した項目は下記の感じです。

ilmbaseのライブラリやインクルードディレクトリの設定
ALEMBIC_ILMBASE_HALF_LIB
ALEMBIC_ILMBASE_IEXMATH_LIB
ALEMBIC_ILMBASE_IEX_LIB
ALEMBIC_ILMBASE_ILMTHREAD_LIB
ALEMBIC_ILMBASE_IMATH_LIB
ILMBASE_INCLUDE_DIR

USE_HDF5などもあるのですが、最初に書いたようにうまくいかなかったので今回はOFFのままにしました。後はVisual Studioを起動してビルドすれば、Alembic.libができているはずです。

使用方法

ライブラリを使用する場合の設定です。Cドライブに直入れしている場合の例なので適当です。

追加するインクルードディレクトリ
C:\alembic-1.6\lib
C:\alembic-1.6\build64\lib
↑ Config.hのために必要
C:\OpenEXR\include

追加するライブラリディレクトリ
C:\alembic-1.6\build64\lib\Alembic\Release
C:\OpenEXR\lib
追加するライブラリファイル
Alembic.lib

ソースコードの一部
#include <Alembic/AbcCoreAbstract/All.h>
#include <Alembic/Abc/ErrorHandler.h>
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
#include <Alembic/AbcCoreOgawa/All.h>

using namespace Alembic::Abc;
using namespace Alembic::AbcGeom;

...

  // Ogawa形式のAbcファイル読込み
  Alembic::Abc::IArchive archive(Alembic::AbcCoreOgawa::ReadArchive(), FileName);
  Alembic::Abc::IObject obj = archive.getTop();
  // 子データ数の取得
  unsigned int numChildren = obj.getNumChildren();
  std::cout << "found " << numChildren << " children in file\n";

  // 子データの数だけ繰り返し
  for (int i = 0; i < numChildren; ++i)
  {
    // 子データのオブジェクトを取得
    IObject child(obj, obj.getChildHeader(i).getName());
    // さらに子データの数を取得
    std::cout << "Children " << child.getNumChildren() << "\n";
    const MetaData &md = child.getMetaData();
    std::cout << md.serialize() << "\n";

    // 子データが見つかった場合
    for (int x = 0; x<child.getNumChildren(); x++)
    {
      // 子データのオブジェクトを取得
      IObject child2(child, child.getChildHeader(x).getName());
      const MetaData &md2 = child2.getMetaData();
      std::cout << md2.serialize() << "\n";

      // 子データがメッシュデータの場合(今回はカメラの時は未処理)
      if (IPolyMeshSchema::matches(md2) || ISubDSchema::matches(md2))
      {
        std::cout << "Found a mesh " << child2.getName() << "\n";
        // we have a mesh so build a mesh object
        IPolyMesh mesh(child, child2.getName());
        // grab the schema for the object
        IPolyMeshSchema schema = mesh.getSchema();

        // now grab the time sample data for the object
        IPolyMeshSchema::Sample mesh_samp;
        // ISampleSelectorを使って取得するフレームを設定する(今回は11200番目のフレーム)
        // HDF5だと1000以上の値を設定するとなぜかエラーが発生した
        ISampleSelector ss(Abc::index_t(11200));
        schema.get(mesh_samp, ss);
        // get how many points (positions) there are
        Alembic::Util::v8::uint32_t size = mesh_samp.getPositions()->size();
        // 面数の取得
        Alembic::Abc::Int32ArraySamplePtr faceNum = mesh_samp.getFaceCounts();
        // 法線情報の取得
        IN3fGeomParam N = schema.getNormalsParam();
        // we didn't set any on write, so on read, it should be an invalid container
        // getExpandedValue() takes an optional ISampleSelector;
        // getVals() returns a TypedArraySamplePtr
        N3fArraySamplePtr nsp = N.getExpandedValue().getVals();
        // UV情報の取得
        IV2fGeomParam uv = schema.getUVsParam();
        IV2fGeomParam::Sample uvsamp = uv.getIndexedValue();
        std::cout << "size=" << size << ", faceCount=" << faceNum->size() << "\n";
        // 面の数だけ繰り返す
        for (Alembic::Util::v8::uint32_t m = 0; m < size; ++m)
        {
          // get the current point
          V3f p = mesh_samp.getPositions()->get()[m];
          N3f Normal = nsp->get()[m];
          V2f uv2 = (*(uvsamp.getVals()))[m];
          // 以上で頂点座標/法線ベクトル/テクスチャ座標を取得
        }
        for (Alembic::Util::v8::uint32_t m = 0; m < faceNum->size(); ++m)
        {
          unsigned int index[3];
          // get the current face
          for (int k = 0; k < 3; k++)
          {
            index[k] = mesh_samp.getFaceIndices()->get()[3 * m + k];
          }
          // 以上で頂点インデックスを取得
        }
      }
    }
  }

  ...

基本的に[2]を参考にさせていただいて、Alembic 1.6でちょっと仕様が変わっている感じです。

実行結果

AlembicTest_001.png

変換のサンプルがAlembicに入っているので意味はないですが、Alembicを読み込んでObjで出力してMeshLabで表示したものです。Tda式ミクさんとマリエルさんステージを使用させていただいております。

[1] http://www.alembic.io/
[2] http://jonmacey.blogspot.jp/2011/12/getting-started-with-alembic.html
posted by シンドラー at 22:14 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/442306304
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック