3Dモデルファイルの独自形式制作(1回目)

3D系プログラムをしていると、どうしてもついて回るのが読み込みモデリングファイルの形式です。
私は普段DirectXを使っていますので、標準形式のXFileを使えばよいではないかということになりそうですが、XFileの方言はひどいものがある上に上位にあたるXNAではXFileをサポートしないといったことになっており散々です。
よくデザイナーさんが使うmaya, 3ds max, LightWave3Dなどのソフトがありますが、それぞれのエクスポーターで出力するXFileも方言があり、Root Frameにアニメーション情報を含んだり、含まなかったり、メッシュの名前をつけたりつけなかったりと様々です。
(他にもアニメーションの名前をつけたりつけなかったりするものまで・・・)
当然標準のビューアのみで対応できるはずもなく、マテリアル色がおかしくなったり、アニメーションが壊れたり、描画すらされなかったりといった結果になります。

それぞれのソフトに対応したエクスポーターを書いてもよいのですが、現在進行中プロジェクトではmaya, max, light waveすべてからのデータが送られてくるため、3つもエクスポータを書くのはさすがに無理です。
ですので、これらの問題を埋めるためにAutodesk社が標準とするFBXというファイル形式から必要な情報を抜き出し独自形式にコンバートしたいと思います。

今回はお仕事用としてのメモも兼ねますので、すぐに次の情報をアップしたいと思います。今回はFBXのパース、バージョンが上がったSDKの使い方がメインです。

FBXにはAutodesk社が提供しているFBX SDKなるものがあり簡単にパース、エクスポートできる・・・はずなのですが、標準でメモリリークをしたり、デバイスハンドラを作成するだけのプログラムでwarningが300以上出たりと黒い話が絶えません。(200611)
そしてこのたび2009.1という(恐らく2009年1月バージョン)未来バージョンらしきものにバージョンアップされましたので使用してみることにしました。

ビルド前設定

とりあえず昔作ったパーサをビルドしようとすると、全く動きません。何やらビルド前オプションやインクルードディレクトリにも変更があったようですので設定を行います。
以下VisualStudio 2005の設定です。

  • 追加のインクルードファイルパスに\Autodesk\FBX\FbxSdk\2009.1\include; \Autodesk\FBX\FbxSdk\2009.1\include\kbaselib;を設定
  • ライブラリファイルパスに\Autodesk\FBX\FbxSdk\2009.1\libを設定
  • 追加の依存ファイルにfbxsdk_md2005d.libを設定
  • プリプロセッサ定義 : WIN32;_DEBUG;_CONSOLEK_PLUGIN;K_FBXSDK;K_NODLL;_CRT_SECURE_NO_DEPRECATE

なぜライブラリ内ですらインクルードファイルパスが相対で書かれてないのかとか、コマンド引数を指定しないとデバイスハンドラ作成メソッドすら使えないのかとか、色々言いたいことはありあますが、とりあえず設定はこれで大丈夫です。
また、今回からUnicode文字セットが使えるようになりました。
日本語ディレクトリ名が存在すると読み込めないのは相変わらずのようです。
また、日本語ディレクトリが存在してファイルが読み込めなかった場合、ファイルが存在しないエラーではなく、サポートしていないファイル形式エラーを返してくるので注意が必要です

何はともあれインポート(読み込み)

バージョンアップにより各種オブジェクト生成関数名、ファイルフォーマット定義マクロ、オブジェクト作成方法など色々変更されましたので旧バージョンのままだとやはり使えません。

以前のバージョンでは全体の管理クラスであるKFbxSdkManagerオブジェクトの作成にKFbxSdkManager::CreateKFbxSdkManagerメソッドを使用していましたが今回からメソッド名が変わりました。
まずこのオブジェクト生成と同時にインポートしてくる情報を受け取るためのKFbxSceneオブジェクトを生成します。

KFbxSdkManager *pSdkManager = NULL;
KFbxScene      *pScene = NULL;
pSdkManager = KFbxSdkManager::Create();
pScene = KFbxScene::Create(pSdkManager, "");

続いてインポータオブジェクトを生成します。
ここではファイルフォーマットの設定を行いますが、ここも旧バージョンとは違い直接ASCII文字やバイナリコードであることを示すためのマクロがKFbxImporterからなくなっています。
またKFbxStreamOptionsFbxReaderというインポート用オプションが追加され、以前はKFbxImporterで設定していた読み込むべきデータをこちらで設定することになります。

// インポータ作成
KFbxImporter* pImporter = KFbxImporter::Create(pSdkManager, "");
// インポートする情報を設定するオプション
KFbxStreamOptionsFbxReader* lImportOptions=KFbxStreamOptionsFbxReader::Create(pSdkManager, "");
// ファイルフォーマットをファイルから取得
if(!pSdkManager->GetIOPluginRegistry()->DetectFileFormat(file, iFileFormat)) {
// ファイルフォーマット取得に失敗した場合
iFileFormat = pSdkManager->GetIOPluginRegistry()->GetNativeReaderFormat();
}
// インポータにファイルフォーマットを設定
pImporter->SetFileFormat(iFileFormat);
// インポータ初期化
const bool bImportStatus = pImporter->Initialize(file);
// ファイルバージョンを取得
pImporter->GetFileVersion(iFileMajor, iFileMinor, iFileRevision);
if(!bImportStatus) {  // インポータ初期化失敗の場合エラーを表示
puts("FBXImporterの初期化に失敗しました\n");
printf("エラー内容: %s\n\n", pImporter->GetLastErrorString());
if(pImporter->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_YET ||
pImporter->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_ANYMORE)
{
puts("FBX SDK 2009.1がサポートしていないバージョンのファイルです\n");
}
}

いよいよデータ取得に入ります。
今回はノードをたどり必要情報を抜き出すところまではやりませんのでオプションを設定し、シーンオブジェクトに必要情報をロードするだけです。

// インポートする情報を設定
// とりあえずパースしたいので全てtrueで必要情報は全部読み込みます
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_MATERIAL, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_TEXTURE, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_LINK, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_SHAPE, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_GOBO, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_ANIMATION, true);
lImportOptions->SetOption(KFBXSTREAMOPT_FBX_GLOBAL_SETTINGS, true);
bStatus = pImporter->Import(pScene, lImportOptions);

これでシーンオブジェクトの中に必要情報が格納されました。
最後に後片付けを行います

lImportOptions->Destroy();
lImportOptions = NULL;
pImporter->Destroy();
pSdkManager->Destroy();
pSdkManager = NULL;

次回はシーンオブジェクトから必要情報を抜き出作業になります。
また今回作成したファイルはこちらです。
ファイルをダウンロード

担当:松浦(3D標準形式キテクレー!)