XFile方言アレコレ

最近 3ds max から独自形式を吐き出すエクスポーターを作成しているのですが、その際 3d, xfile, maxsdk の仕様など学ぶことがいろいろありました。
今回はその備忘録的な内容です。

まずは独自形式の前に一般的に知られているファイル形式を出力したいと思いました。
そこで、アニメーションを設定できるファイル形式で一般的 (?) なものとしてX形式があります。
こちらは前々回のコラムでも書いたように非常に多数の方言があり、その構造解析の方法も様々です。
しかしながら必要な情報は入っている (エクスポーターによっては) こと、テキスト形式で出力が可能であるので、内部を理解しやすい利点があります。
なので、まずは情報が使いやすい形式を探してみます。

イントロダクション

最近 3ds max から独自形式を吐き出すエクスポーターを作成しているのですが、その際 3d, xfile, maxsdk の仕様など学ぶことがいろいろありました。
今回はその備忘録的な内容です。

まずは独自形式の前に一般的に知られているファイル形式を出力したいと思いました。
そこで、アニメーションを設定できるファイル形式で一般的 (?) なものとしてX形式があります。
こちらは前々回のコラムでも書いたように非常に多数の方言があり、その構造解析の方法も様々です。
しかしながら必要な情報は入っている (エクスポーターによっては) こと、テキスト形式で出力が可能であるので、内部を理解しやすい利点があります。
なので、まずは情報が使いやすい形式を探してみます。

XFileの方言集

まず、Microsoft DirectX形式には以下のような形式があります。

  • ノーフレーム型
  • 単一フレーム型
  • 複数フレーム型

各々について解説したいと思います。

ノーフレーム型

DirectX SDKサンプルについてくる tiger.x がこれにあたります。
カスタムテンプレート、フレームノードを省略し、突然メッシュノードから始まりますが、LoadFileFromX 関数で正常に読み込めます。
アニメーション、スキンデータ、ボーン構造などは含みません。
含まれるデータは以下の通りです:

  • メッシュ座標
  • メッシュインデックス情報
  • UV座標
  • UVインデックス情報
  • テクスチャ座標
  • マテリアル情報
Mesh MeshName { // メッシュノード開始
  v;  // 頂点数
  x; y; z;,  // 頂点座標
  ...
  Faces;  // 面数
  Index; x, y, z;,  // 面を構成する頂点の数、インデックス番号
  ...
  }
MeshTextureCoords {  // テクスチャ座標
  v;  // 頂点数
  U; V;;  // UV座標
MeshMaterialList {  // 面が使用するマテリアルの番号リスト
  Materials;  // マテリアルの数
  Faces;      // 面の数
  MatIdx,     // マテリアルインデックス
  ...
  Material {  // マテリアル情報
    Color;
    TextureFileName {  // テクスチャファイル名
      name;
    }
  }
}

単一フレーム型

サブセットを内包フレーム単位で管理し、メッシュ座標にオフセット行列、初期フレームトランスフォームマトリクスを適用して初期位置を決定します。
LightWave 公式エクスポーター出力ファイル, DirectX SDK サンプル tiny.x, Microsoft 公式エクスポーターをはじめとする多くのエクスポーターでこの形式が出力されます。
主に人型のようなオブジェクトを作る際にフレーム階層構造、スキンウェイトによってボーン階層構造を定義することができます。
しかしながらここで問題になるのがサブセット座標計算式です。
出力したXファイルMicrosoft SDK に入っている XViewer で表示すると動きがおかしかったり、エラーを出して強制終了してしまう場合が多々あります。

まず動きがおかしい問題点について考えましょう。
公式 XViewer に入っている初期サブセット座標、アニメーション座標は以下のような計算式になっています。
頂点変換行列 = 子オフセットマトリクス * 子フレームトランスフォームマトリクス * 親フレームトランスフォームマトリクス * 子オフセットマトリクスの逆行列
しかしLightWaveエクスポーターでは以下のようになります。
頂点変換行列 = 子オフセットマトリクス * 子フレームトランスフォームマトリクス * 親フレームトランスフォームマトリクス
その他のエクスポーターではどのような計算式で頂点変換行列をエクスポートしているのか判別がつかなかったのでここでは書けませんが、なんらかの計算式で計算している(?) のでしょう。
これらの計算式違いによって、公式XViewerではおかしなことになってしまいます。

また LightWave XFile では頂点変換行列のフレームトランスフォームマトリクスと AnimationSet に入っているアニメーション トランスフォームマトリクスを指定フレームごとに入れ替えることによって頂点変換行列を変化させアニメーションを行っています。

次になぜか XViewer がエラーで強制終了する問題点についてです。
こちらは原因が特定しづらいのですが、なぜかデスクトップから一個階層を掘ったディレクトリだとエラーを吐き、デスクトップに置くとエラーなく表示できる。またその逆もあります。
こちらのエラーについてはあまり深く考えたくありませんので、スルーさせていただきます。。。

含まれるデータ

  • カスタムテンプレートデータ
  • フレーム階層構造
  • メッシュ座標
  • メッシュインデックス情報
  • スキンウェイト情報
  • UV座標
  • UVインデックス情報
  • テクスチャ座標
  • マテリアル情報
  • アニメーション情報

template {  // テンプレートノード
}
Frame Frame_SCENE_ROOT {  // ルートノード開始
  FrameTransformMatrix {  //ルートノードのワールド座標マトリクス
    Matrix;
  }
  Frame FrameName {  // オブジェクトの名前
    FrameTransformMatrix {  // オブジェクトのワールド座標
      Matrix;
    }
  Mesh, UV, Texture, etc...  //ノーフレーム型と同じ

SkinWeights { // スキニング情報 name; // 追随するボーン(フレーム)の名前 vertex_index; // 影響を受ける頂点インデックス ... weights; // どの程度影響を受けるか(スキンウェイト) ... Matrix; // オフセットマトリクス }

Frame name { // ボーン(フレーム)階層構造を定義 FrameTransformMatrix { // ボーン(フレーム)の変換行列 Matrix; } Frame name { ... } Frame name { ... } } .... }

AnimationSet name { // 影響を与えるオブジェクト名を定義

Animation name { { name } // 影響を与えるボーン(フレーム)名 flag; // 変換方法(回転のみ、並行移動のみ、両方など) keyframe; // キーフレーム数 ... // フラグに応じてベクトル、変換マトリクスが出力される } Animation name { ... } ... }

複数フレーム型

サブセットを最上位フレーム単位で管理します。
完全に独立したサブセットごとの管理となるので、単一フレーム型を一個のファイルにまとめたタイプがこれにあたります。
アニメーション情報も持つことができるようなのですが、各々のフレーム内部にフレーム階層構造を作った結果、エラーにより読み込まれないといったエラーになりました。
サブセット単位のキーフレームアニメーションしかサポートしていない型なのか、それとも階層構造の設計の仕方が違うのか、ドキュメントにこれらの違いについて書いていませんので理解できませんでした。
Microsoft 公式エクスポーターから出力される形式となっています。こちらは正常にLoadFileFromX 関数を使用すれば読み込むことができます。

含まれるデータ

  • カスタムテンプレートデータ
  • 複数のルートフレーム
  • メッシュ座標
  • メッシュインデックス情報
  • UV座標
  • UVインデックス情報
  • テクスチャ座標
  • マテリアル情報
  • アニメーション情報
template {  // テンプレートノード
}

Frame name { // フレーム ... // スキンウェイト、フレーム階層以外単一フレームと同様 }

Frame name { ... } ... // 複数のルートフレーム

AnimationSet default { // デフォルトと定義されている意味は・・・? Animation { } } ...

結論

結論として最も使いやすい形式は LightWave XFileです。
理由として以下のようになります。

  • 単一フレーム型で、フレーム階層構造が理解しやすい。
  • アニメーション情報が正確に出力されている。
  • 逆行列を毎回計算するのは手間なので。(コスト的な意味で)
  • 計算式が直観的でわかりやすい。

しかし、こちらを使うのであれば公式パーサである LoadFileFromX 関数は使うことができませんので手動でパースすることになります。
しかしながら、計算式が明らかであるのでパーサを作ることはそれほど難易度が高い話ではありません。

よって、私が作るエクスポーターが吐き出すX形式も LightWave 方式となります。
パーサのほうはエクスポーター公開に合わせて公開しようと思いますので、今回はこのあたりで。

松浦(エクスポーターを書く人はパーサも同時公開すべきでは・・・)