ようやくモーション再生の実装終わった

さくさくっと2日くらいで実装するつもりだったMMDのモーション再生部分が、2週間もかかってしまった。C++での実装経験があって、以下のような参考にできるソースがあるのですぐできると思ったのだが・・・。

ただ、演算部分にブラックボックスが残っているとその部分はまるっとコピーしてこないといかんので完全に把握するためには、それなりに時間かけてやらないと仕方無いのだった。元ソースのC++のもの(irrmmd)も、Irrlichtの四次行列・クォータニオンブラックボックスのまま使っていてたまたま動いている状態だったので、そのままscalaに移植できなかった。今回、きっちり全部自前実装で動作させたので次からはここまでははまらないだろう。結局必要な演算は、FKには3次元ベクトルの加減算、クォータニオンの積、ベクトルへの回転適用くらいで、IKには追加してクォータニオンオイラー角への分解と、オイラー角からの構成、共役、3次元ベクトルの内積外積、正規化くらいだった。行列はむしろ経由しないことにした。


スキニングでは、ある骨の回転+移動は、

q=Quaternion(x, y, z, w)

v=Vector3(x, y, z)
のペアで表せて、

ある点p=Point(x, y, z)に適用するには、pにqを適用して結果にvを足す。
元に戻すには、vを引いてからqの共役を適用するというルールを基礎に組み立てていくのだが、ライブラリの作り方次第では、紙上で組み立てた式が全然違う形になったりするので非常によろしくなかった(演算対象の左右の順序に一貫性が無かったり)。あと、最適化のためにmutableなVector3やQuaternionを再利用するのが可読性をかなり損なうのがわかってしまった。値を作るのに数手順かかったり、変数流用で理解しにくくなる。scalaで中値演算子を適当に定義して、immutableな要素だけで計算を進めるのが結構見やすかった(遅いかもしれない?)。


scalaで実装していると、pythonluaなどの非コンパイル型の言語でやるのと違ってコンパイルが通ればそれなりに動く+柔軟に書けるのでDry原則を保ちやすいので開発自身は快適だ。今のところ、コードは5000行くらいだがrubyとかで書いたのと同じくらいの量の感覚だ(C++javaはもっと必要)。あと、ビルドツールをsbtにしてサブプロジェクトに分割したらコンパイルの遅さは軽減された。
一山超えたので、次はもっと楽に進むのを希望している、というか、まだ移植しているだけで新しい領域に踏み出していないので早く進みたいところだ。次にJBulletの組み込みをやれば、やっと去年の自分に追いつける。

その次にShaderでトゥーンとか実装する予定。