[Animation] colladaファイルの読み取りからAnimationを行うまで

# collada形式とは
colladaはソニー・インタラクティブエンタテインメントにより開発された3Dモデルをあつかうデータ形式で、今はKhronos Groupが管理しているデータフォーマットのこと。
3Dモデルを扱うデータフォーマットとしては他にFBXやOBJ形式など色々あるが、自作のコードででパースしてアニメーションを行いたかったため

- テキスト形式であること（つまりバイナリ形式でないこと）
- アニメーション情報を扱えること
- blenderで扱えるファイル形式であること（ファイル変換などがblenderで行えると色々なアニメーションファイルを扱えるので）

の要素から、collada形式をチョイスし、colladaファイルをパースしてアニメーションをレンダリングしてみた。

# 結果
まったくもって完璧ではないが、とりあえずモデルを動かすということには成功。
同期の制御がまだまだ甘く、動きがカクカクしている点は今後の改善ポイント。
グラフィクスAPIとしてはVulkanを用いてAndoridで動作しており、描画領域はすべてray tracingでレンダリングしている。
モデルのレンダリングにray tracingを使うメリットはほとんどないと思うが、学習用にと思っての実装。
画面が揺れているのは、デバッグ用にジャイロ操作でカメラ位置を変えるようにしているから。（酔ってしまうようであればすみません）
左下のボタンはimguiで実装。

<iframe width="322" height="572" src="https://www.youtube.com/embed/ns4k1lTZJGU" title="20220821 cowboy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### モデルのライセンス
配布元リポジトリ
https://github.com/TheThinMatrix/OpenGL-Animation
> Unlicense license
> <https://unlicense.org/>

重要なヒントを与えてくれたTheThinMatrix様には最大限の感謝を！
こちらの動画で解説されているので、さらなる学習を望む方はどうぞ。
https://www.youtube.com/playlist?list=PLRIWtICgwaX2tKWCxdeB7Wv_rTET9JtWW

# colladaファイルの読み取りからアニメーションを実行するまで
先に紹介したTheThinMatrix様はJava+OpenGLという組み合わせでレンダリングしているが、私の例ではCpp+Vulkanの組み合わせでレンダリングしている。
実装のすべてに言及すると範囲が大きくなりすぎるので、ここではcolladaファイルを読み取ってanimationを動かす際の、座標変換の部分に絞って記事にしようと思う。
その他は簡単な流れやキーワードだけ紹介。

## collada仕様書
https://www.khronos.org/collada/
のページ内に仕様書がある。
日本語の仕様書もあるので、かなりありがたい。

## boost ptreeでのファイルのパース
colladaはXML形式のテキストファイルなので、

- 自作のライブラリでがんばって読み取る
- 既存のライブラリ(OSS)の利用
  - boost::ptree
  - その他 

などで分類できると思う。
私はptreeを利用。

## Animationの実装
### 格納位置
アニメーション情報は<b>library_animations</b>要素に各jointごとに記載されている。
joint自体は木構造になっており、<b>library_visual_scenes</b>のnodeの要素で構造が記述されている。

### 行列のデータ形式
colladaファイルの行列は<b><font color = "RED">row major</font></b>形式で記述されている。
例えばglmを利用する場合など、プログラム内ではcolumn major形式で扱うことの方が多いと思うので、transposeで転置しておく。
コンパイルも通り、正確にデータは読み込めているはずなのに何故かモデルが意味不明の挙動をする場合、行列への格納方法が間違っていたりするので、意外に重要なポイントだったりする。

### 座標変換
独学で学ぶにはあまりにハードルが高く、Khronos Forumに書かれてある記事を参考にした。
先人達に感謝！！
https://community.khronos.org/t/need-help-with-skeletal-animation/68668

個人的な理解をまとめると、用いる座標系と座標変換は2種類ある。

+ <b>OBJECT SPACE</b> : 最終的な描画に用いられる座標系。初期のgeometryを読み取っただけの状態もここにあたる。
+ <b>BONE SPACE</b> : 各jointのオリジナルの位置。
+ <b>JOINT MATRIX</b> : 各jointごとに定義されている、BONE SPACE -> OBJECT SPACEに変換する行列。colladaファイルにおいては<b>library_visual_scene > node > matrix</b>に記述されている。
+ <b>ANIMATION MATRIX</b> : 各jointごとに定義されている、BONE SPACE -> OBJECT SPACEに変換する行列。colladaファイルにおいては<b>library_animations > animation</b>の内、matrix-outputのような書かれ方で記述されている。

geometryはOBJECT SPACEにあるので、
|OBJECT SPACE|->|BONE SPACE|->|OBJECT SPACE|
|:--:|:--:|:--:|:--:|:--:|
|geometry|$J^{-1}$|b|$A$|p|

の変換を行う。($J^{-1}$ : JOINT MATRIXの逆行列, $A$ : ANIMATION MATRIX)
column majorを想定しているので、行列は左から掛ける。つまり
```math
b = J^{-1} * geometry
```
ここでさらに注意が必要なのは、jointは木構造であり、<b>各変換は親nodeから継承される。</b>
つまり、以下のような構造であった場合に
![test.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1446998/06482d07-ab23-9eb0-89a2-1631ac21c0a0.png)
C1のjointでの座標変換は
|OBJECT SPACE|->|BONE SPACE|->|OBJECT SPACE|
|:--:|:--:|:--:|:--:|:--:|
|geometry|$(J_{A} * J_{B1} * J_{C1})^{-1}$|b|$A_{A} * A_{B1} * A_{C1}$|p|

のようにして行わなければならない。
各nodeごとに情報を覚えておかなければならないので、プログラムの実装の観点から言えば再帰関数で実装するのが簡単そう。

以上は各jointについての変換についてだが、ここで頂点に目を向けると、各頂点は複数のjointに属するので、それぞれのjointに対して重みが定義されている。
最終的な頂点の位置としては、各jointごとに重みつきで位置を計算した後に、最後にそれらを足し合わせる必要がある。

# 感想
めちゃくちゃ大変だった。
colladaファイルを読み込んでからanimationの変換を作成する流れをまとめてしまうと、以上のようなものになりかなりシンプルなのだが、ただアニメーションを動かすだけでも（それもきれいなものではなく、カクついた状態で）かなり大変だった。
Googleで調べてみてもまとまっている情報はなく、Git Hubで関係がありそうなソースコードを調べるという方法でなんとかTheThinMatrix様のリポジトリに辿り着き、ソースを解析することでようやく実装を行うことができた。
ray tracingのレンダリングを学んだときよりも大変で、時間もかかってしまった。
自作のプログラムでanimationの実装を行おうと思っている方の参考になれれば幸いです。

# GitHub
私のリポジトリです。
何かの参考になれば。
https://github.com/kodai731/Aqoole-Engine-Android-Vulkan-Rendering-Engine-