573 lines
23 KiB
Python
573 lines
23 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
import bpy
|
|
from mathutils import Vector, Matrix
|
|
from math import radians
|
|
from bpy_extras.anim_utils import bake_action
|
|
|
|
|
|
def hasIKConstraint(pose_bone):
|
|
#utility function / predicate, returns True if given bone has IK constraint
|
|
ik = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"]
|
|
if ik:
|
|
return ik[0]
|
|
else:
|
|
return False
|
|
|
|
|
|
def createDictionary(perf_arm, end_arm):
|
|
# clear any old data
|
|
for end_bone in end_arm.bones:
|
|
for mapping in end_bone.reverseMap:
|
|
end_bone.reverseMap.remove(0)
|
|
|
|
for perf_bone in perf_arm.bones:
|
|
#find its match and add perf_bone to the match's mapping
|
|
if perf_bone.map:
|
|
end_bone = end_arm.bones[perf_bone.map]
|
|
newMap = end_bone.reverseMap.add()
|
|
newMap.name = perf_bone.name
|
|
end_bone.foot = perf_bone.foot
|
|
|
|
#root is the root of the enduser
|
|
root = end_arm.bones[0].name
|
|
feetBones = [bone.name for bone in perf_arm.bones if bone.foot]
|
|
return feetBones, root
|
|
|
|
|
|
def loadMapping(perf_arm, end_arm):
|
|
for end_bone in end_arm.bones:
|
|
#find its match and add perf_bone to the match's mapping
|
|
if end_bone.reverseMap:
|
|
for perf_bone in end_bone.reverseMap:
|
|
perf_arm.bones[perf_bone.name].map = end_bone.name
|
|
|
|
#creation of intermediate armature
|
|
# the intermediate armature has the hierarchy of the end user,
|
|
# does not have rotation inheritance
|
|
# 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(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step):
|
|
#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 / DEBUG
|
|
|
|
#Simple 1to1 retarget of a bone
|
|
def singleBoneRetarget(inter_bone, perf_bone):
|
|
perf_world_rotation = perf_bone.matrix
|
|
inter_world_base_rotation = inter_bone.bone.matrix_local
|
|
inter_world_base_inv = inter_world_base_rotation.inverted()
|
|
bake_matrix = inter_world_base_inv.to_3x3() @ perf_world_rotation.to_3x3()
|
|
return bake_matrix.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 hierarchy change needed and calls the
|
|
#right function
|
|
def retargetPerfToInter(inter_bone):
|
|
if inter_bone.bone.reverseMap:
|
|
perf_bone_name = inter_bone.bone.reverseMap
|
|
# 1 to many not supported yet
|
|
# then its either a many to 1 or 1 to 1
|
|
if len(perf_bone_name) > 1:
|
|
performer_bones_s = [performer_bones[map.name] for map in perf_bone_name]
|
|
#we need to map several performance bone to a single
|
|
inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone, performer_bones_s)
|
|
else:
|
|
perf_bone = performer_bones[perf_bone_name[0].name]
|
|
inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone)
|
|
#Some bones have incorrect roll on the source armature, and need to be marked for fixing
|
|
if inter_bone.bone.twistFix:
|
|
inter_bone.matrix_basis @= Matrix.Rotation(radians(180), 4, "Y")
|
|
rot_mode = inter_bone.rotation_mode
|
|
if rot_mode == "QUATERNION":
|
|
inter_bone.keyframe_insert("rotation_quaternion")
|
|
elif rot_mode == "AXIS_ANGLE":
|
|
inter_bone.keyframe_insert("rotation_axis_angle")
|
|
else:
|
|
inter_bone.keyframe_insert("rotation_euler")
|
|
|
|
#creates the intermediate armature object
|
|
inter_obj = enduser_obj.copy()
|
|
inter_obj.data = inter_obj.data.copy() # duplicate data
|
|
bpy.context.collection.objects.link(inter_obj)
|
|
inter_obj.name = "intermediate"
|
|
bpy.context.view_layer.objects.active = inter_obj
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
#add some temporary connecting bones in case end user bones are not connected to their parents
|
|
rollDict = {}
|
|
print("creating temp bones")
|
|
for bone in inter_obj.data.edit_bones:
|
|
if not bone.use_connect and bone.parent:
|
|
if inter_obj.data.bones[bone.parent.name].reverseMap or inter_obj.data.bones[bone.name].reverseMap:
|
|
newBone = inter_obj.data.edit_bones.new("Temp")
|
|
newBone.head = bone.parent.tail
|
|
newBone.tail = bone.head
|
|
newBone.parent = bone.parent
|
|
bone.parent = newBone
|
|
bone.use_connect = True
|
|
newBone.use_connect = True
|
|
rollDict[bone.name] = bone.roll
|
|
bone.roll = 0
|
|
#resets roll
|
|
print("retargeting to intermediate")
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
inter_obj.data.name = "inter_arm"
|
|
inter_arm = inter_obj.data
|
|
performer_bones = performer_obj.pose.bones
|
|
inter_bones = inter_obj.pose.bones
|
|
#clears inheritance
|
|
for inter_bone in inter_bones:
|
|
if inter_bone.bone.reverseMap:
|
|
inter_bone.bone.use_inherit_rotation = False
|
|
else:
|
|
inter_bone.bone.use_inherit_rotation = True
|
|
|
|
for t in range(s_frame, e_frame, step):
|
|
if (t - s_frame) % 10 == 0:
|
|
print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
|
scene.frame_set(t)
|
|
for bone in inter_bones:
|
|
retargetPerfToInter(bone)
|
|
|
|
return inter_obj
|
|
|
|
# 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_obj, enduser_obj, root, s_frame, e_frame, scene, step):
|
|
inter_bones = inter_obj.pose.bones
|
|
end_bones = enduser_obj.pose.bones
|
|
|
|
#Basic "visual baking" function, for transferring rotations from intermediate to end user
|
|
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:
|
|
srcParent = src_bone.parent
|
|
if "Temp" in srcParent.name:
|
|
srcParent = srcParent.parent
|
|
parent_mat = srcParent.matrix
|
|
parent_rest = trg_bone.parent.bone.matrix_local
|
|
parent_rest_inv = parent_rest.inverted()
|
|
parent_mat_inv = parent_mat.inverted()
|
|
bake_matrix = parent_mat_inv @ bake_matrix
|
|
rest_matrix = parent_rest_inv @ rest_matrix
|
|
|
|
rest_matrix_inv = rest_matrix.inverted()
|
|
bake_matrix = rest_matrix_inv @ bake_matrix
|
|
end_bone.matrix_basis = bake_matrix
|
|
rot_mode = end_bone.rotation_mode
|
|
if rot_mode == "QUATERNION":
|
|
end_bone.keyframe_insert("rotation_quaternion")
|
|
elif rot_mode == "AXIS_ANGLE":
|
|
end_bone.keyframe_insert("rotation_axis_angle")
|
|
else:
|
|
end_bone.keyframe_insert("rotation_euler")
|
|
if not end_bone.bone.use_connect:
|
|
end_bone.keyframe_insert("location")
|
|
|
|
for bone in end_bone.children:
|
|
bakeTransform(bone)
|
|
|
|
for t in range(s_frame, e_frame, step):
|
|
if (t - s_frame) % 10 == 0:
|
|
print("Second pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
|
scene.frame_set(t)
|
|
end_bone = end_bones[root]
|
|
end_bone.location = Vector((0, 0, 0))
|
|
end_bone.keyframe_insert("location")
|
|
bakeTransform(end_bone)
|
|
|
|
#receives the performer feet bones as a variable
|
|
# by "feet" I mean those bones that have plants
|
|
# (they don't move, despite root moving) somewhere in the animation.
|
|
|
|
|
|
def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame, scene, enduser_obj_mat):
|
|
|
|
perf_bones = performer_obj.pose.bones
|
|
end_bones = enduser_obj.pose.bones
|
|
|
|
perfRoot = perf_bones[0].name
|
|
endFeet = [perf_bones[perfBone].bone.map for perfBone in perfFeet]
|
|
locDictKeys = perfFeet + endFeet + [perfRoot]
|
|
|
|
def tailLoc(bone):
|
|
return bone.center + (bone.vector / 2)
|
|
|
|
#Step 1 - we create a dict that contains these keys:
|
|
#(Performer) Hips, Feet
|
|
#(End user) Feet
|
|
# where the values are their world position on each frame in range (s,e)
|
|
|
|
locDict = {}
|
|
for key in locDictKeys:
|
|
locDict[key] = []
|
|
|
|
for t in range(scene.frame_start, scene.frame_end):
|
|
scene.frame_set(t)
|
|
for bone in perfFeet:
|
|
locDict[bone].append(tailLoc(perf_bones[bone]))
|
|
locDict[perfRoot].append(tailLoc(perf_bones[perfRoot]))
|
|
for bone in endFeet:
|
|
locDict[bone].append(tailLoc(end_bones[bone]))
|
|
|
|
# now we take our locDict and analyze it.
|
|
# we need to derive all chains
|
|
|
|
def locDeriv(key, t):
|
|
graph = locDict[key]
|
|
return graph[t + 1] - graph[t]
|
|
|
|
# now find the plant frames, where perfFeet don't move much
|
|
|
|
linearAvg = []
|
|
for key in perfFeet:
|
|
for i in range(len(locDict[key]) - 1):
|
|
v = locDeriv(key, i)
|
|
if (v.length < 0.1):
|
|
hipV = locDeriv(perfRoot, i)
|
|
endV = locDeriv(perf_bones[key].bone.map, i)
|
|
#this is a plant frame.
|
|
#lets see what the original hip delta is, and the corresponding
|
|
#end bone's delta
|
|
if endV.length != 0:
|
|
linearAvg.append(hipV.length / endV.length)
|
|
|
|
action_name = performer_obj.animation_data.action.name
|
|
#is there a stride_bone?
|
|
if "stride_bone" in bpy.data.objects:
|
|
stride_action = bpy.data.actions.new("Stride Bone " + action_name)
|
|
stride_action.use_fake_user = True
|
|
stride_bone = enduser_obj.parent
|
|
stride_bone.animation_data.action = stride_action
|
|
else:
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
bpy.ops.object.add()
|
|
stride_bone = bpy.context.active_object
|
|
stride_bone.name = "stride_bone"
|
|
stride_bone.location = enduser_obj_mat.to_translation()
|
|
if linearAvg:
|
|
#determine the average change in scale needed
|
|
avg = sum(linearAvg) / len(linearAvg)
|
|
else:
|
|
avg = 1
|
|
scene.frame_set(s_frame)
|
|
initialPos = (tailLoc(perf_bones[perfRoot]) / avg)
|
|
for t in range(s_frame, e_frame):
|
|
scene.frame_set(t)
|
|
#calculate the new position, by dividing by the found ratio between performer and enduser
|
|
newTranslation = (tailLoc(perf_bones[perfRoot]) / avg)
|
|
stride_bone.location = enduser_obj_mat @ (newTranslation - initialPos)
|
|
stride_bone.keyframe_insert("location")
|
|
stride_bone.animation_data.action.name = ("Stride Bone " + action_name)
|
|
|
|
return stride_bone
|
|
|
|
|
|
def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step):
|
|
end_bones = enduser_obj.pose.bones
|
|
for pose_bone in end_bones:
|
|
ik_constraint = hasIKConstraint(pose_bone)
|
|
if ik_constraint:
|
|
target_is_bone = False
|
|
# set constraint target to corresponding empty if targetless,
|
|
# if not, keyframe current target to corresponding empty
|
|
perf_bone = pose_bone.bone.reverseMap[-1].name
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
orgLocTrg = originalLocationTarget(pose_bone, enduser_obj)
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
if not ik_constraint.target:
|
|
ik_constraint.target = enduser_obj
|
|
ik_constraint.subtarget = pose_bone.name + "IK"
|
|
target = orgLocTrg
|
|
|
|
# There is a target now
|
|
if ik_constraint.subtarget:
|
|
target = ik_constraint.target.pose.bones[ik_constraint.subtarget]
|
|
target.bone.use_local_location = False
|
|
target_is_bone = True
|
|
else:
|
|
target = ik_constraint.target
|
|
|
|
# bake the correct locations for the ik target bones
|
|
for t in range(s_frame, e_frame, step):
|
|
scene.frame_set(t)
|
|
if target_is_bone:
|
|
final_loc = pose_bone.tail - target.bone.matrix_local.to_translation()
|
|
else:
|
|
final_loc = pose_bone.tail
|
|
target.location = final_loc
|
|
target.keyframe_insert("location")
|
|
ik_constraint.mute = False
|
|
scene.frame_set(s_frame)
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
|
|
def turnOffIK(enduser_obj):
|
|
end_bones = enduser_obj.pose.bones
|
|
for pose_bone in end_bones:
|
|
ik_constraint = hasIKConstraint(pose_bone)
|
|
if ik_constraint:
|
|
ik_constraint.mute = True
|
|
|
|
|
|
#copy the object matrixes and clear them (to be reinserted later)
|
|
def cleanAndStoreObjMat(performer_obj, enduser_obj):
|
|
perf_obj_mat = performer_obj.matrix_world.copy()
|
|
enduser_obj_mat = enduser_obj.matrix_world.copy()
|
|
zero_mat = Matrix()
|
|
performer_obj.matrix_world = zero_mat
|
|
enduser_obj.matrix_world = zero_mat
|
|
return perf_obj_mat, enduser_obj_mat
|
|
|
|
|
|
#restore the object matrixes after parenting the auto generated IK empties
|
|
def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame):
|
|
pose_bones = enduser_obj.pose.bones
|
|
for pose_bone in pose_bones:
|
|
if pose_bone.name + "Org" in bpy.data.objects:
|
|
empty = bpy.data.objects[pose_bone.name + "Org"]
|
|
empty.parent = stride_bone
|
|
performer_obj.matrix_world = perf_obj_mat
|
|
enduser_obj.parent = stride_bone
|
|
scene.frame_set(s_frame)
|
|
enduser_obj_mat = enduser_obj_mat.to_3x3().to_4x4() @ Matrix.Translation(stride_bone.matrix_world.to_translation())
|
|
enduser_obj.matrix_world = enduser_obj_mat
|
|
|
|
|
|
#create (or return if exists) the related IK empty to the bone
|
|
def originalLocationTarget(end_bone, enduser_obj):
|
|
ik_bone = hasIKConstraint(end_bone).subtarget
|
|
if not ik_bone:
|
|
print("Adding IK bones for: " + end_bone.name)
|
|
newBone = enduser_obj.data.edit_bones.new(end_bone.name + "IK")
|
|
newBone.head = end_bone.tail
|
|
newBone.tail = end_bone.tail + Vector((0, 0.1, 0))
|
|
else:
|
|
newBone = enduser_obj.pose.bones[ik_bone]
|
|
return newBone
|
|
|
|
|
|
#create the specified NLA setup for base animation, constraints and tweak layer.
|
|
def NLASystemInitialize(enduser_arm, context):
|
|
enduser_obj = context.active_object
|
|
NLATracks = enduser_arm.mocapNLATracks[enduser_obj.data.active_mocap]
|
|
name = NLATracks.name
|
|
anim_data = enduser_obj.animation_data
|
|
s_frame = 0
|
|
if ("Base " + name) in bpy.data.actions:
|
|
mocapAction = bpy.data.actions[("Base " + name)]
|
|
else:
|
|
print("That retargeted anim has no base action")
|
|
anim_data.use_nla = True
|
|
for track in anim_data.nla_tracks:
|
|
anim_data.nla_tracks.remove(track)
|
|
mocapTrack = anim_data.nla_tracks.new()
|
|
mocapTrack.name = "Base " + name
|
|
NLATracks.base_track = mocapTrack.name
|
|
mocapStrip = mocapTrack.strips.new("Base " + name, s_frame, mocapAction)
|
|
constraintTrack = anim_data.nla_tracks.new()
|
|
constraintTrack.name = "Auto fixes " + name
|
|
NLATracks.auto_fix_track = constraintTrack.name
|
|
if ("Auto fixes " + name) in bpy.data.actions:
|
|
constraintAction = bpy.data.actions[("Auto fixes " + name)]
|
|
else:
|
|
constraintAction = bpy.data.actions.new("Auto fixes " + name)
|
|
constraintAction.use_fake_user = True
|
|
constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction)
|
|
constraintStrip.extrapolation = "NOTHING"
|
|
userTrack = anim_data.nla_tracks.new()
|
|
userTrack.name = "Manual fixes " + name
|
|
NLATracks.manual_fix_track = userTrack.name
|
|
if ("Manual fixes " + name) in bpy.data.actions:
|
|
userAction = bpy.data.actions[("Manual fixes " + name)]
|
|
else:
|
|
userAction = bpy.data.actions.new("Manual fixes " + name)
|
|
userAction.use_fake_user = True
|
|
userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction)
|
|
userStrip.extrapolation = "HOLD"
|
|
userStrip.blend_type = "ADD"
|
|
anim_data.nla_tracks.active = constraintTrack
|
|
anim_data.action_extrapolation = "NOTHING"
|
|
#set the stride_bone's action
|
|
if "stride_bone" in bpy.data.objects:
|
|
stride_bone = bpy.data.objects["stride_bone"]
|
|
if NLATracks.stride_action:
|
|
stride_bone.animation_data.action = bpy.data.actions[NLATracks.stride_action]
|
|
else:
|
|
NLATracks.stride_action = stride_bone.animation_data.action.name
|
|
stride_bone.animation_data.action.use_fake_user = True
|
|
anim_data.action = None
|
|
|
|
|
|
def preAdvancedRetargeting(performer_obj, enduser_obj):
|
|
createDictionary(performer_obj.data, enduser_obj.data)
|
|
bones = enduser_obj.pose.bones
|
|
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
|
perf_root = performer_obj.pose.bones[0].name
|
|
for bone in map_bones:
|
|
perf_bone = bone.bone.reverseMap[0].name
|
|
|
|
cons = bone.constraints.new('COPY_ROTATION')
|
|
cons.name = "retargetTemp"
|
|
locks = bone.lock_rotation
|
|
cons.use_x = not locks[0]
|
|
cons.use_y = not locks[1]
|
|
cons.use_z = not locks[2]
|
|
cons.target = performer_obj
|
|
cons.subtarget = perf_bone
|
|
cons.target_space = 'WORLD'
|
|
cons.owner_space = 'WORLD'
|
|
|
|
if (not bone.bone.use_connect) and (perf_bone != perf_root):
|
|
cons = bone.constraints.new('COPY_LOCATION')
|
|
cons.name = "retargetTemp"
|
|
cons.target = performer_obj
|
|
cons.subtarget = perf_bone
|
|
cons.use_x = True
|
|
cons.use_y = True
|
|
cons.use_z = True
|
|
if bone.bone.poseSpace:
|
|
cons.target_space = 'POSE'
|
|
cons.owner_space = 'POSE'
|
|
else:
|
|
cons.target_space = 'LOCAL'
|
|
cons.owner_space = 'LOCAL'
|
|
|
|
def prepareForBake(enduser_obj):
|
|
bones = enduser_obj.pose.bones
|
|
for bone in bones:
|
|
bone.bone.select = False
|
|
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
|
for bone in map_bones:
|
|
for cons in bone.constraints:
|
|
if "retargetTemp" in cons.name:
|
|
bone.bone.select = True
|
|
|
|
|
|
def cleanTempConstraints(enduser_obj):
|
|
bones = enduser_obj.pose.bones
|
|
map_bones = [bone for bone in bones if bone.bone.reverseMap]
|
|
for bone in map_bones:
|
|
for cons in bone.constraints:
|
|
if "retargetTemp" in cons.name:
|
|
bone.constraints.remove(cons)
|
|
|
|
|
|
#Main function that runs the retargeting sequence.
|
|
#If advanced == True, we assume constraint's were already created
|
|
def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
|
|
perf_arm = performer_obj.data
|
|
end_arm = enduser_obj.data
|
|
advanced = end_arm.advancedRetarget
|
|
step = end_arm.frameStep
|
|
enduser_obj.animation_data_create()
|
|
|
|
try:
|
|
enduser_obj.animation_data.action = bpy.data.actions.new("temp")
|
|
enduser_obj.animation_data.action.use_fake_user = True
|
|
except:
|
|
print("no need to create new action")
|
|
|
|
print("creating Dictionary")
|
|
feetBones, root = createDictionary(perf_arm, end_arm)
|
|
print("cleaning stuff up")
|
|
perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj)
|
|
if not advanced:
|
|
turnOffIK(enduser_obj)
|
|
print("Creating intermediate armature (for first pass)")
|
|
inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step)
|
|
print("First pass: retargeting from intermediate to end user")
|
|
retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step)
|
|
else:
|
|
prepareForBake(enduser_obj)
|
|
print("Retargeting pose (Advanced Retarget)")
|
|
bake_action(
|
|
bpy.context.object,
|
|
s_frame, e_frame,
|
|
action=enduser_obj.animation_data.action,
|
|
only_selected=True,
|
|
do_pose=True,
|
|
do_object=False,
|
|
frame_step=step,
|
|
)
|
|
name = performer_obj.animation_data.action.name[:10]
|
|
#We trim the name down to 10 chars because of Action Name length maximum
|
|
enduser_obj.animation_data.action.name = "Base " + name
|
|
print("Second pass: retargeting root translation and clean up")
|
|
stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat)
|
|
if not advanced:
|
|
print("hry")
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
bpy.context.view_layer.objects.active = enduser_obj
|
|
bpy.ops.object.select_pattern(pattern=enduser_obj.name, extend=False)
|
|
IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step)
|
|
bpy.ops.object.select_pattern(pattern=stride_bone.name, extend=False)
|
|
restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame)
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
if not advanced:
|
|
bpy.ops.object.select_pattern(pattern=inter_obj.name, extend=False)
|
|
bpy.ops.object.delete()
|
|
else:
|
|
cleanTempConstraints(enduser_obj)
|
|
bpy.ops.object.select_pattern(pattern=enduser_obj.name, extend=False)
|
|
|
|
if not name in [tracks.name for tracks in end_arm.mocapNLATracks]:
|
|
NLATracks = end_arm.mocapNLATracks.add()
|
|
NLATracks.name = name
|
|
else:
|
|
NLATracks = end_arm.mocapNLATracks[name]
|
|
end_arm.active_mocap = name
|
|
print("retargeting done!")
|
|
|
|
|
|
def isRigAdvanced(enduser_obj):
|
|
bones = enduser_obj.pose.bones
|
|
for bone in bones:
|
|
for constraint in bone.constraints:
|
|
if constraint.type != "IK":
|
|
return True
|
|
if enduser_obj.data.animation_data:
|
|
if enduser_obj.data.animation_data.drivers:
|
|
return True
|