VRM用のアニメーションファイル(.vrma)の簡単な作り方

# はじめに

みなさん こんにちは またはこんばんは、ギャルプロです💪

今回はブラウザ(threejs)で利用できるVRMにモーションをさせるためのアニメーションファイルを作成してみたいと思います！
なお、VRMにアニメーションをさせるコンポーネントはpixivさんの[VRMAnimation](https://github.com/pixiv/ChatVRM/tree/main/src/lib/VRMAnimation)を利用させていただきます✨

# アニメーションファイルを準備する
今回はギャルプロちゃんに考える :thinking: ポーズをさせてみたいと思います。
アニメーションを作成するのはとても大変なので、ゲーム開発者にはお馴染み(?)のMixamoというサービスを利用します😳

まずは[Mixamo](https://www.mixamo.com/#/)にアクセスし、好きなアニメーションを探します。(Adobeのアカウントが必要ですが無料で利用できます！神！)

好みのアニメーションが見つかったら、ダウンロードボタンを押し、画像のような設定でfbxファイルをダウンロードしましょう💪
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468343/f83b5d1f-4aea-c1f2-1f4a-5c4e6e73458a.png)


# FBXをglTFに変換する
さて、ダウンロードしたfbxファイルですが、このままではVRMにロードさせることができないので、`glTF`という形式に変換します。
変換にはMeta Incubator(旧Facebook Incubator)が提供している、FBX2glTFを使用します。
https://github.com/facebookincubator/FBX2glTF

インストールが完了したら、以下のようにfbxをglTFに変換してください。
```bash
path/to/FBX2glTF -i Thinking.fbx -o Thinking.glTF
```
buffer.binとThinking.gltfの2つのファイルが出力されれば完璧です！

# glTFを編集する
VRMAnimationクラスが使用する.vrmaというフォーマットに変換するために、以下の2つのデータを付与します。

1. glTF拡張の宣言
1. ヒューマノイドのボーン情報

VRMコンソーシアム[^1]によるVRMAの仕様については以下のページを参照してみてください！
※現時点ではドラフトなので変更される可能性があります
https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm_animation-1.0/README.ja.md

## 1.glTF拡張の宣言
glTFの拡張機能を利用していることを宣言するため、.glTFファイルを編集していきます :pencil: 
`glTF` はただのjsonなのでVSCodeなどのエディタで開きます。

開いたら次のようなフィールドを追加してください！
```json
"extensionsUsed": ["VRMC_vrm_animation"]
```

簡単ですね :sparkles: 

## 2.ヒューマノイドのボーン情報
こちらが本作業のキモです。
Mixamoが吐き出したモーションのノードをVRMのヒューマノイドボーンにリターゲティングしていきます :muscle: 

具体的にはアニメーションのglTFに含まれるnodesから対応するボーンのインデックスを確認する作業です。

たとえば `glTF`の中身が以下のようになっていたときに
```json
"nodes": [
        {
            "name": "RootNode",
            "children": [1, 66, 67]
        },
        {
            "name": "mixamorig:Hips",
            "translation": [
                -7.72729080722456e-8, 1.04274868965149, 0.0155431581661105
            ],
            "rotation": [0.00645854603499174, 0, 0, 0.999979138374329],
            "children": [2, 56, 61]
        },
        ...
]
```
`Hips`は1番目のノードなので
```json
"extensions": {
    "VRMC_vrm_animation": {
        "specVersion": "1.0",
        "humanoid": {
            "humanBones": {
                "hips": {
                    "node": 1
                },
                ...
            }
        }
    }
}
```
というデータを作成することになります。

 
vrmaで利用するヒューマイノイドのボーン一覧は[こちら](https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0/humanoid.ja.md#%E3%83%92%E3%83%A5%E3%83%BC%E3%83%9E%E3%83%8E%E3%82%A4%E3%83%89%E3%83%9C%E3%83%BC%E3%83%B3%E3%81%AE%E4%B8%80%E8%A6%A7)から参照できました。


これは地道な作業となるのでチートシートを記載しておきます :joy:
以下のデータをglFTの末尾にコピペすればMixamoからダウンロードしてきたアニメーションは動くと思います！
<details><summary>MixamoからダウンロードしてきたglTFに対して追加するノード情報 :point_right: </summary><div>

```json
"extensions": {
    "VRMC_vrm_animation": {
        "specVersion": "1.0",
        "humanoid": {
            "humanBones": {
                "hips": {
                    "node": 1
                },
                "spine": {
                    "node": 2
                },
                "chest": {
                    "node": 3
                },
                "upperChest": {
                    "node": 4
                },
                "neck": {
                    "node": 5
                },
                "head": {
                    "node": 6
                },
                "rightShoulder": {
                  "node": 8
                },
                "rightUpperArm": {
                    "node": 9
                },
                "rightLowerArm": {
                    "node": 10
                },
                "rightHand": {
                    "node": 11
                },
                "rightThumbMetacarpal": {
                    "node": 12
                },
                "rightThumbProximal": {
                    "node": 13
                },
                "rightThumbDistal": {
                    "node": 14
                },
                "rightIndexProximal": {
                    "node": 16
                },
                "rightIndexIntermediate": {
                    "node": 17
                },
                "rightIndexDistal": {
                    "node": 18
                },
                "rightMiddleProximal": {
                    "node": 20
                },
                "rightMiddleIntermediate": {
                    "node": 21
                },
                "rightMiddleDistal": {
                    "node": 22
                },
                "rightRingProximal": {
                    "node": 24
                },
                "rightRingIntermediate": {
                    "node": 25
                },
                "rightRingDistal": {
                    "node": 26
                },
                "rightLittleProximal": {
                    "node": 28
                },
                "rightLittleIntermediate": {
                    "node": 29
                },
                "rightLittleDistal": {
                    "node": 30
                },
                "leftShoulder": {
                    "node": 32
                },
                "leftUpperArm": {
                    "node": 33
                },
                "leftLowerArm": {
                    "node": 34
                },
                "leftHand": {
                    "node": 35
                },
                "leftThumbMetacarpal": {
                    "node": 36
                },
                "leftThumbProximal": {
                    "node": 37
                },
                "leftThumbDistal": {
                    "node": 38
                },
                "leftIndexProximal": {
                    "node": 40
                },
                "leftIndexIntermediate": {
                    "node": 41
                },
                "leftIndexDistal": {
                    "node": 42
                },
                "leftMiddleProximal": {
                    "node": 44
                },
                "leftMiddleIntermediate": {
                    "node": 45
                },
                "leftMiddleDistal": {
                    "node": 46
                },
                "leftRingProximal": {
                    "node": 48
                },
                "leftRingIntermediate": {
                    "node": 49
                },
                "leftRingDistal": {
                    "node": 50
                },
                "leftLittleProximal": {
                    "node": 52
                },
                "leftLittleIntermediate": {
                    "node": 53
                },
                "leftLittleDistal": {
                    "node": 54
                },
                "rightUpperLeg": {
                    "node": 56
                },
                "rightLowerLeg": {
                    "node": 57
                },
                "rightFoot": {
                    "node": 58
                },
                "rightToes": {
                    "node": 59
                },
                "leftUpperLeg": {
                    "node": 61
                },
                "leftLowerLeg": {
                    "node": 62
                },
                "leftFoot": {
                    "node": 63
                },
                "leftToes": {
                    "node": 64
                }
            }
        }
    }
}
```
</div></details>

# .glTFを.vrmaに変換する
色々と方法があると思いますが、一番簡単なのは以下の方法でした。
VSCode拡張の「glTF Tools」をインストールします。
https://github.com/AnalyticalGraphicsInc/gltf-vscode

.gltfをエディターで開き、右クリック→glTF: Export To GLBを選択し、保存先のパスを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468343/9f3024ed-eb6f-4607-1079-03503528d127.png)


glbファイルが出来上がったら拡張子を.vrmaに変更します。

以上でVRMで利用できるアニメーションファイルができました :tada: 

# vrmaをロードして再生してみる
前のステップで作成したvrmaファイルをロードして再生してみましょう！
うまくいっていれば正しくモーション再生されるはずです🎉


今回は以上となります。
みなさんも良いVRMライフを！

バイビー


# おまけ
DCCツール(Blenderとか)を使用せずに、glTF ⇄ glbの相互変換を行いたかったんだけど、なかなか良いイテレーション方法がなかったので、私のケースを記しておくね。

## glb → glTF
glTF-Transformというプロジェクトを使ったよ！
https://gltf-transform.donmccurdy.com
Nodejsでも動くし、cliもあるからとっても便利💓

```bash
gltf-transform copy hoge.glb hoge.gltf
```
これで.binと.gltfが吐き出されます！


## glTF → glb
これは上で紹介したようにVSCode拡張でやるのが便利だった！
gltf-transformにもpackの機能があるかなーって思ったんだけどチョットミツカラナカッタ🥺


# おまけ2
Mixamoからダウンロードしてきたモーションだとチラつきが発生しちゃった:frowning2:
クォータニオンがなんか飛んでる気がしたからVRMAnimationのコードを直してIssue[^2]を立てておいたよ！
もし同じ症状が発生している人がいたら見てみてね:v:

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Mixamoから持ってきたモーションがVRMで動かせたよ🙌<br>なんか腕の動きにチラつきがあるけど今日は眠いから明日調べよー😪<a href="https://twitter.com/hashtag/VRM?src=hash&amp;ref_src=twsrc%5Etfw">#VRM</a> <a href="https://twitter.com/hashtag/%E3%82%AE%E3%83%A3%E3%83%AB%E3%83%97%E3%83%AD?src=hash&amp;ref_src=twsrc%5Etfw">#ギャルプロ</a> <a href="https://twitter.com/hashtag/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0?src=hash&amp;ref_src=twsrc%5Etfw">#プログラミング</a> <a href="https://t.co/UZrh0r5ysQ">pic.twitter.com/UZrh0r5ysQ</a></p>&mdash; ギャルプロ | AIペアプロパートナー🌟 (@galpro_ai) <a href="https://twitter.com/galpro_ai/status/1664650862311141382?ref_src=twsrc%5Etfw">June 2, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[^1]: https://vrm-consortium.org/
[^2]: https://github.com/pixiv/ChatVRM/issues/9