まるコットでメモ的な何か

Auto-Rig Pro + Quick Rigで既存モデルのリグ追加とAnimationリターゲット

Blender 4.4.3
Auto-Rig Pro 4.74.63
Quick Rig 1.26.49

 

概要

Auto-Rig Proを学ぶ環境があったので既存モデルにリグを追加したり、既存Action(Animation)のリターゲットをして追加リグ上でも動かせるようにしたときの対応メモです。

ところどころAI生成したスクリプトが出てきます。軽く動作テストはしていますが利用は自己責任で。
(バックアップをお忘れなく)

 

使用アドオン

Auto-Rig Pro

キャラクターのリギングとか、FBXのエクスポートとかを見てくれるやつ。

Auto-Rig Pro: Quick Rig

既存モデルにリギングする Auto-Rig Pro の拡張機能

 

既存モデルのボーン構造

一頭身用に魔改造していますが、ボーン構造は概ね以下のアセットに倣っています。

 

既存モデルにリグを追加

Quick Rigに肢体を追加→リグ生成という流れ。

まずはPoseModeで以下の6つをボーンを1つずつ選択して、Quick RigメニューからAdd Limbします。

ボーン名:Quickリグの選択項目

  • Shoulder.l:Arm
  • Shoulder.r:Arm
  • UpperLeg.l:Leg
  • UpperLeg.r:Leg
  • Hips:Spine
  • Neck:Head

追加が完了したら「Quick Rig!」を選択してデフォルトの設定のままQuick Rigを実行する。

エラーその1:Armを追加する際に起きるエラー

Python: Traceback (most recent call last):
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro_quick_rig\auto_rig_quick.py", line 1390, in execute
    _add_limb(self)
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro_quick_rig\auto_rig_quick.py", line 5698, in _add_limb
    evaluate_hand_up_axis(new_item, hand_bone, arm_bone)
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro_quick_rig\auto_rig_quick.py", line 5348, in evaluate_hand_up_axis
    angle = signed_angle(arm_dir_proj_y, world_x, world_y)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro_quick_rig\lib\maths_geo.py", line 8, in signed_angle
    if vector_u.cross(vector_v).angle(normal) < 1:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Vector.angle(other): zero length vectors have no valid angle

解決策:肩~手にかけてボーンが一直線だとうまく計算できないようなので以下のようにEditModeでボーンの位置をほんのちょっとズラすと解決する。

上に若干ずらす
更に、IK Poleの向きが認識されるように後ろ(肘が曲がる方向)にもずらす

エラーその2:「Quick Rig!」を実行した際に発生するエラー

Error in (C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro_quick_rig\auto_rig_quick.py
Line 1273 "execute_make_rig()"): Error: Python: Traceback (most recent call last):
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro\src\auto_rig.py", line 6379, in execute
    _align_leg_limbs()
  File "C:\Users\poi\AppData\Roaming\Blender Foundation\Blender\4.4\extensions\user_default\auto_rig_pro\src\auto_rig.py", line 23605, in _align_leg_limbs
    align_bone_x_axis(ik_pole, -heel_ref.y_axis)
                                ^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'y_axis'
Location: C:\Tools\Blender Launcher\stable\blender-4.4.3-stable.802179c51ccc\4.4\scripts\modules\bpy\ops.py:109

解決策:エラーその1と似た感じでうまく計算が出来ていないので、EditModeで膝ボーンをほんのちょっと手前にズラすと解決する。

無事エラーが解消されて「Quick Rig!」が正常に実行されるとリグが追加される。

 

IKの動作確認と修正

PoseModeで腕のボーンを選択後、Toolタブの「Snap IK-FK」を押すとFK↔IKの切り替えができる。

IKに切り替えると腕のPoleがおかしなところに出現して腕がねじれるので修正する

EditModeにすると腕のPoleがなぜか腕の手前にあるので

腕の後ろに持っていく

PoseModeに戻って腕のPoleを選択→Alt+Gで元の位置に戻すと腕の後ろ側にPoleが来るようになってねじれも解消しているはず。

次に、c_root_masterというIKの位置はそのままに腰を動かせるリグの挙動を修正する。

腰の動きに対して腕は追従したほうが使い勝手がいいのでc_hand_ik.X (l, r)を選択して対象のBoneをc_trajからc_spine_03.xに変更後、Set Inverseを選択する。

するとc_root_masterのリグに手のIKが追従するようになる。

 

既存Animationのリターゲット

元のArmatureで作ったアニメーションが大量にあるのでリターゲットを行い、今回追加したリグにアニメーションをインポートする。

まず、Auto-Rig Pro: RemapのSource Armatureに元のアーマチュア、Target ArmatureにQuick Rigで生成したアーマチュアを設定後、「Build Bones List」を選択する。

Bones Listに大量のボーンが表示されるが、基本的にはほとんどの設定はそのままでいいはず。

最低限Hipsの項目にSet as Rootにチェックを付ける。

「Multiple Source Anim…」ボタンから、複数のアニメーションをリターゲットする設定をする

  • Enable Multiple Animations Retargettingにチェックを付ける
  • リターゲット対象のアニメーションにすべてチェック付ける

その他、うまくアニメーションがリターゲットされなかったのでいくつか修正した点

  • UpperLeg.Xとかは命名が変わって未選択になっているので対応するボーン (c_thigh_b.X) を選択する
  • シェイプキードライバーとか、ローカル座標で色々アニメーションさせてるボーンはLocation(Local)にチェックをつける

最後に「Re-Target」を選択してリターゲットを実行。

  • Keyframe InterpolationをBezier
  • その他はデフォルト

平均100フレームのアニメーション64個をリターゲット対象に選択した状態で、処理完了までに5分ほど掛かった。

リターゲットが完了すると、「{元のアニメーション名}_remap」というアニメーションが作られているので再生すると動作確認できる。

 

FBXエクスポート

Auto-Rig Pro: ExportのExport FBXから諸々設定してFBX出力する。

リグの出力設定

リグに紐づかないオブジェクトを同時に出力しようとSelected Objects Onlyを試したけど、色々やってもうまくいかなかったので個別出力するように。

複数アニメーションの出力設定

他キャラのアニメーション選択し直すのダルそうなので出力設定プリセット登録すべき?

その他の設定

Modifier適用とか

FBX出力を実行。

64個のアニメーション付きFBXの出力に約10分…個別出力する方法じゃないとちょっと辛い時間かも。

ファイルサイズの差異は以下の通り

  • 今まで(b2.83のリグなしモデル出力) のFBXが約30MB
  • Auto-Rig Pro(b4.4.3)で出力したFBXが約60MB

ファイルサイズが2倍になっており、軽く見た感じ出力されるアニメーションのキーフレーム数が今までより多くなってる気がする。

Unity上で動いているのでとりあえずおk

 

その他メモ

 

Quick Rigで生成したリグの未解決問題

FK→IKに切り替えるたびにPoleの位置がランダムにズレるので毎回Alt+Gで元の位置に戻している。

他に設定が必要だったかも…

 

リターゲットでボーンのScale情報が転送されない

既存モデルのアニメーションでHipsボーンに拡縮を多用しているがリターゲットでHips等の拡縮情報が引き継がれない。

Hipsに c_root_master.x ではなく c_traj を選択しても特に直らず。

そもそもスケール情報はリターゲットで転送出来ないっぽい。(キー情報から確認)
元のアニメーションのキーをリターゲット後のアクションに独自で転送する必要がある。

また、Hipsの位置にc_root_master.xが生成されるはずなのでターゲットはc_root_master.xのままでおk。
c_root_master.xを選択してItemタブを見るとScaleにロックが掛かっているので外してあげれば拡縮できるようになる。(ただし、手足の挙動が怪しくなる問題が出る)

逆にHips→c_trajにしてしまうと足元の位置から拡縮が行われてしまい、うまく拡縮アニメーションが復元できないためc_trajには設定しないほうが良さそう。

以下は自動でScaleの一括転送をしてくれるスクリプト。
BlenderのText EditorにコピペしてRun Scriptすると動作するはず。

import bpy

# ■■ ここに、Auto-Rig Pro: Remapに設定したSourceBone → TargetBone の対応辞書を貼り付け ■■
# 入力が面倒なのでAIとかにOCRで生成してもらうのが良いかも。TargetBoneが空の場合は""にする。
mapping = {
    "A":                        "A",
    "Chest":                    "c_spine_02.x",
    "Hips":                     "c_root_master.x",
    "LittleDistal.L":           "",
...
}

SUFFIX = "_remap"

# 軸のマッピング 元アクションのAxis: リターゲットアクションのAxis 
# 0=X, 1=Y, 2=Z
# 後述する回転軸が正常なリグであれば {0: 0, 1: 1, 2: 2} でOK
axis_map = {0: 0, 1: 2, 2: 1}

# ■■ 実行部 ■■
for remap_act in [a for a in bpy.data.actions if a.name.endswith(SUFFIX)]:
    orig_name = remap_act.name[:-len(SUFFIX)]
    orig_act  = bpy.data.actions.get(orig_name)
    if not orig_act:
        continue

    # 1) 先ボーンの scale f-curve を一度すべてクリア(空文字はスキップ)
    for dst_bone in mapping.values():
        if not dst_bone:
            continue  # ← Value が空なら処理しない
        dst_path = f'pose.bones["{dst_bone}"].scale'
        for fcu in [f for f in remap_act.fcurves if f.data_path == dst_path]:
            remap_act.fcurves.remove(fcu)

    # 2) mapping の Key→Value に対応する scale をコピー(空文字はスキップ)
    for src_bone, dst_bone in mapping.items():
        if not dst_bone:
            continue  # ← Value が空ならコピーせずスキップ
        src_path = f'pose.bones["{src_bone}"].scale'
        dst_path = f'pose.bones["{dst_bone}"].scale'

        # 軸ごとに転送
        for src_axis, dst_axis in axis_map.items():
            src_fcu = orig_act.fcurves.find(src_path, index=src_axis)
            if not src_fcu:
                continue

            # 新規に fcurve を作成
            dst_fcu = remap_act.fcurves.new(data_path=dst_path, index=dst_axis)

            # ← ここでグループを取得/作成して fcu.group にセット
            grp = remap_act.groups.get(dst_bone)
            if not grp:
                grp = remap_act.groups.new(name=dst_bone)
            dst_fcu.group = grp

            # キーを転送
            for kp in src_fcu.keyframe_points:
                frame = kp.co.x
                value = kp.co.y
                new_kp = dst_fcu.keyframe_points.insert(frame, value, options={'FAST'})
                new_kp.interpolation    = 'BEZIER'
                new_kp.handle_left_type  = 'AUTO_CLAMPED'
                new_kp.handle_right_type = 'AUTO_CLAMPED'

            dst_fcu.update()

print("完了:Scale情報を転送しました。")

転送後のアニメーションで拡縮する軸の向きがYとZで逆転していたので、暫定対処として転送時にYとZのキーを入れ替えて登録することで対処したが、抜本的にはQuick Rig作成前にSpine.xの軸をZ(Tポーズした時に腕を伸ばしてる方向?)に設定してからリグを作るべき。(リグを作り直すのが面倒なので暫定策を実施しただけ)

問題点が一つあって、c_root_master.xを拡縮する都合か手足のIKが変な動きをしてしまうので…これはもうしょうがない?(?)

c_trajでの拡縮ではIKの挙動は安定してそうなので、c_trajの位置をc_root_master.xと同じ位置にしてc_trajでリターゲットすれば一応解決しそうだけど、うーん…面倒!(笑)

 

既存モデルのシェイプキードライバーについて

Auto-Rig Proでうまく設定引き継ぐ方法が分からなかったので手動でドライバーを設定し直して対応。

ドライバーで使うボーンのアニメーション情報はリターゲットで持っているので問題なく動く。

 

リターゲットしたアニメーション…なんかキーフレーム数多くね?

1フレーム毎にすべてのキーフレームが登録されていてすごい事になっている。

元アクションのキーの位置を見て不要なキーを全部一括で削減するスクリプトをChatGPTと作った。

使い方はScale転送スクリプトとだいたい同じ。

実行順としては、Remap→Scale転送スクリプト→キー削減スクリプトの順。

import bpy, re

# ─── 設定 ────────────────────────────────
mapping = {
    "A":                        "A",
    "Chest":                    "c_spine_02.x",
    "Hips":                     "c_root_master.x",
    "LittleDistal.L":           "",
...
}

# ─── reduceしたいアクション名のサフィックス ────────────────
SUFFIX = "_remap"

# ─── mapping から空文字を除いた逆引き辞書を作成 ────────────
#     remap先ボーン名 → 元ボーン名
revmap = {dst: src for src, dst in mapping.items() if dst}

# ─── リマップ後アクションを列挙 ────────────────────────────
to_reduce = [act for act in bpy.data.actions if act.name.endswith(SUFFIX)]

# ─── 実行部 ────────────────────────────────────────────────
for remap in to_reduce:
    orig_name = remap.name[:-len(SUFFIX)]
    orig = bpy.data.actions.get(orig_name)
    if not orig:
        print(f"[SKIP] 元アクション未検出: {remap.name}")
        continue

    # ─── ① 元アクションの「ボーンごとの有効フレーム」を収集 ────────
    orig_frames_by_bone = {}
    for fcu in orig.fcurves:
        m = re.match(r'pose\.bones\["([^"]+)"\]', fcu.data_path)
        if not m:
            continue
        bone = m.group(1)
        frames = orig_frames_by_bone.setdefault(bone, set())
        for kp in fcu.keyframe_points:
            frames.add(kp.co.x)

    # ─── ② リマップ後アクションの「ボーン F‐Curve」だけを処理 ───
    for fcu in list(remap.fcurves):
        m = re.match(r'pose\.bones\["([^"]+)"\]', fcu.data_path)
        if not m:
            # ボーン以外の F-Curve はそのまま残す
            continue

        tgt = m.group(1)
        # 空文字マッピングは reverse_map に入っていないのでスキップ
        if tgt not in revmap:
            continue

        src = revmap[tgt]
        valid = orig_frames_by_bone.get(src, set())
        # 対応元にフレームがなければこの F-Curve 丸ごと削除
        if not valid:
            remap.fcurves.remove(fcu)
            continue

        # ─── ③ 不要なキーフレームを削除 ──────────────────────
        to_remove = [i for i, kp in enumerate(fcu.keyframe_points) if kp.co.x not in valid]
        for idx in reversed(to_remove):
            fcu.keyframe_points.remove(fcu.keyframe_points[idx])
        fcu.update()

print("完了:不要なキーを削除しました。")    
元のAction
リターゲット+Scale転送+キー削減したアクション

 

間違って作った複数のリターゲットアニメーションを一括削除したい

名前の末尾が指定した文字列のアクションを一括削除するスクリプト。

import bpy

# 削除したいリターゲットアクションのサフィックス
SUFFIX = "_remap"

# ① まず削除対象のアクション名だけをリスト化
names_to_remove = [a.name for a in bpy.data.actions if a.name.endswith(SUFFIX)]

# ② 名前リストを走査して、存在するものを削除
for name in names_to_remove:
    action = bpy.data.actions.get(name)
    if action:
        print(f"Removing action: {name}")
        bpy.data.actions.remove(action, do_unlink=True)

print(f"Done. Removed {len(names_to_remove)} actions.")

 

あとで試すこと

  • リターゲットで持ってきたアニメーションは基本的にFKで動いているので、ここから新しくIKでアニメーション作る場合に既存アニメーションに影響は出ないか検証
  • 別オブジェクトとして作ってる初心者マークやステッキも含めたFBX出力方法の検証

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です