2011年12月6日火曜日

ブログを移転しました

真に勝手ながら、この度ブログ及びホームページを移転することになりました。
引越し先はコチラです

今までこのブログを見てくださってありがとうございます。

2011年11月15日火曜日

GTAIVのモデルとテクスチャをGTAViewerで読み込んでみた【一通り終了】

今回は前回途中まで読み込んでたwdrフォーマットをちゃんと読み込んでみました。以下のが前回までの状態です。

前回までは「法線データが壊れる」、「UV座標が壊れる」等の不具合があり(法線の長さが0 or 全て同じ方向を向いてる、UV座標の値が非数値 等等)、参考にしてるサイトにも詳しく記載されておらず、ここ数日困っていました。

ところが先日、より詳しく記載しているサイトを発見してしまいました。頂点データが壊れる場合の対処法の他、wtdフォーマットの読み込み方等とても役に立つ情報が満載なサイトです。おまけにサンプルコード付き!

おかげで頂点データも正しく読めるようになり、テクスチャの構造も理解することが出来ました。

というわけで、頂点読み込み修正+テクスチャ付きスネークがコチラです↓




 前回と比べ物にならない位うまく出来ました。髪のテクスチャが若干おかしいですけど・・・。

wdr内にはマテリアルの記述がなく、代わりにシェーダーに関する記述があります(というよりシェーダーがマテリアル扱いになってる)。DiffuseやらAmbientやらSpecularやらの記述が全くないので、おそらく指定先のシェーダーでそういうものがあらかじめ設定されてるんでしょうね。シェーダーの記述部分は必要なデータは「テクスチャ名」、「適用シェーダー名」くらいです。

「シェーダーで髪を綺麗に見せるような描画処理をしていて、実は何もしないとこうなっちゃうよ?」なのか、「単純に主のコーディングがおかしいんじゃないの?」のどちらなんでしょうかね?まぁどちらだったとしても、綺麗に描画できるようにインデックスいじったりシェーダー書いたりして、結局自分のコードをいじることになるんですけど。

ゲーム画面だと髪、綺麗ですね・・・

他の既存ツールでの描画結果を確認したいんですけど、GTA4のゲームデータがないと使えないものが多いです(SparkIVやOpenIV)。以前紹介したWDRViewerはテクスチャ読み込めないので確認が出来ません・・・。

とりあえずこれ以上踏み込むにはGTA4のPC版ゲームディスクを手に入れる必要があるようです。まぁ持ってませんし今後買うかは未定なので、GTA4を手に入れるまでこれは保留にしておこうと思います。

ちなみに、このスネーク以外のスキンMOD・武器MODはうまく読み込めました。でもGTA4用のスキンMODをあまり持ってないのでテスト回数が少ない、という不安要素はあります。
AK-47


スナイパーライフル

ロケットランチャー(RPG)

wdrフォーマットが読み込めるようになったおかげでwddフォーマットも読み込めるようになってます。wddフォーマットの中身は単純で圧縮したwdrデータの塊でした。

WDD変換された霧雨魔理沙

これでwftフォーマットさえ読み込めればボーンデータも揃うんですけど・・・コチラはやっぱりフォーマット仕様がわかんないので今は無理そうです。

とりあえずGTA4は今回で一区切り打って、次回からまたGTASAに戻ろうと思います。

2011年11月12日土曜日

GTAIVのモデルフォーマットをGTAViewerで読み込んでみた【テストプログラミング】

最近GTA4にMOD導入したものをよく見ようになったのでちょいとやってみました。今回読み込んだものは以下のモデル。

メタルギアシリーズの「スネーク」です。コチラのサイトで紹介されてます。

調べてみたところ、GTA4ではメッシュモデルにdffフォーマットを採用してないようで、新たに「wdr」、「wdd」、「wtd」、「wad」、「wft」等の新たなフォーマットが採用されてるようです。

.wdr・・・武器モデル、人体モデルの部品(顔等) 前作までのdffフォーマットに該当
.wdd・・・pedデータ 前作までのdffフォーマットに該当
.wtd・・・テクスチャデータ 前作までのtxdフォーマットに該当
.wad・・・アニメーションデータ 前作までのifpフォーマットに該当
.wft・・・車データ
※間違ってるかもしれません

今回はフォーマット仕様が公開されてたwdrフォーマットを読み込んでみました。

頂点バッファ・インデックスバッファさえあれば取り合えず形にはなるのでその二つだけ読み込んで描画してみました。結果はコチラ↓

とりあえずうまくいきました。フォーマット公開サイト以外にも「WDR Analyze」というアプリケーションも参考にしています。フォルダ内に.jarファイルがあったので、解凍してclassファイルを逆コンパイルしてソースコードを引っ張り出してきました。おかげで作業がはかどりました(ただこのアプリケーション、不具合があります)。

幾つかのモデルで試してみましたが、読み込みに失敗するモデルもあるようです。まだまだ改良が必要なようです。

ちなみに「WDR Viewer」という既存のツールがあるようです。コチラは特に不具合はないようです。

次はマテリアル、テクスチャも読み込むつもりです。が、wtdフォーマットに関しては無理そうです。まだ仕様が公開されてないフォーマットが幾つかあり、wtdフォーマットはその一つです。ZLib形式で圧縮してるらしく、前半部分の記述がwdrフォーマットと同様なのはわかってるんですけどね。

未知のバイナリフォーマットを解読するスキルが備わってればよかったんですけど・・・私にはそんなスキル備わってません。

まぁ今は他にやることがあるので実装はもう少し先になりそうです。

2011年11月5日土曜日

アニメーション付きColladaフォーマット出力完成

前回の続き。ようやく完成しました。

失敗してた原因ですが、前回の記事で「キーフレーム変換行列の回転部分におかしなところがある」というように書きましたが案の定。クォータニオン→オイラー角を取り出す処理に問題がありました。

以下のコードはCollada内のフレーム行列に関する記述の一部です
<node id="センター" type="JOINT">
  <translate sid="location">0.000000 6.681501 0.000000</translate>
  <rotate sid="rotateZ">0.000000 0.000000 1.000000 0.000000</rotate>
  <rotate sid="rotateY">0.000000 1.000000 0.000000 0.000000</rotate>
  <rotate sid="rotateX">1.000000 0.000000 0.000000 0.000000</rotate>
  <scale sid="scale">1.000000 1.000000 1.000000</scale>
  <node id="センター先" type="JOINT">
    <translate sid="location">0.000000 -6.474001 0.000000</translate>
    <rotate sid="rotateZ">0.000000 0.000000 1.000000 0.000000</rotate>
    <rotate sid="rotateY">0.000000 1.000000 0.000000 0.000000</rotate>
    <rotate sid="rotateX">1.000000 0.000000 0.000000 0.000000</rotate>
    <scale sid="scale">1.000000 1.000000 1.000000</scale>
  </node>
</node>

「translate」がローカル座標の値で左から順にX・Y・Z、「rotate」がローカル軸回転の値で左から順にX軸・Y軸・Z軸・角度(デグリー角)となっています。

次のコードはアニメーションの記述の一部です。
<!-- X軸回転 -->
<animation id="センター_RotationX">
  <source id="センター_input">
    <float_array id="センター_input-array" count="2">0.000000 89.500000</float_array>
    <technique_common>
      <accessor source="センター_input-array" count="2" stride="1">
        <param name="TIME" type="float" />
      </accessor>
    </technique_common>
  </source>
  <source id="センター_output">
    <float_array id="センター_output-array" count="32">0 90</float_array>
      <technique_common>
        <accessor source="#センター_output-array" count="2" stride="1">
          <param name="ANGLE" type="float" />
        </accessor>
      </technique_common>
    </source>
    <source id="センター_INTERPOLATION">
      <Name_array id="センター_INTERPOLATION-array" count="2">LINEAR LINEAR</Name_array>
      <technique_common>
        <accessor source="#センター_INTERPOLATION-array" count="2" stride="1">
          <param name="INTERPOLATION" type="Name" />
        </accessor>
      </technique_common>
    </source>
    <sampler id="センター_sampler">
      <input semantic="INPUT" source="#センター_intput" />
      <input semantic="OUTPUT" source="#センター_output" />
      <input semantic="INTERPOLATION" source="#センター_INTERPOLATION" />
    </sampler>
    <channel source="#センター_sampler" target="センター/rotateX.ANGLE" />
</animation>

<!-- 平行移動 -->
<animation id="センター_Location">
  <source id="センター_input">
    <float_array id="センター_input-array" count="2">0.000000 89.500000</float_array>
    <technique_common>
      <accessor source="センター_input-array" count="2" stride="1">
        <param name="TIME" type="float" />
      </accessor>
    </technique_common>
  </source>
  <source id="センター_output">
    <float_array id="センター_output-array" count="6">0.000000 6.681501 0.000000 0.000000 6.681501 10.000000</float_array>
      <technique_common>
        <accessor source="#センター_output-array" count="2" stride="3">
          <param name="X" type="float" />
          <param name="Y" type="float" />
          <param name="Z" type="float" />
        </accessor>
      </technique_common>
    </source>
    <source id="センター_INTERPOLATION">
      <Name_array id="センター_INTERPOLATION-array" count="2">LINEAR LINEAR</Name_array>
      <technique_common>
        <accessor source="#センター_INTERPOLATION-array" count="2" stride="1">
          <param name="INTERPOLATION" type="Name" />
        </accessor>
      </technique_common>
    </source>
    <sampler id="センター_sampler">
      <input semantic="INPUT" source="#センター_intput" />
      <input semantic="OUTPUT" source="#センター_output" />
      <input semantic="INTERPOLATION" source="#センター_INTERPOLATION" />
    </sampler>
    <channel source="#センター_sampler" target="センター/location" />
</animation>

フレーム宣言部のlocationとrotateの値をここで変更しています。
Colladaはクォータニオンをそのまま使うことが出来ないようで、rotateタグを使う必要があります。ボーン行列の回転量をクォータニオンで所持してるので、クォータニオンからX・Y・Z軸それぞれの回転量を取り出す必要となったわけです。

そこでひと通りのクォータニオン→軸回転抽出について色々調べてみました。が、全てうまくいきませんでした。今回参考にした計算式を幾つか書いておきます。

①回転量がθ、回転軸をX・Y・Zを表す
クォータニオン(w, x, y, z) = (cosθ/2, Xsinθ/2, Ysinθ/2, Zsinθ/2)

これはもう見たまんまですね。XNAで実際に計算するとこうなります。
Quaternion qt; // 既に値が設定されてるものとする
Vector3 angle = Vector3.Zero;

angle.Z = MathHelper.ToDegrees((float)Math.Asin(qt.Z) * 2.0f);
angle.Y = MathHelper.ToDegrees((float)Math.Asin(qt.Y) * 2.0f);
angle.X = MathHelper.ToDegrees((float)Math.Asin(qt.X) * 2.0f);
この方法を試してみたところ、回転軸が複数個混じってるものは正常な回転量が取り出せないようです。単一軸回転のものは正しい回転量が抽出できます。

②回転行列RがXYZ順で合成した回転行列の場合
回転量Z = atan2(R12, R11)
回転量Y = -asin(R13)
回転量X = asin(R23 / cos(回転量Y)) ※ R33が負数の場合、回転量X = 180 - 回転量X

これをXNAで計算するとこうなります。
Quaternion qt; // 既に値が設定されてるものとする
Matrix rotate = Matrix.CreateFromQuaternion(qt); // 行列化
Vector3 angle = Vector3.Zero;

angle.Z = MathHelper.ToDegrees(
    (float)Math.Atan2(rotate.M12, rotate.M11));

float radianY = -(float)Math.Asin(rotate.M13);
angle.Y = MathHelper.ToDegrees(radianY);

float cosY = (float)Math.Cos(radianY);
angle.X = MathHelper.ToDegrees(
    (float)Math.Asin(rotate.M23) / cosY);

if(rotate.M33 < 0)
  angle.X = 180 - angle.X;
これは回転行列の合成順によって使用する値が異なりますが、計算式は変わりません(全6パターン)。詳しく知りたい方はコチラを参照してください。

YawPitchRollなので合成順はYXZ。XNAは右手系なので合成順を入れ替えて回転行列の合成順をZXYとして計算してみました。

が、コチラもうまくいきませんでいた。Z・Y・Z軸単体回転のモーションをそれぞれ作りテストしてみたのですが、X軸回転だけうまくいきません。念のため6パターン全てのやり方で計算してみましたが、やはり一つの軸だけ正常な値が取り出せませんでした。上記の計算式には条件が足りないのでしょうか?

というわけでクォータニオンからの角度抽出がうまくいかずここ数日悩んでたんですが、先日あることを思い出しました。

そうだ!MATRIXタグ使ってアニメーションすればいいんじゃないかな?と。

むしろ今まで何故気付かなかったのかが今となっては謎ですが、「location」「rotate」「scale」タグを「matrix」タグに書き換えて出力しました。
<node id="センター" type="JOINT">
  <matrix sid="transform">1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 6.681501 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000</matrix>
  <node id="センター先" type="JOINT">
    <matrix sid="transform">1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 -6.474001 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000</matrix>
  </node> 
<animation id="センター_PoseMatrix">
  <source id="センター_input">
    <float_array id="センター_input-array" count="2">0.000000 0.333333</float_array>
    <technique_common>
      <accessor source="#センター_input-array" count="2" stride="1">
        <param name="TIME" type="float" />
      </accessor>
    </technique_common>
  </source>
  <source id="センター_output">
    <float_array id="センター_output-array" count="32">1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 6.681501 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 6.681501 0.000000 0.000000 1.000000 -25.750000 0.000000 0.000000 0.000000 1.000000</float_array>
    <technique_common>
      <accessor source="#センター_output-array" count="2" stride="16">
        <param name="TRANSFORM" type="float4x4" />
      </accessor>
    </technique_common>
  </source>
  <source id="センター_INTERPOLATION">
    <Name_array id="センター_INTERPOLATION-array" count="2">LINEAR LINEAR</Name_array>
    <technique_common>
      <accessor source="センター_INTERPOLATION-array" count="2" stride="1">
        <param name="INTERPOLATION" type="Name" />
      </accessor>
    </technique_common>
  </source>
  <sampler id="センター_sampler">
    <input semantic="INPUT" source="#センター_input" />
    <input semantic="OUTPUT" source="#センター_output" />
    <input semantic="INTERPOLATION" source="#センター_INTERPOLATION" />
  </sampler>
  <channel source="#センター_sampler" target="センター/transform" />
</animation>

平行移動・スケール・回転を一つにまとめたのでコード量も少なくなりました。スッキリしていいですね。

Blender2.6で出力したデータを読み込んでみました。
video

以前までのものは読み込んでも型崩れを起こしてたんですが、今回の更新でそれもなくなりました。Papervision3Dでも動作確認済みです。ようやくうまくいきました。

さて、Collada出力が完成したので以前から言ってた通りGTAViewerを更新します。前身のPMDForColladaもクォータニオン→角度抽出処理をしてるので、コチラも再度更新しておきますね。

※追記
GTAViewer、及びPMDForColladaを更新しました。

※Colladaで行列を扱う場合の注意点
以前AndroidでColladaスキンメッシュアニメーションをやった時にも少し触れてたのですが(少し的外れだったけど)、Colladaの行列は「縦行列」のようです(値が行順ではなく列順)。なので出力する際にボーンのローカル行列を転置行列に変換する必要があります(Transpose)。これ、やってるとやってないじゃ大違いです。ツールによって規模が異なりますが、大抵型崩れを起こします。ちなみにBlenderだとこうなります。
video

もはや原型すらありません。自前でCollada入出力をする方がいらっしゃるなら、行列には注意しましょう。アニメーションでも、シーンでも、ジョイントでも、行列を扱う場合は必ず転置行列化するように!

2011年10月30日日曜日

GTAViewerでアニメーション付きColladaフォーマット出力テストプログラミング

BlenderのVMDインポーターが思ったほどうまくいかないので息抜きにやってみました。アニメーション以外の出力は既に出来てるのでアニメーション部分だけ以前作った『PMDForCollada』のものを流用して組んでみました。

結果はこのようになりました。Papervision3Dで表示しました。
Papervision3Dでアニメーション其の3・4

回転で一部おかしなところがあります。アニメーションを付属してないデータは正常に描画されるので、キーフレーム変換行列の回転部分におかしなところがあるようです。

MMD→DAE変換を試したところうまくいきました。何でGTAだと失敗するんでしょうね?
video

まぁ、Blenderのスクリプトも含めぼちぼちとやっていこうと思います。

2011年10月27日木曜日

Blender2.6用VMDインポーター作成中・・・

ひと通り形になったので記事にしてみました。

Blender2.5版は作らない予定です。というのも、2.6からBlenderの日本語化が出来るようになり、いま組んでるスクリプトは日本語化していることが前提となるからです。

調べてみましたが 、公式で日本語化に対応したのはBlender2.6RCからみたいです。

今の方法だと2.5では導入できないので、おかしな動作が起きてもそれがスクリプトのバグなのかBlender2.6の不具合なのかがわからないのがネックです。

何故ネックなのかというと、以下のような現象が起こったからです。実際に見てもらいたいと思います。

まずはZ軸回転モーションをMMDとBlender2.6で読み込み比較したものです。
video

次にX軸回転モーションをMMDとBlender2.6で読み込み比較したものです。
video

膝がおかしいのはIKの使用の違いからくるものなので気にしないで下さい。それ以外はX・Z軸回転はMMDと同じ動作をしています。ところがY軸回転だとこのようになってしまいます。
video

何故このようになるのかがよくわかりません。Blender側で同じような回転を手動で行って回転角度を出力したところ、Y軸の角度は対して変わっておらずX軸の角度が大きく変貌してました。

X軸角度とY軸角度を入れ替えると、MMDと同じように回転するモーションもあればそうでないモーションもありチンプンカンプンです。

クォータニオン回転・オイラー角回転の両方で試してみましたが結果は同じでした。

これはBlender2.6の不具合なんでしょうか?それとも只私が 仕様を誤認識して起きたスクリプトの不具合なのでしょうか?よくわからないです。

2.5版でも導入できたら確認しやすかったんですけどね・・・まぁこのスクリプトは元々作る予定のなかった、オマケみたいなようなものなのでゆっくりと作っていくつもりです。

もう一度既存のスクリプトやドキュメントに目を通してみようかな?

2011年10月25日火曜日

Blender2.54~2.60対応PMDインポーター更新&リリース②

少々早すぎますが、一応日付は変わったので更新しちゃいました。
昨日言っていた通り、膝ボーンの回転制限及びボーンの移動・拡大のロック処理をBlender2.54~2.60のスクリプト全てに追加しました。

Blender2.49のスクリプトもIKの再帰回数・ウェイトを新たに反映させて更新しました。コチラは膝ボーンの処理・ロックの処理は追加してません(やり方がわからん・・・)。

※注意
今回から膝ボーンの処理を追加したため、スクリプト内で"ひざ"という文字列をutf-8形式で記入してます。万が一ですが、この文字列のせいでスクリプトが認識されない場合があるかもしれません。もしそうなった場合はスクリプトファイルを「utf-8」の文字コードで上書き保存するか、もしくは"ひざ"と書かれてある部分を「utf-8」に文字コード変換して上書き保存してください。

utf-8文字コードで開いた場合、このようになります。

s-jis文字コードで開いた場合、このようになります。

追記
VMDインポーター作成中!