TouchDesignerでAzure Kinectを使ってBlenderで作成した自作モデルを動かす

Azure KinectのBody Trackingを使ってアバターを動かす（IKではなくFK）的なことを検索するとUnityの情報ばっかり出てきて、TouchDesignerでどうやんねんって情報が見つからなかったのでやってみました。

今回はこんな感じで、Blenderで作った簡単なモデルにボーンを入れて、ちゃんと動きに追従しますよというところまでです。

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/AzureKinect?src=hash&amp;ref_src=twsrc%5Etfw">#AzureKinect</a> でTrackingしたデータでアバターを動かすテスト <a href="https://t.co/6u7kDOWWYq">pic.twitter.com/6u7kDOWWYq</a></p>&mdash; 岩谷成晃 (@nariakiiwatani) <a href="https://twitter.com/nariakiiwatani/status/1296450750617395205?ref_src=twsrc%5Etfw">August 20, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



# 前提

[前回までやっていたような](https://qiita.com/nariakiiwatani/items/b3d457b6747ed2659b26)、トラッキングされた各関節の__位置__に何かを表示するという場合は、モデルには親子階層構造を持たせずに、ワールド位置とワールド回転（？）を反映させるのが簡単です。

が、今回やろうとしているような、3Dモデル（アバター）を動かそうという場合はそれではいけません。関節間がモデルの体型を無視して演者のサイズに合わせて伸び縮みしてしまいます。

ですので、関節間の距離は無視して回転だけを適用するという手法がよく使われます。ルートノードから順に計算していく手法であるため、Forward Kinematics(FK)と呼ばれます。

ちなみに、末端の位置または向きを先に決め、そこからルートに向かって角度計算を伝播させていく手法をInverse Kinematics(IK)といいます。

今回はFKをやります。

やることを整理すると、

- Blenderでモデルを作って
- TouchDesignerにインポートして
- Azure KinectのBodyTrackingから取れるデータを使用して
- モデルを動かす

です。

# Blenderでモデル作り

「僕、Blender触ったことないんですよね・・・」
マジです。Blender起動したことくらいはありますが、「え？オブジェクト選択できないんだけど？え？動かせないんだけど？え？え？え？」ってなって何もしてませんでした。

ということで入門してみました。
Blenderのバージョンは2.83.4です。
キーボードでサクサク操作できるのすんごい良いですね。

今回作るものはこういう、円錐にボーンが入ってるやつです。これを腕にします。
![スクリーンショット 2020-08-20 12.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/3cf93000-d563-cf98-b221-928d697c3302.png)

Blenderの話を飛ばしてTDの話を読みたい方は[こちら](#FBXをTouchDesignerで読む)へ

はじめてのモデル作成始めます。

まず最初に配置されてるキューブを消します。
選択してx

## メッシュを作る

後から分かったんですが、はじめに知っておいた方がよかったことを書いておきます。

- Blender座標はZが上
    - 変更は書き出し時に可能
- Blenderはメートル法
    - このあとコーンを長さ4mとかで作ってしまう。50cmとかで作ればよかった
    - 書き出し時にスケーリング可能
- BlenderのボーンはY方向を向いている
    - 変更は書き出し時に可能

Shift+AでMesh(Cone)追加
クリックで選択
rで回転
yで回転軸(y)固定
90と打って角度を指定
sx2と打ってx方向2倍
gx2でx方向移動（コーンの底面が原点になる）
動かした時にどの軸で回転しているのかがわかるようにsy0.5でY方向に潰しておく
Shift+Dで複製
gx4でx方向移動（旧コーンの頂点に新コーンの底面）
Shift+D, gx4でもう一つ複製しておく

いまこんな感じ
![スクリーンショット 2020-08-20 11.49.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/38d692df-366c-072a-dff8-d9212bf715f5.png)


## ボーンを入れる

Shift+Sで3Dカーソルを移動するメニューを開く
Cursor To Originを選んで原点（コーンの底面中心）へ移動させる
Shift+AでArmatuerがカーソル位置に作成される
こんな感じで作られる
![スクリーンショット 2020-08-20 11.59.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/f84ad258-0298-674a-82a1-95a983a3c11b.png)

ry90でコーンと同じ方向を向くように回転させる
TabでEditモードへ
Boneのtip（先っちょ）を選択してgx3でコーン先端へ移動させる
ex4で次のコーン用のボーンを作成
再度ex4
![スクリーンショット 2020-08-20 12.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/cb533117-4e6e-592b-3f9e-c5174c7cd422.png)

## ボーンの向きを設定する
[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/kinect-dk/body-joints)にある各関節の向きに合うようにボーンを回転させる
※軸のXYZは合ってなくていいから、矢印が同じ形になるように編集していく
ちなみにこれは左腕のつもり

![スクリーンショット 2020-08-20 22.34.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/be535e38-913b-7215-a85d-4a657e04367d.png)



## メッシュとボーンの関連付け

aで全選択
Object->Parent->Armature Deform With Automatic Weightsすると、メッシュがArmatureの子になる
（Ctrl+Pでparentメニューへショートカット）
![スクリーンショット 2020-08-20 12.13.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/d8d2a4d6-28b1-1ba9-ae02-2f092be8a8a9.png)
![スクリーンショット 2020-08-20 12.13.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/3abafcf8-f327-1f8c-bd1b-ac2a44cdf23c.png)

（動作確認）
Ctrl+TabでPoseモード
ボーンを選択してrで動かしてみる
![スクリーンショット 2020-08-20 12.19.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/90455bad-6103-a5a2-2b9f-9c058aa3f071.png)

# FBXで書き出し

- Scaleを0.01にする
    - BlenderもTDもメートルスケールのはずだが、こうしないとダメだった。多分どこかに設定がある
- ForwardをZに、UpをYに
    - TD側の座標に合わせるため
- BoneAxisをPrimaryをXに、Secondaryを-Zに
    - ボーンのローカル回転座標を公式ドキュメントに合わせるため

![スクリーンショット 2020-08-20 22.19.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/b82ac1e8-cd0b-13c9-64aa-6ea81d473e3f.png)



## FBXをTouchDesignerで読む

FBXはTDのウィンドウにD&Dすれば読めます。
Blenderで作ったメッシュとボーンの構造がそのまま表れていますね。
![スクリーンショット 2020-08-21 9.10.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/e3067fd7-f527-24e0-927b-39bd8dc5434c.png)

今回気が回らなかったのですが、Blender側で最初のボーンを回転させて倒して作ってしまったために、TD側でも`Bone`に`(180,0,0)`の回転が入ってしまっています。
このボーンはKinectからの値で動かしたいので、急場しのぎですがArmatureにその分の回転を押し付けました。（`(90,0,180)` => `(-90,0,180)`）
本来は、回転を操作しないルートノード的なボーンを一つ置いておけば良いのだと思います。


## Azure Kinectから情報を取得

`Azure Kinect TOP`を置いて、`Azure Kinect CHOP`に食わせればスケルトンの情報が取れます。
今回は[前作ったレコーダー](https://qiita.com/nariakiiwatani/items/b3d457b6747ed2659b26)で書き出したデータをMacで再生しています。

ここでDerivativeに感謝したいんですけど、Azure Kinectのドキュメントによると、Azure KinectのSDK自体は、回転の情報はワールドでのQuaternionしか渡してくれません。しかもその基準になる各関節の座標軸はドキュメントにある「独自の関節座標系」とのこと。

しかしThanks to Derivative, `Azure Kinect CHOP` が面倒な計算をやってくれて、我々ユーザーはTDでそのまま使えるオイラー角での回転として使えます。

さらに、Kinect SDKが返す位置情報はミリメートル単位ですが、TDではメートルであるのもちゃんと考慮してくれているようです。
どこまでが親切でどこからがお節介なのかは決められませんが、分かって使う分にはお手軽で良い仕様だと思います。
今回は手探りだったので、Kinectのドキュメントに書いてあることとTDで実際に取れる値が違って戸惑ったこともありました。。

ともあれ、`Azure Kinect CHOP`からは`[プレイヤーインデックス]/[関節名]:[パラメータ名]`という名前で情報が取得できます。
パラメータ名は以下です。

- 位置: tx,ty,tz
- 回転（親からの相対）: rx,ry,rz
- 回転（カメラ座標での絶対値）: [関節名]_abs:rx,ry,rz


## モデルへ適用

望みの関節の回転の値を対応するボーンに設定してやればOKです。

ボーンの数が多くなってくると手動でスクリプトを書くのが辛くなってくるので、こんな感じのTable DATを用意して、`Export`スイッチ（右下の緑色のスイッチ）をオンにすると、指定したジョイントの姿勢をボーンに反映するのが比較的楽にできます。
![スクリーンショット 2020-08-21 9.25.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/033e42e6-2444-6707-5baa-886b254feebd.png)


```python
path	parameter	value	enable
Armature	tx	op('current_frame')['shoulder_l:tx']	1
Armature	ty	op('current_frame')['shoulder_l:ty']	1
Armature	tz	op('current_frame')['shoulder_l:tz']	1
Bone	rx	op('current_frame')['shoulder_l:rx']	1
Bone	ry	op('current_frame')['shoulder_l:ry']	1
Bone	rz	op('current_frame')['shoulder_l:rz']	1
Bone_001	rx	op('current_frame')['elbow_l:rx']	1
Bone_001	ry	op('current_frame')['elbow_l:ry']	1
Bone_001	rz	op('current_frame')['elbow_l:rz']	1
Bone_002	rx	op('current_frame')['wrist_l:rx']	1
Bone_002	ry	op('current_frame')['wrist_l:ry']	1
Bone_002	rz	op('current_frame')['wrist_l:rz']	1
```

これで左腕ができました。
あとは同じことを体全体でやればアバターが動かせるはずです。たぶん。