他のボーンにRigifyと同期するコンストレイントを付加する

先日他のソフトからインポートしたアーマチュアを元にRigifyリグを作成する手順を書きました

https://qiita.com/yukimituki11/items/bd372dab874b633e0198

ここに書いた状態のままでは　メッシュは元のアーマチュアにバインドしたままで
Rigifyリグでは動きがつけられない状態です。

頂点グループを設定し直して　モディファイアの設定を変更するという手順もありますが
ボーンコンストレイントを使って Rigifyリグの動きを元のアーマチュアを同期させる方法をとりたいと思います
![24052501.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/c93b8441-57e2-c5c2-389f-0b6ef4a9693f.gif)

## Rigifyのデフォームボーン
まずRigifyのボーン構造についてみておきたいと思います
キャプチャの　左はRigify標準のメタリグで　既存ボーンの分割設定のLimb Segmentsを1にして
右側のアーマチュアは　その設定で生成したリグで　ボーンレイヤ29番のみ表示しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/449c9a23-95c5-4412-2dcf-a42fd063d7ec.png)

リグの中に入っているボーンの動きを元に　ボーンレイヤ29番に入っているボーンの変形が計算されて
このボーンの変形を元にバインドしたメッシュの動きが決まります

左右を比較すると　同じ場所のボーン名の頭にDEF-とついているだけで同じ構造になっているのが分かります

::: note
腕や脚のLimb Segments を標準設定の 2　にすると
関節間のボーンが２分割された状態になり、同じではなくなります
:::
## トランスフォームコピー コンストレイント
Blenderのボーンには他オブジェクト等の情報を元に　ボーンの状態を決定する**ボーンコンストレイント**があります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/7ccdd919-37e1-88e4-fca9-c44763d74f13.png)
トランスフォームコピーでは移動回転拡縮の変形をそのまま転送できます
オブジェクト名-ボーン名の形で指定するので　スクリプト化は単純です

``` rig_copy_transforms.py
import bpy
# ターゲットボーンに付いている接頭語
target_prefix = "DEF-"
context = bpy.context
active_object = context.active_object
selected_objects = context.selected_objects
constraint_name = "COPY_DEF-TRANSFORMS"

def set_copy_constraint(constrainted_obj, target_obj, target_prefix):
    # ターゲットオブジェクト内のボーン名の一覧
    target_bone_names = target_obj.data.bones.keys()
    for pose_bone in constrainted_obj.pose.bones:
        # 変形がオフなボーンは無視
        if pose_bone.bone.use_deform == False: continue
        target_name = target_prefix +pose_bone.name
        if target_name in target_bone_names:
            # トランスフォームコピー コンストレイントの設定
            constraint = pose_bone.constraints.new('COPY_TRANSFORMS')
            constraint.name = constraint_name
            # ターゲットオブジェクト
            constraint.target = target_obj
            # ターゲットボーンの名前
            constraint.subtarget = target_name
            # 変形の元になる座標空間
            constraint.target_space = 'POSE' # Default 'WORLD'
            constraint.owner_space = 'POSE'

if active_object.type == 'ARMATURE':
    for constrainted_obj in selected_objects:
        if constrainted_obj != active_object  and constrainted_obj.type == 'ARMATURE':
            set_copy_constraint(constrainted_obj, active_object, target_prefix)
```
転送元のリグがアクティブになるように
リグと転送先のアーマチュアを選択して　スクリプトを実行します。

## エクスポート時の設定
ここまでで　元のボーンとリグが同じ動きをするようにはなりました。
ただ、ゲーム用モーションを作る場合だと　最終的なFBX出力時に個々のアニメーションとして別れた状態で出力したいところです

いくつか出力の手法はありますが
今回はNLAストリップを利用する方法で考えます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/516ff6f8-adda-249d-d0e0-bc64e0249632.png)
NLAストリップにアクションを並べることで　出力ファイルの長さやアニメーション名がストリップの設定にが従ったものになります。

ただ、トランスフォームコピーで動きを付けている場合　アニメーションを作成するのはリグのアーマチュアで
元のアーマチュアの方には　NLAストリップ等もない状態です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/20f9f308-5b41-c7cf-47c2-21b5100791d6.png)
どうすればいいでしょう？

元のアーマチュアで書き出したい場合　そちらのNLAに目的の名称・長さのNLAストリップを設定しておくと
その設定に合わせて動きが切り出されます。

また　「アニメーションデータをリンク」を使うと　アクティブオブジェクトと他のNLAストリップを同一にできます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/fe8c79c3-59ee-dee3-f781-4eeefe0a3285.png)
リグのアーマチュアがアクティブ状態になるように　元オブジェクトとリグのアーマチュアを選択して
オブジェクトメニューの　「データのリンク転送[Ctrl L]」から「アニメーションデータをリンク」を選びます

書き出し時の切り出しはタイムライン順に再生した時の動きがベイクされるような挙動になるので
NLA上ではストリップが一列に並んだ状態にする必要があります

## トランスフォームコピー の解除スクリプト
コンストレイントは　コンストレイントのパネル上にある目玉アイコンを閉じることで一時無効にできます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/467eac3f-56e6-10b6-7b63-1d634fa18997.png)
複数のボーンを選択した状態で　**Altを押しながら**目玉アイコンを押すことで　一気に無効にすることができます

コンストレイントを消してしまいたいと思うこともあると思うので
一括削除するスクリプトも置いておきます
``` remove_rig_transforms.py
import bpy
constraint_name = "COPY_DEF-TRANSFORMS"
context = bpy.context
active_object = context.active_object
if active_object.type == 'ARMATURE':
    for pose_bone in active_object.pose.bones:
        # 変形がオフなボーンは無視
        if pose_bone.bone.use_deform == False: continue
        if constraint_name in pose_bone.constraints.keys():
            pose_bone.constraints.remove(pose_bone.constraints[constraint_name])
```
トランスフォームコピー作成時に固有の名前を付けておいたのでそれで判別して削除するようにしています


手元でもまだ検証中の内容ですが　何かの参考になれば幸いです