forked from bartvdbraak/blender
198 lines
7.9 KiB
Python
198 lines
7.9 KiB
Python
import bpy
|
|
from mathutils import *
|
|
from math import radians, acos
|
|
performer_obj = bpy.data.objects["performer"]
|
|
enduser_obj = bpy.data.objects["enduser"]
|
|
end_arm = bpy.data.armatures["enduser_arm"]
|
|
scene = bpy.context.scene
|
|
|
|
#TODO: Only selected bones get retargeted.
|
|
# Selected Bones/chains get original pos empties, if ppl want IK instead of FK
|
|
# Some "magic" numbers - frame start and end, eulers of all orders instead of just quats keyframed
|
|
|
|
|
|
|
|
# dictionary of mapping
|
|
# this is currently manuall input'ed, but will
|
|
# be created from a more comfortable UI in the future
|
|
bonemap = { "LeftFoot": ("DEF_Foot.L","DEF_Toes.L"),
|
|
"LeftUpLeg": "DEF_Thigh.L",
|
|
"Hips": "DEF_Hip",
|
|
"LowerBack": "DEF_Spine",
|
|
"Spine": "DEF_Torso",
|
|
"Neck": "DEF_Neck",
|
|
"Neck1": "DEF_Neck",
|
|
"Head": "DEF_Head",
|
|
"LeftShoulder": "DEF_Shoulder.L",
|
|
"LeftArm": "DEF_Forearm.L",
|
|
"LeftForeArm": "DEF_Arm.L",
|
|
"LeftHand": "DEF_Hand.L",
|
|
"RightShoulder": "DEF_Shoulder.R",
|
|
"RightArm": "DEF_Forearm.R",
|
|
"RightForeArm": "DEF_Arm.R",
|
|
"RightHand": "DEF_Hand.R",
|
|
"RightFoot": ("DEF_Foot.R","DEF_Toes.R"),
|
|
"RightUpLeg": "DEF_Thigh.R",
|
|
"RightLeg": "DEF_Shin.R",
|
|
"LeftLeg": "DEF_Shin.L"}
|
|
|
|
# creation of a reverse map
|
|
# multiple keys get mapped to list values
|
|
bonemapr = {}
|
|
for key in bonemap.keys():
|
|
if not bonemap[key] in bonemapr:
|
|
if type(bonemap[key])==type((0,0)):
|
|
for key_x in bonemap[key]:
|
|
bonemapr[key_x] = [key]
|
|
else:
|
|
bonemapr[bonemap[key]] = [key]
|
|
else:
|
|
bonemapr[bonemap[key]].append(key)
|
|
|
|
# list of empties created to keep track of "original"
|
|
# position data
|
|
# in final product, these locations can be stored as custom props
|
|
# these help with constraining, etc.
|
|
|
|
constraints = []
|
|
|
|
|
|
#creation of intermediate armature
|
|
# the intermediate armature has the hiearchy of the end user,
|
|
# does not have rotation inheritence
|
|
# and bone roll is identical to the performer
|
|
# its purpose is to copy over the rotations
|
|
# easily while concentrating on the hierarchy changes
|
|
def createIntermediate():
|
|
|
|
#creates and keyframes an empty with its location
|
|
#the original position of the tail bone
|
|
#useful for storing the important data in the original motion
|
|
#i.e. using this empty to IK the chain to that pos.
|
|
def locOfOriginal(inter_bone,perf_bone):
|
|
if not perf_bone.name+"Org" in bpy.data.objects:
|
|
bpy.ops.object.add()
|
|
empty = bpy.context.active_object
|
|
empty.name = perf_bone.name+"Org"
|
|
empty = bpy.data.objects[perf_bone.name+"Org"]
|
|
offset = perf_bone.vector
|
|
scaling = perf_bone.length / inter_bone.length
|
|
offset/=scaling
|
|
empty.location = inter_bone.head + offset
|
|
empty.keyframe_insert("location")
|
|
|
|
#Simple 1to1 retarget of a bone
|
|
def singleBoneRetarget(inter_bone,perf_bone):
|
|
perf_world_rotation = perf_bone.matrix * performer_obj.matrix_world
|
|
inter_world_base_rotation = inter_bone.bone.matrix_local * inter_obj.matrix_world
|
|
inter_world_base_inv = Matrix(inter_world_base_rotation)
|
|
inter_world_base_inv.invert()
|
|
return (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()).to_4x4()
|
|
|
|
#uses 1to1 and interpolation/averaging to match many to 1 retarget
|
|
def manyPerfToSingleInterRetarget(inter_bone,performer_bones_s):
|
|
retarget_matrices = [singleBoneRetarget(inter_bone,perf_bone) for perf_bone in performer_bones_s]
|
|
lerp_matrix = Matrix()
|
|
for i in range(len(retarget_matrices)-1):
|
|
first_mat = retarget_matrices[i]
|
|
next_mat = retarget_matrices[i+1]
|
|
lerp_matrix = first_mat.lerp(next_mat,0.5)
|
|
return lerp_matrix
|
|
|
|
#determines the type of hierachy change needed and calls the
|
|
#right function
|
|
def retargetPerfToInter(inter_bone):
|
|
if inter_bone.name in bonemapr.keys():
|
|
perf_bone_name = bonemapr[inter_bone.name]
|
|
#is it a 1 to many?
|
|
if type(bonemap[perf_bone_name[0]])==type((0,0)):
|
|
perf_bone = performer_bones[perf_bone_name[0]]
|
|
if inter_bone.name == bonemap[perf_bone_name[0]][0]:
|
|
locOfOriginal(inter_bone,perf_bone)
|
|
else:
|
|
# then its either a many to 1 or 1 to 1
|
|
|
|
if len(perf_bone_name) > 1:
|
|
performer_bones_s = [performer_bones[name] for name in perf_bone_name]
|
|
#we need to map several performance bone to a single
|
|
for perf_bone in performer_bones_s:
|
|
locOfOriginal(inter_bone,perf_bone)
|
|
inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone,performer_bones_s)
|
|
else:
|
|
perf_bone = performer_bones[perf_bone_name[0]]
|
|
locOfOriginal(inter_bone,perf_bone)
|
|
inter_bone.matrix_basis = singleBoneRetarget(inter_bone,perf_bone)
|
|
|
|
inter_bone.keyframe_insert("rotation_quaternion")
|
|
for child in inter_bone.children:
|
|
retargetPerfToInter(child)
|
|
|
|
#creates the intermediate armature object
|
|
bpy.ops.object.select_name(name="enduser",extend=False)
|
|
bpy.ops.object.duplicate(linked=False)
|
|
bpy.context.active_object.name = "intermediate"
|
|
inter_obj = bpy.context.active_object
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
#resets roll
|
|
bpy.ops.armature.calculate_roll(type='Z')
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
inter_arm = bpy.data.armatures["enduser_arm.001"]
|
|
inter_arm.name = "inter_arm"
|
|
performer_bones = performer_obj.pose.bones
|
|
inter_bones = inter_obj.pose.bones
|
|
|
|
#clears inheritance
|
|
for inter_bone in inter_bones:
|
|
inter_bone.bone.use_inherit_rotation = False
|
|
|
|
for t in range(1,150):
|
|
scene.frame_set(t)
|
|
inter_bone = inter_bones["DEF_Hip"]
|
|
retargetPerfToInter(inter_bone)
|
|
|
|
return inter_obj,inter_arm
|
|
|
|
# this procedure copies the rotations over from the intermediate
|
|
# armature to the end user one.
|
|
# As the hierarchies are 1 to 1, this is a simple matter of
|
|
# copying the rotation, while keeping in mind bone roll, parenting, etc.
|
|
# TODO: Control Bones: If a certain bone is constrained in a way
|
|
# that its rotation is determined by another (a control bone)
|
|
# We should determine the right pos of the control bone.
|
|
# Scale: ? Should work but needs testing.
|
|
def retargetEnduser():
|
|
inter_bones = inter_obj.pose.bones
|
|
end_bones = enduser_obj.pose.bones
|
|
|
|
def bakeTransform(end_bone):
|
|
src_bone = inter_bones[end_bone.name]
|
|
trg_bone = end_bone
|
|
bake_matrix = src_bone.matrix
|
|
rest_matrix = trg_bone.bone.matrix_local
|
|
|
|
if trg_bone.parent and trg_bone.bone.use_inherit_rotation:
|
|
parent_mat = src_bone.parent.matrix
|
|
parent_rest = trg_bone.parent.bone.matrix_local
|
|
parent_rest_inv = parent_rest.copy()
|
|
parent_rest_inv.invert()
|
|
parent_mat_inv = parent_mat.copy()
|
|
parent_mat_inv.invert()
|
|
bake_matrix = parent_mat_inv * bake_matrix
|
|
rest_matrix = parent_rest_inv * rest_matrix
|
|
|
|
rest_matrix_inv = rest_matrix.copy()
|
|
rest_matrix_inv.invert()
|
|
bake_matrix = rest_matrix_inv * bake_matrix
|
|
trg_bone.matrix_basis = bake_matrix
|
|
end_bone.keyframe_insert("rotation_quaternion")
|
|
|
|
for bone in end_bone.children:
|
|
bakeTransform(bone)
|
|
|
|
for t in range(1,150):
|
|
scene.frame_set(t)
|
|
end_bone = end_bones["DEF_Hip"]
|
|
bakeTransform(end_bone)
|
|
|
|
inter_obj, inter_arm = createIntermediate()
|
|
retargetEnduser() |