はじめに
ありがたいことにRigifyの記事が増えており、Rigifyでもっと遊んでみたい。
MMD向けのモデルが大好きなので、Rigifyを適用してBlenderで使いやすくしてみる。
注意事項
レミリアモデルでしかテストしていないので、
他のモデルに使用するにはコード修正が必要と思われます。
MMD以外での使用を禁止しているモデルが多いので、
ご利用の際はモデルのREADMEをよくご確認ください。
MMDモデルのインポート
ここではフリック氏配布のレミリアモデルを使用する。
URL : http://seiga.nicovideo.jp/seiga/im2651527
MMDモデルのインポーターは、sugiany氏作成のものを使用する。
URL : https://github.com/sugiany/blender_mmd_tools
※2015/12/20時点での最新のものを使用。
インポート直後の画像はこちら。
インポート後の設定は好みだが、ここでは以下の5つの設定を行う。
マテリアル設定
ツールシェルフ > mmd_toolsタブ > 「Convert Materials For Cycles」ボタンをクリックし、Cycles用マテリアルの設定をする。剛体設定
ツールシェルフ > mmd_toolsタブ > Rigidbody「Build」ボタンをクリックし、剛体設定を完了させる。白目修正
目に影が出来てしまうので、”目玉”のマテリアルを変更する。
MMDBasicShaderを切断し、放射シェーダーを接続する。オブジェクト分離
以下のマテリアルを選択し、オブジェクトを分離する。
(ショートカット : P)- 青ざめ
- 魅了の眼
- 口2
- 頬紅
- 照れ
- 瞳2
オブジェクト配置
まずはAlt+Hですべてのオブジェクトを表示させ、
各オブジェクトのレイヤーを変更する。
必須の設定ではないが、ボーン、メッシュ、それ以外でレイヤーを分けると楽。
ここまではインポーターの機能やモデル特有の設定なので特に自動化はしない。
Rigify適用
ここからはスクリプトを使用してRigifyを適用していく。
エラーに備え、Blenderのシステムコンソールを表示させておく。
(Window > Toggle System Console)
基本データ
まずはMMDとRigifyのボーン名の対応情報を持つスクリプトファイルをBlenderに作成する。
スクリーンレイアウトをScriptingに変更し、テキストを新規作成する。
ファイル名は”commons.py”とする。
※この名前を使用してモジュールインポートしているので、ファイル名は固定で。
以下のコードをテキストエディタに貼り付ける。
class BoneNameMap(dict): | |
pitchipoy = True | |
def __init__(self, pitchipoy=True): | |
dict.__init__(self) | |
self.pitchipoy = pitchipoy | |
def set_LR_name(self, key, value, value_suffix=""): | |
self[key + ".L"] = value + ".L" + value_suffix | |
self[key + ".R"] = value + ".R" + value_suffix | |
class MetarigMmdTailBoneNameMap(BoneNameMap): | |
def __init__(self, pitchipoy=True): | |
BoneNameMap.__init__(self, pitchipoy) | |
self.set_LR_name("shoulder", "肩") | |
self.set_LR_name("upper_arm", "腕捩") | |
self.set_LR_name("forearm", "手捩") | |
self.set_LR_name("hand", "手首") | |
self.set_LR_name("thumb.01", "親指0") | |
self.set_LR_name("thumb.02", "親指1") | |
self.set_LR_name("thumb.03", "親指2") | |
self.set_LR_name("f_index.01", "人指1") | |
self.set_LR_name("f_index.02", "人指2") | |
self.set_LR_name("f_index.03", "人指3") | |
self.set_LR_name("f_middle.01", "中指1") | |
self.set_LR_name("f_middle.02", "中指2") | |
self.set_LR_name("f_middle.03", "中指3") | |
self.set_LR_name("f_ring.01", "薬指1") | |
self.set_LR_name("f_ring.02", "薬指2") | |
self.set_LR_name("f_ring.03", "薬指3") | |
self.set_LR_name("f_pinky.01", "小指1") | |
self.set_LR_name("f_pinky.02", "小指2") | |
self.set_LR_name("f_pinky.03", "小指3") | |
self.set_LR_name("thigh", "足") | |
self.set_LR_name("shin", "ひざ") | |
self.set_LR_name("foot", "足首") | |
if self.pitchipoy: | |
self["spine.001"] = "上半身" | |
self["spine.003"] = "上半身2" | |
self["spine.005"] = "首" | |
self["spine.006"] = "頭" | |
else: | |
self["spine"] = "上半身" | |
self["chest"] = "上半身2" | |
self["neck"] = "首" | |
self["head"] = "頭" | |
class MetarigMmdHeadBoneNameMap(BoneNameMap): | |
def __init__(self, pitchipoy=True): | |
BoneNameMap.__init__(self, pitchipoy) | |
self.set_LR_name("shoulder", "肩") | |
self.set_LR_name("upper_arm", "腕") | |
self.set_LR_name("thumb.01", "親指0") | |
self.set_LR_name("f_index.01", "人指1") | |
self.set_LR_name("f_middle.01", "中指1") | |
self.set_LR_name("f_ring.01", "薬指1") | |
self.set_LR_name("f_pinky.01", "小指1") | |
self.set_LR_name("thigh", "足") | |
if self.pitchipoy: | |
self["face"] = "頭" | |
else: | |
self["neck"] = "首" | |
class MmdRigifyBoneNameMap(BoneNameMap): | |
def __init__(self, pitchipoy=True): | |
BoneNameMap.__init__(self, pitchipoy) | |
self.set_LR_name("足首D", "DEF-foot") | |
self.set_LR_name("足先EX", "DEF-toe") | |
self.set_LR_name("肩", "DEF-shoulder") | |
self.set_LR_name("手首", "DEF-hand") | |
self.set_LR_name("親指1", "DEF-thumb.02") | |
self.set_LR_name("親指2", "DEF-thumb.03") | |
self.set_LR_name("人指2", "DEF-f_index.02") | |
self.set_LR_name("人指3", "DEF-f_index.03") | |
self.set_LR_name("中指2", "DEF-f_middle.02") | |
self.set_LR_name("中指3", "DEF-f_middle.03") | |
self.set_LR_name("薬指2", "DEF-f_ring.02") | |
self.set_LR_name("薬指3", "DEF-f_ring.03") | |
self.set_LR_name("小指2", "DEF-f_pinky.02") | |
self.set_LR_name("小指3", "DEF-f_pinky.03") | |
if self.pitchipoy: | |
self["下半身"] = "DEF-spine" | |
self["上半身"] = "DEF-spine.001" | |
self["上半身2"] = "DEF-spine.003" | |
self["首"] = "DEF-spine.005" | |
self["頭"] = "DEF-spine.006" | |
self.set_LR_name("足D", "DEF-thigh", ".001") | |
self.set_LR_name("ひざD", "DEF-shin", ".001") | |
self.set_LR_name("腕", "DEF-upper_arm") | |
self.set_LR_name("腕捩", "DEF-upper_arm", ".001") | |
self.set_LR_name("ひじ", "DEF-forearm") | |
self.set_LR_name("手捩", "DEF-forearm", ".001") | |
self.set_LR_name("親指0", "DEF-thumb.01") | |
self.set_LR_name("人指1", "DEF-f_index.01") | |
self.set_LR_name("中指1", "DEF-f_middle.01") | |
self.set_LR_name("薬指1", "DEF-f_ring.01") | |
self.set_LR_name("小指1", "DEF-f_pinky.01") | |
else: | |
self["下半身"] = "DEF-hips" | |
self["上半身"] = "DEF-spine" | |
self["上半身2"] = "DEF-chest" | |
self["首"] = "DEF-neck" | |
self["頭"] = "DEF-head" | |
self.set_LR_name("足D", "DEF-thigh.02") | |
self.set_LR_name("ひざD", "DEF-shin.02") | |
self.set_LR_name("腕", "DEF-upper_arm.01") | |
self.set_LR_name("腕捩", "DEF-upper_arm.02") | |
self.set_LR_name("ひじ", "DEF-forearm.01") | |
self.set_LR_name("手捩", "DEF-forearm.02") | |
self.set_LR_name("親指0", "DEF-thumb.01", ".01") | |
self.set_LR_name("人指1", "DEF-f_index.01", ".01") | |
self.set_LR_name("中指1", "DEF-f_middle.01", ".01") | |
self.set_LR_name("薬指1", "DEF-f_ring.01", ".01") | |
self.set_LR_name("小指1", "DEF-f_pinky.01", ".01") | |
if __name__ == "__main__": | |
print("---------- start test ----------") | |
print("* test MetarigMmdTailBoneNameMap") | |
metarig_tail_map = MetarigMmdTailBoneNameMap(True) | |
[print(x) for x in metarig_tail_map.keys()] | |
print() | |
print("* test MetarigMmdHeadBoneNameMap") | |
metarig_head_map = MetarigMmdHeadBoneNameMap(True) | |
[print(x) for x in metarig_head_map.keys()] | |
print() | |
print("* test MmdRigifyBoneNameMap") | |
mmd_rigify_map = MmdRigifyBoneNameMap(True) | |
[print(x) for x in mmd_rigify_map.values()] | |
print("---------- end test ----------") |
このスクリプトは他のスクリプトから呼ばれるものなので、
ここではまだスクリプト実行を行わない。
貼り付け後の画像は以下。
metarig生成
テキストを新規作成し、以下のコードを張り付ける。
※ファイル名は何でもよい。
import bpy, mathutils | |
from mathutils import Vector | |
from commons import MetarigMmdTailBoneNameMap, MetarigMmdHeadBoneNameMap | |
class MetarigController: | |
pitchipoy = True | |
obj_mmd = bpy.context.active_object | |
obj_metarig = None | |
arm_mmd = None | |
arm_metarig = None | |
bone_name_map_tail = MetarigMmdTailBoneNameMap(pitchipoy) | |
bone_name_map_head = MetarigMmdHeadBoneNameMap(pitchipoy) | |
extra_required_bone_names = ["下半身",] | |
mmd_bone_map = {} | |
bone_location_map_tail = {} | |
bone_location_map_head = {} | |
def execute(self): | |
if self.check() == False: return | |
if self.get_bone() == False: return | |
if self.init_bone_location_map() == False: return | |
if self.create_metarig() == False: return | |
if self.edit_bone() == False: return | |
# Rigifyの生成も可能だが、かなり時間が必要なうえ、 | |
# 画面の表示が変化しないのでここでは実行しない。 | |
# 生成したMetarigを微調整する可能性もある。 | |
# if self.createRigify() == False: return | |
def check(self): | |
if self.obj_mmd is None: | |
print('can not find object') | |
return False | |
if self.obj_mmd.type != 'ARMATURE': | |
print('selected object is not armature : ' + self.obj_mmd.type) | |
return False | |
if self.obj_mmd.mode != 'OBJECT': | |
print('selected object is not object mode : ' + self.obj_mmd.mode) | |
return False | |
# if hasattr(self.obj_mmd, "mmd_root") == False: | |
# print('selected object has no attrivute : mmd_root') | |
# return False | |
self.arm_mmd = self.obj_mmd.data | |
if self.arm_mmd is None: | |
print('can not find MMD armature') | |
return False | |
print('success check') | |
return True | |
def get_bone(self): | |
# 重複のないボーン名のリストを作成する。 | |
bone_name_list =[] | |
bone_name_list.extend(self.bone_name_map_tail.values()) | |
bone_name_list.extend(self.bone_name_map_head.values()) | |
bone_name_list.extend(self.extra_required_bone_names) | |
bone_name_list = list(set(bone_name_list)) | |
# ボーン名をキーにしたMMDボーン辞書を作成 | |
for bone in self.arm_mmd.bones: | |
if bone.name in bone_name_list: | |
self.mmd_bone_map[bone.name] = bone | |
# 必要なボーンが無い場合は終了 | |
if len(bone_name_list) != len(self.mmd_bone_map.keys()): | |
print('MMD armature has no required bone.') | |
return False | |
print('success get bone') | |
return True | |
def init_bone_location_map(self): | |
# 単純な対応関係ではないボーンの位置を設定する。 | |
# metarigタイプ別の設定 | |
if self.pitchipoy: | |
# pelvisの先端は特に設定していない。 | |
self.bone_location_map_tail["spine.004"] = self.getCenter(self.mmd_bone_map["首"]) | |
self.bone_location_map_tail["spine"] = self.mmd_bone_map["下半身"].head_local | |
self.bone_location_map_tail["spine.002"] = self.getCenter(self.mmd_bone_map["上半身2"]) | |
self.bone_location_map_tail["face"] = self.getCenter(self.mmd_bone_map["頭"]) | |
spine_tail = self.mmd_bone_map["下半身"].tail_local | |
self.bone_location_map_head["spine"] = spine_tail | |
self.bone_location_map_head["pelvis.L"] = spine_tail | |
self.bone_location_map_head["pelvis.R"] = spine_tail | |
heel_tail_L = self.mmd_bone_map["ひざ.L"].tail_local.copy() | |
heel_tail_L[0] += 0.067 | |
heel_tail_L[1] += 0.05 | |
heel_tail_L[2] = 0.0 | |
self.bone_location_map_tail["heel.02.L"] = heel_tail_L | |
heel_tail_R = self.mmd_bone_map["ひざ.R"].tail_local.copy() | |
heel_tail_R[0] -= 0.067 | |
heel_tail_R[1] += 0.05 | |
heel_tail_R[2] = 0.0 | |
self.bone_location_map_tail["heel.02.R"] = heel_tail_R | |
heel_head_L = self.mmd_bone_map["ひざ.L"].tail_local.copy() | |
heel_head_L[0] -= 0.067 | |
heel_head_L[1] += 0.05 | |
heel_head_L[2] = 0.0 | |
self.bone_location_map_head["heel.02.L"] = heel_head_L | |
heel_head_R = self.mmd_bone_map["ひざ.R"].tail_local.copy() | |
heel_head_R[0] += 0.067 | |
heel_head_R[1] += 0.05 | |
heel_head_R[2] = 0.0 | |
self.bone_location_map_head["heel.02.R"] = heel_head_R | |
else: | |
self.bone_location_map_tail["hips"] = self.mmd_bone_map["下半身"].head_local | |
ex_heel_tailL = self.mmd_bone_map["ひざ.L"].tail_local.copy() | |
ex_heel_tailL[1] += 0.12 | |
ex_heel_tailL[2] = 0.0 | |
self.bone_location_map_tail["heel.L"] = ex_heel_tailL | |
ex_heel_tailR = self.mmd_bone_map["ひざ.R"].tail_local.copy() | |
ex_heel_tailR[1] += 0.12 | |
ex_heel_tailR[2] = 0.0 | |
self.bone_location_map_tail["heel.R"] = ex_heel_tailR | |
heel_tail_L = self.mmd_bone_map["ひざ.L"].tail_local.copy() | |
heel_tail_L[0] += 0.067 | |
heel_tail_L[2] = 0.0 | |
self.bone_location_map_tail["heel.02.L"] = heel_tail_L | |
heel_tail_R = self.mmd_bone_map["ひざ.R"].tail_local.copy() | |
heel_tail_R[0] -= 0.067 | |
heel_tail_R[2] = 0.0 | |
self.bone_location_map_tail["heel.02.R"] = heel_tail_R | |
heel_head_L = self.mmd_bone_map["ひざ.L"].tail_local.copy() | |
heel_head_L[0] -= 0.067 | |
heel_head_L[2] = 0.0 | |
self.bone_location_map_head["heel.02.L"] = heel_head_L | |
heel_head_R = self.mmd_bone_map["ひざ.R"].tail_local.copy() | |
heel_head_R[0] += 0.067 | |
heel_head_R[2] = 0.0 | |
self.bone_location_map_head["heel.02.R"] = heel_head_R | |
# tail | |
self.bone_location_map_tail["palm.01.L"] = self.mmd_bone_map["人指1.L"].head_local | |
self.bone_location_map_tail["palm.02.L"] = self.mmd_bone_map["中指1.L"].head_local | |
self.bone_location_map_tail["palm.03.L"] = self.mmd_bone_map["薬指1.L"].head_local | |
self.bone_location_map_tail["palm.04.L"] = self.mmd_bone_map["小指1.L"].head_local | |
self.bone_location_map_tail["palm.01.R"] = self.mmd_bone_map["人指1.R"].head_local | |
self.bone_location_map_tail["palm.02.R"] = self.mmd_bone_map["中指1.R"].head_local | |
self.bone_location_map_tail["palm.03.R"] = self.mmd_bone_map["薬指1.R"].head_local | |
self.bone_location_map_tail["palm.04.R"] = self.mmd_bone_map["小指1.R"].head_local | |
toe_L = self.mmd_bone_map["足首.L"].tail_local.copy() | |
toe_L[1] -= 0.11 | |
self.bone_location_map_tail["toe.L"] = toe_L | |
toe_R = self.mmd_bone_map["足首.R"].tail_local.copy() | |
toe_R[1] -= 0.11 | |
self.bone_location_map_tail["toe.R"] = toe_R | |
# head | |
wrist_L = self.mmd_bone_map["手捩.L"].tail_local | |
index_L = self.mmd_bone_map["人指1.L"].head_local | |
middle_L = self.mmd_bone_map["中指1.L"].head_local | |
ring_L = self.mmd_bone_map["薬指1.L"].head_local | |
pinky_L = self.mmd_bone_map["小指1.L"].head_local | |
self.bone_location_map_head["palm.01.L"] = self.getCentre(index_L, wrist_L) | |
self.bone_location_map_head["palm.02.L"] = self.getCentre(middle_L, wrist_L) | |
self.bone_location_map_head["palm.03.L"] = self.getCentre(ring_L, wrist_L) | |
self.bone_location_map_head["palm.04.L"] = self.getCentre(pinky_L, wrist_L) | |
wrist_R = self.mmd_bone_map["手捩.R"].tail_local | |
index_R = self.mmd_bone_map["人指1.R"].head_local | |
middle_R = self.mmd_bone_map["中指1.R"].head_local | |
ring_R = self.mmd_bone_map["薬指1.R"].head_local | |
pinky_R = self.mmd_bone_map["小指1.R"].head_local | |
self.bone_location_map_head["palm.01.R"] = self.getCentre(index_R, wrist_R) | |
self.bone_location_map_head["palm.02.R"] = self.getCentre(middle_R, wrist_R) | |
self.bone_location_map_head["palm.03.R"] = self.getCentre(ring_R, wrist_R) | |
self.bone_location_map_head["palm.04.R"] = self.getCentre(pinky_R, wrist_R) | |
print('success init bone location map') | |
return True | |
def getCenter(self, bone): | |
return self.getCentre(bone.tail_local, bone.head_local) | |
def getCentre(self, vector1, vector2): | |
def_x = (vector1[0] - vector2[0]) / 2.0 | |
def_y = (vector1[1] - vector2[1]) / 2.0 | |
def_z = (vector1[2] - vector2[2]) / 2.0 | |
return Vector((vector2[0] + def_x, vector2[1] + def_y, vector2[2] + def_z)) | |
def create_metarig(self): | |
# Rigifyのメタリグはどこに置いたとしても、 | |
# 生成ボタンをおすと(0, 0, 0)にRigifyArmatureが生成される。 | |
# よってメタリグも(0, 0, 0)に生成するようにする。 | |
bpy.context.scene.cursor_location = (0.0, 0.0, 0.0) | |
if self.pitchipoy: bpy.ops.object.armature_pitchipoy_human_metarig_add() | |
else: bpy.ops.object.armature_human_metarig_add() | |
self.obj_metarig = bpy.context.object | |
self.arm_metarig = self.obj_metarig.data | |
# 縮尺をだいたい合わせる | |
z_mmd = self.mmd_bone_map["頭"].tail_local[2] | |
if self.pitchipoy: | |
z_rigify = self.arm_metarig.bones["spine.006"].tail_local[2] | |
else: | |
z_rigify = self.arm_metarig.bones["head"].tail_local[2] | |
scale = z_mmd / z_rigify | |
self.obj_metarig.scale = (scale, scale, scale) | |
bpy.ops.object.transform_apply(scale=True) | |
print('success create metarig') | |
return True | |
def edit_bone(self): | |
bpy.context.scene.objects.active = self.obj_metarig | |
self.obj_metarig.select = True | |
bpy.ops.object.mode_set(mode='EDIT') | |
for bone_rigify in self.arm_metarig.edit_bones: | |
# tailの位置合わせ | |
if bone_rigify.name in self.bone_name_map_tail: | |
key_mmd = self.bone_name_map_tail[bone_rigify.name] | |
bone_mmd = self.mmd_bone_map[key_mmd] | |
bone_rigify.tail = bone_mmd.tail_local | |
# headの位置合わせ | |
if bone_rigify.name in self.bone_name_map_head: | |
key_mmd = self.bone_name_map_head[bone_rigify.name] | |
bone_mmd = self.mmd_bone_map[key_mmd] | |
bone_rigify.head = bone_mmd.head_local | |
# その他の位置合わせ | |
if bone_rigify.name in self.bone_location_map_tail: | |
bone_rigify.tail = self.bone_location_map_tail[bone_rigify.name] | |
if bone_rigify.name in self.bone_location_map_head: | |
bone_rigify.head = self.bone_location_map_head[bone_rigify.name] | |
bpy.ops.object.mode_set(mode='OBJECT') | |
print('success edit bone') | |
return True | |
def createRigify(self): | |
bpy.ops.pose.rigify_generate() | |
print('success create rigify') | |
return True | |
if __name__ == "__main__": | |
print("---------- start script ----------") | |
MetarigController().execute() | |
print("---------- end script ----------") |
MMDアーマチュアを選択した状態で、スクリプトを実行する。
スクリプト実行後、metarigが生成されていることを確認する。
Rigify生成
メタリグのアーマチュアタブの一番下、「生成」(Generate)ボタンをクリックし、
Rigifyアーマチュアを生成する。
必須ではないが、Rigifyアーマチュア生成後はmetarigをどこか適当なレイヤーに移動させておく。
ここではボーンシェイプオブジェクトが生成される19番レイヤーに移動しておく。
Rigifyボーン設定
以下のコードをテキストエディタに張り付ける。
※metarig生成時のコードを上書きでOK
import bpy | |
from commons import MmdRigifyBoneNameMap | |
class RigifyBoneController: | |
pitchipoy = True | |
objects = bpy.context.selected_objects | |
obj_mmd = None | |
obj_rigify = None | |
arm_mmd = None | |
arm_rigify = None | |
rig_id = "" | |
bone_name_map = MmdRigifyBoneNameMap(pitchipoy) | |
def execute(self): | |
if self.check() == False: return | |
if self.join() == False: return | |
if self.change_parent() == False: return | |
def check(self): | |
if len(self.objects) != 2: | |
print('selected objects count : ' + str(len(self.objects))) | |
return False | |
for obj in self.objects: | |
if obj.type != 'ARMATURE': | |
print('selected object is not mesh : ' + obj.type) | |
return False | |
if obj.mode != 'OBJECT': | |
print('selected object is not object mode : ' + obj.mode) | |
return False | |
for obj in self.objects: | |
if "rig_id" in obj.data: | |
self.obj_rigify = obj | |
else: | |
self.obj_mmd = obj | |
if self.obj_mmd == None: | |
print('can not find MMD object') | |
return False | |
if self.obj_rigify == None: | |
print('can not find Rigify object') | |
return False | |
self.arm_mmd = self.obj_mmd.data | |
self.arm_rigify = self.obj_rigify.data | |
self.rig_id = self.arm_rigify["rig_id"] | |
print("rigify rig id : " + self.rig_id) | |
print('success check') | |
return True | |
def join(self): | |
# ボーンレイヤーを変更 | |
for bone in self.arm_mmd.bones: | |
if bone.layers[0] == True: | |
bone.layers[24] = True | |
bone.layers[0] = False | |
if bone.layers[8] == True: | |
bone.layers[25] = True | |
bone.layers[8] = False | |
# 可視レイヤーを一時保存 | |
layers = self.arm_rigify.layers | |
# 統合 | |
bpy.context.scene.objects.active = self.obj_mmd | |
bpy.ops.object.join() | |
self.arm_mmd.layers = layers | |
self.obj_mmd.draw_type = 'WIRE' | |
self.arm_mmd["rig_id"] = self.rig_id | |
# MMDボーンを可視にしないと、ボーンコンストレイントの編集でエラーとなる | |
self.arm_mmd.layers[24] = True | |
self.arm_mmd.layers[25] = True | |
# レントゲン設定(おまけ) | |
self.obj_mmd.show_x_ray = True | |
print('success join') | |
return True | |
def change_parent(self): | |
bpy.context.scene.objects.active = self.obj_mmd | |
self.obj_mmd.select = True | |
bpy.ops.object.mode_set(mode='EDIT') | |
for bone in self.arm_mmd.edit_bones: | |
if bone.name in self.bone_name_map: | |
continue | |
if bone.parent is not None: | |
if bone.parent.name in self.bone_name_map: | |
parentName = self.bone_name_map[bone.parent.name] | |
bone.parent = self.arm_mmd.edit_bones[parentName] | |
bpy.ops.object.mode_set(mode='OBJECT') | |
bpy.ops.object.mode_set(mode='POSE') | |
for bone in self.obj_mmd.pose.bones: | |
for constraint in bone.constraints: | |
if constraint.type == 'CHILD_OF': | |
if constraint.subtarget in self.bone_name_map: | |
# ボーンコンストレイントの親を変更 | |
constraint.subtarget = self.bone_name_map[constraint.subtarget] | |
# 逆補正を設定 | |
pbone = bpy.context.active_object.pose.bones[bone.name] | |
context = bpy.context.copy() | |
context["constraint"] = constraint | |
bpy.context.active_object.data.bones.active = pbone.bone | |
bpy.ops.constraint.childof_set_inverse(context, constraint=constraint.name, owner='BONE') | |
bpy.ops.object.mode_set(mode='OBJECT') | |
self.arm_mmd.layers[24] = False | |
self.arm_mmd.layers[25] = False | |
print('success change parent') | |
return True | |
if __name__ == "__main__": | |
print("---------- start script ----------") | |
RigifyBoneController().execute() | |
print("---------- end script ----------") |
MMDアーマチュアと、生成されたRigifyアーマチュアを選択してスクリプトを実行する。
※アクティブオブジェクトはスクリプト内で設定しているので、選択する順番は適当でOK
スクリプトを実行すると、MMDアーマチュアとRigifyアーマチュアが統合される。
ここでは大まかに以下の処理を行っている。
- MMDボーンを24,25番レイヤーに移動
- MMDアーマチュアとRigifyアーマチュアを統合
- MMDボーンの親をRigifyボーンに変更
- MMDボーンに付与されているコンストレイントのターゲット先をRigifyボーンに変更
頂点グループ名変更
以下のコードをテキストエディタに張り付ける。
※Rigifyボーン設定時のコードを上書きでOK
import bpy | |
from commons import MmdRigifyBoneNameMap | |
class VertexNameController: | |
pitchipoy = True | |
obj = bpy.context.active_object | |
bone_name_map = MmdRigifyBoneNameMap(pitchipoy) | |
def execute(self): | |
if self.check() == False: return | |
if self.rename() == False: return | |
def check(self): | |
if self.obj.type != 'MESH': | |
print('selected object is not mesh : ' + obj.type) | |
return False | |
if self.obj.mode != 'OBJECT': | |
print('selected object is not object mode : ' + self.obj.mode) | |
return False | |
bone_count = 0 | |
for vertex_group in self.obj.vertex_groups: | |
if vertex_group.name in self.bone_name_map: | |
bone_count += 1 | |
if len(self.bone_name_map.keys()) != bone_count: | |
print('bone count : ' + str(bone_count)) | |
return False | |
print('success check') | |
return True | |
def rename(self): | |
for vertex_group in self.obj.vertex_groups: | |
if vertex_group.name in self.bone_name_map: | |
vertex_group.name = self.bone_name_map[vertex_group.name] | |
print('success rename') | |
return True | |
if __name__ == "__main__": | |
print("---------- start script ----------") | |
VertexNameController().execute() | |
print("---------- end script ----------") |
モデルのメッシュを選択した状態でスクリプトを実行する。
スクリプト実行後、モデルの頂点グループ名がRigifyボーンの名前に置き換わる。
剛体設定
以下のコードをテキストエディタに張り付ける。
※頂点グループ名変更時のコードを上書きでOK
import bpy | |
from commons import MmdRigifyBoneNameMap | |
class RigController: | |
pitchipoy = True | |
objects = bpy.context.selected_objects | |
bone_name_map = MmdRigifyBoneNameMap(pitchipoy) | |
def execute(self): | |
if self.change_parent() == False: return | |
def change_parent(self): | |
for obj in self.objects: | |
if obj.type != 'MESH': | |
continue | |
for constraint in obj.constraints: | |
if constraint.type != 'CHILD_OF': | |
continue | |
if constraint.subtarget in self.bone_name_map: | |
# 一つ一つの処理が重いので、名前を表示させて進捗確認 | |
print(obj.name) | |
bpy.context.scene.objects.active = obj | |
obj.select = True | |
bpy.ops.object.mode_set(mode='EDIT') | |
# ボーンコンストレイントの親を変更 | |
constraint.subtarget = self.bone_name_map[constraint.subtarget] | |
# 逆補正を設定 | |
context = bpy.context.copy() | |
context["constraint"] = constraint | |
bpy.context.active_object.data = obj.data | |
bpy.ops.constraint.childof_set_inverse(context, constraint=constraint.name, owner='OBJECT') | |
bpy.ops.object.mode_set(mode='OBJECT') | |
print('success change parent') | |
return True | |
if __name__ == "__main__": | |
print("---------- start script ----------") | |
RigController().execute() | |
print("---------- end script ----------") |
剛体を選択した状態でスクリプトを実行する。
※結構時間がかかるので、コンソールを眺めながらのんびり待つ。
確認
再生ボタンをクリックしてRigifyボーンを動かすと、
メッシュと剛体がボーンに追従して動くことが確認できる。
おまけ
Deformボーンの表示・非表示ボタンの追加は以下の記事を参考に。
URL : http://ch.nicovideo.jp/takosuke/blomaga/ar710878
おわりに
私自身は、今のところレミリアモデルしか使用する予定がないので、
他のモデルへの対応は未定。
また、Rigify Typeに応じた処理なども実装していないですが、
標準Rigifyボーンで満足してしまっているのでこちらも優先度が低いです。
各スクリプトファイルのpitchipoyプロパティをすべてFalseにすると、
pitchipoyでない方のRigifyアーマチュアの作成が可能です。
このページのソースコードはすべてライセンスCC0とします。
MMD x Rigifyのアドオンが増えることを願います。
(Rigify適用モデルを配布なんかしていただけたら・・・凄くありがたい!)
何かありましたら、@takosuke_twまで連絡下さい。