Skip to content

Instantly share code, notes, and snippets.

@xenogenesi
Last active January 5, 2021 02:05
Show Gist options
  • Save xenogenesi/3e5cdb29f2071109631206ec610291fc to your computer and use it in GitHub Desktop.
Save xenogenesi/3e5cdb29f2071109631206ec610291fc to your computer and use it in GitHub Desktop.
Blender script to re-target motion capture
bl_info = {
"name": "Rig Empty Retarget",
"blender": (2, 91, 0),
"author": "Alex <raziel@eml.cc>",
"version": (0, 1),
"category": "Rigging",
"location": "Operator Search",
"description": "Helpers to re-target animations",
"warning": "",
"doc_url": "",
"tracker_url": "",
}
# select source armature and go into pose mode, select the bone(s) and invoke "Add Bone Empty" from the search menu
# edit the bmap below according to your armatures bones name: "target": "source"
# select target armature/rig and go into pose mode, select the bones and invoke "Add Bone Copy Rotation" from the search menu
# (the names in the bmap will be used as target)
# Credits to:
# https://youtu.be/LU0u_b88K8k
# https://youtu.be/Sc_0PfNnJsw
# DONE
# 1. place empty(ies) for each selected bone (pose mode) in the source armature
# 2. map and add copy-rotation constrain for each bone in the target armature (rigify)
# TODO
# 3. calculate empty(ies) rotations to re-align target armature in rest-position
import bpy
from mathutils import Matrix
# FIXME the map should be some external file yaml/json/...
bmap = {
"head": "head",
"neck": "neck",
"chest": "chest",
# "breast.L": ?
# "breast.R": ?
"torso": "abdomen", # ?
"hips": "hip",
## LEFT
"thigh_fk.L": "lThigh",
"shin_fk.L": "lShin",
"foot_fk.L": "lFoot",
# "toe.L": ?
"shoulder.L": "lCollar",
"upper_arm_fk.L": "lShldr",
"forearm_fk.L": "lForeArm",
"hand_fk.L": "lHand",
## RIGHT
"thigh_fk.R": "rThigh",
"shin_fk.R": "rShin",
"foot_fk.R": "rFoot",
# "toe.R": ?
"shoulder.R": "rCollar",
"upper_arm_fk.R": "rShldr",
"forearm_fk.R": "rForeArm",
"hand_fk.R": "rHand"
}
def bone_add_empty(context):
# for ob in context.scene.objects:
# print(ob)
sel_bones = []
if bpy.context.selected_pose_bones != None:
for bone in bpy.context.selected_pose_bones:
sel_bones.append(bone)
for bone in sel_bones:
target = "Empty_" + bone.name
armature = bpy.context.active_object
if target in bpy.data.objects:
if bpy.data.objects[target].parent == armature and bpy.data.objects[target].parent_bone == bone.name:
continue
empty = bpy.data.objects.new(target, None)
empty.empty_display_size = 5
empty.empty_display_type = 'SPHERE'
bpy.context.collection.objects.link(empty)
empty.parent = armature
empty.parent_bone = bone.name
empty.parent_type = 'BONE'
mpi = empty.matrix_parent_inverse
ti = Matrix.Translation([0, bone.length, 0]).inverted()
empty.matrix_parent_inverse = mpi @ ti
bpy.context.view_layer.update()
def bone_add_copy_rotation(context):
if bpy.context.selected_pose_bones != None:
for bone in bpy.context.selected_pose_bones:
if bone.name in bmap:
print(f"{bone.name} -> {bmap[bone.name]}")
#print(bone.constraints)
target = "Empty_" + bmap[bone.name]
if not target in bpy.data.objects:
print(f"Warning: target '{target}' doesn't exists, ignored")
continue
# check each constraint, if exists without target, set it (FIXME need to check subtarget?)
# if exists and have the same target, skip it
# if doesn't exists, create it, set the target and go on
found = False
for constraint in bone.constraints:
if constraint.type == 'COPY_ROTATION':
if constraint.target == None:
copy_rot_con = constraint
copy_rot_con.target = bpy.data.objects[target]
found = True
break
elif constraint.target == bpy.data.objects[target]:
copy_rot_con = constraint
found = True
break
if not found:
copy_rot_con = bone.constraints.new('COPY_ROTATION')
copy_rot_con.target = bpy.data.objects[target]
class BoneAddEmpty(bpy.types.Operator):
"""Add an empty object to selected pose bones"""
bl_idname = "pose.bone_add_empty"
bl_label = "Bone Add Empty"
@classmethod
def poll(cls, context):
return context.active_object is not None and context.mode == 'POSE'
def execute(self, context):
bone_add_empty(context)
return {'FINISHED'}
class BoneAddCopyRotation(bpy.types.Operator):
"""Add Copy-Rotation Constraint to selected pose bones"""
bl_idname = "pse.bone_add_copy_rotation"
bl_label = "Bone Add Copy Rotation"
@classmethod
def poll(cls, context):
return context.active_object is not None and context.mode == 'POSE'
def execute(self, context):
bone_add_copy_rotation(context)
return {'FINISHED'}
def register():
bpy.utils.register_class(BoneAddEmpty)
bpy.utils.register_class(BoneAddCopyRotation)
def unregister():
bpy.utils.unregister_class(BoneAddEmpty)
bpy.utils.unregister_class(BoneAddCopyRotation)
if __name__ == "__main__":
register()
# test call
#bpy.ops.object.simple_operator()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment