Skip to content

Instantly share code, notes, and snippets.

@creativeIKEP
Created January 3, 2022 12:45
Show Gist options
  • Save creativeIKEP/7634b5b5f616a087dcb10b925168053a to your computer and use it in GitHub Desktop.
Save creativeIKEP/7634b5b5f616a087dcb10b925168053a to your computer and use it in GitHub Desktop.
Auto rigging for mimiclear with Blender and Kinect
# BlenderのAPIを呼び出すのに必要
import bpy
import os
import csv
directory = os.path.dirname(bpy.data.filepath)
def delete_all_objects():
# 全データオブジェクトを削除する
for item in bpy.data.objects:
bpy.data.objects.remove(item)
# 全メッシュデータを削除する
for item in bpy.data.meshes:
bpy.data.meshes.remove(item)
# 全マテリアルデータを削除する
for item in bpy.data.materials:
bpy.data.materials.remove(item)
return
# PIFuHDが生成したOBJファイルをBlenderのシーンにインポート
def mesh_import():
path = os.path.join(directory, "Avatar.obj")
bpy.ops.import_scene.obj(filepath=path)
return bpy.context.scene.objects["Avatar"]
# メッシュデータをリグ付けりやすくする
def fix_model(object):
bpy.context.view_layer.objects.active = object
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="SELECT")
# https://nn-hokuson.hatenablog.com/entry/2017/08/30/195716
# リグ付け成功率をあげるために、ここに書かれている処理をする
# PIFuHDが出力するメッシュは頂点数が多いため重複点は削除
bpy.ops.mesh.remove_doubles()
# メッシュのスムージング
bpy.ops.mesh.vertices_smooth()
bpy.ops.object.mode_set(mode='OBJECT')
# CSVファイルを読み込んで使いやすいように整形
def kinect_joint_import():
path = os.path.join(directory, "KinectJoints.csv")
print(path)
with open(path) as f:
reader = csv.reader(f)
data = {}
for row in reader:
data[row[0]] = [float(row[1]), float(row[2]), float(row[3])]
return data
# 人骨格点情報からボーンを生成
def create_bones(data):
# 親となるhipボーンを生成
bpy.ops.object.armature_add(enter_editmode=True, location=(0, 0, 0))
hip = bpy.context.object
kinect_joint_names = \
["SpineBase", "SpineMid", "SpineShoulder", "Neck", "Head",
"ShoulderLeft", "ElbowLeft", "WristLeft", "HandLeft", "HandTipLeft", "ThumbLeft",
"ShoulderRight", "ElbowRight", "WristRight", "HandRight", "HandTipRight", "ThumbRight",
"HipLeft", "KneeLeft", "AnkleLeft", "FootLeft",
"HipRight", "KneeRight", "AnkleRight", "FootRight"]
# kinect_joint_namesのどのインデックス同士をつなぐか
joint_pairs = \
[[0, 1], [1, 2], [2, 3], [3, 4],
[2, 5], [5, 6], [6, 7], [7, 8], [8, 9], [8, 10],
[2, 11], [11, 12], [12, 13], [13, 14], [14, 15], [14, 16],
[0, 17], [17, 18], [18, 19], [19, 20],
[0, 21], [21, 22], [22, 23], [23, 24]
]
init_bone = hip.data.edit_bones.get("Bone")
hip.data.edit_bones.remove(init_bone)
# PIFuHDのメッシュは腰が原点になっているので、Kinectからのすべての骨格点をSpineBaseの座標分だけずらすようにする
offset_pos = (-data["SpineBase"][0], -data["SpineBase"][1], -data["SpineBase"][2])
for pair in joint_pairs:
# 新しくつなぐボーンの先端同士のKienctでの名前を取得
name = kinect_joint_names[pair[0]]
name_next = kinect_joint_names[pair[1]]
# 新しくつなぐボーンの生成
bone = hip.data.edit_bones.new(name_next)
# PIFuHDのメッシュは腰が原点になっているので、Kinectからのすべての骨格点をSpineBaseの座標分だけずらすようにする
bone.head = (-(data[name][0] + offset_pos[0]), data[name][2] + offset_pos[2], data[name][1] + offset_pos[1])
bone.tail = (-(data[name_next][0] + offset_pos[0]), data[name_next][2] + offset_pos[2], data[name_next][1] + offset_pos[1])
# ボーンの親子関係の構築
to_head_list = ["SpineMid", "SpineShoulder", "Neck", "Head"]
to_lefthand_list = ["SpineShoulder", "ShoulderLeft", "ElbowLeft", "WristLeft", "HandLeft", "HandTipLeft"]
to_righthand_list = ["SpineShoulder", "ShoulderRight", "ElbowRight", "WristRight", "HandRight", "HandTipRight"]
to_thumbleft_list = ["HandLeft", "ThumbLeft"]
to_thumbright_list = ["HandRight", "ThumbRight"]
to_legleft_list = ["SpineMid", "HipLeft", "KneeLeft", "AnkleLeft", "FootLeft"]
to_legright_list = ["SpineMid", "HipRight", "KneeRight", "AnkleRight", "FootRight"]
bone_list = [to_head_list, to_lefthand_list, to_righthand_list, to_thumbleft_list, to_thumbright_list, to_legleft_list, to_legright_list]
for list in bone_list:
for i in range(len(list)):
if i == 0:
continue;
parent_bone = hip.data.edit_bones.get(list[i-1])
child_bone = hip.data.edit_bones.get(list[i])
child_bone.parent = parent_bone
# Kinect to Unity Bone mapping
# Unityで読み込んだ時に自動でどのボーンがどの部分かを判別しやすくするようにUnityでの名前に変換
kinect_to_unity_bonename = {
"Head": "Head",
"Neck": "Neck",
"SpineMid": "Hips",
"SpineShoulder": "Spine",
"ElbowLeft": "LeftUpperArm",
"WristLeft": "LeftLowerArm",
"HandLeft": "LeftHand",
"ElbowRight": "RightUpperArm",
"WristRight": "RightLowerArm",
"HandRight": "RightHand",
"KneeLeft": "LeftUpperLeg",
"AnkleLeft": "LeftLowerLef",
"FootLeft": "LeftFoot",
"KneeRight": "RightUpperLeg",
"AnkleRight": "RightLowerLeg",
"FootRight": "RightFoot"
}
for name in kinect_joint_names:
if name not in kinect_to_unity_bonename:
continue
bone = hip.data.edit_bones.get(name)
bone.name = kinect_to_unity_bonename[name]
bpy.ops.object.mode_set(mode="OBJECT")
# main program
# リグ付け前の処理
delete_all_objects()
data = kinect_joint_import()
create_bones(data)
avatar_mesh = mesh_import()
fix_model(avatar_mesh)
bpy.context.view_layer.objects.active = bpy.data.objects["Armature"]
# リグ付けの実行
bpy.ops.object.parent_set(type='ARMATURE_AUTO')
# リグ付け後FBXファイルで出力
bpy.ops.export_scene.fbx(filepath=os.path.join(directory, "Avatar.fbx"))
# Blenderを終了
bpy.ops.wm.quit_blender()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment