forked from bartvdbraak/blender
Two new operators for easier retargeting: Auto scale performer, and a first attempt at auto hiearchy mapping
This commit is contained in:
parent
2c61949179
commit
a08a510d65
@ -573,7 +573,67 @@ def scale_fix_armature(performer_obj, enduser_obj):
|
||||
perf_bones = performer_obj.data.bones
|
||||
end_bones = enduser_obj.data.bones
|
||||
|
||||
#perf_avg = performer_obj.dimensions
|
||||
def calculateBoundingRadius(bones):
|
||||
center = Vector()
|
||||
for bone in bones:
|
||||
center += bone.head_local
|
||||
center /= len(bones)
|
||||
radius = 0
|
||||
for bone in bones:
|
||||
dist = (bone.head_local - center).length
|
||||
if dist > radius:
|
||||
radius = dist
|
||||
return radius
|
||||
|
||||
perf_rad = calculateBoundingRadius(performer_obj.data.bones)
|
||||
end_rad = calculateBoundingRadius(enduser_obj.data.bones)
|
||||
#end_avg = enduser_obj.dimensions
|
||||
#print(perf_avg, end_avg)
|
||||
#performer_obj.scale /= (perf_avg / end_avg)
|
||||
factor = end_rad / perf_rad * 1.2
|
||||
performer_obj.scale *= factor
|
||||
|
||||
|
||||
def guessMapping(performer_obj, enduser_obj):
|
||||
perf_bones = performer_obj.data.bones
|
||||
end_bones = enduser_obj.data.bones
|
||||
|
||||
root = perf_bones[0]
|
||||
|
||||
def findBoneSide(bone):
|
||||
if "Left" in bone:
|
||||
return "Left", bone.replace("Left", "").lower().replace(".", "")
|
||||
if "Right" in bone:
|
||||
return "Right", bone.replace("Right", "").lower().replace(".", "")
|
||||
if "L" in bone:
|
||||
return "Left", bone.replace("Left", "").lower().replace(".", "")
|
||||
if "R" in bone:
|
||||
return "Right", bone.replace("Right", "").lower().replace(".", "")
|
||||
return "", bone
|
||||
|
||||
def nameMatch(bone_a, bone_b):
|
||||
# nameMatch - recieves two strings, returns 2 if they are relatively the same, 1 if they are the same but R and L and 0 if no match at all
|
||||
side_a, noside_a = findBoneSide(bone_a)
|
||||
side_b, noside_b = findBoneSide(bone_b)
|
||||
if side_a == side_b:
|
||||
if noside_a in noside_b or noside_b in noside_a:
|
||||
return 2
|
||||
else:
|
||||
if noside_a in noside_b or noside_b in noside_a:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def guessSingleMapping(perf_bone):
|
||||
possible_bones = [end_bones[0]]
|
||||
while possible_bones:
|
||||
for end_bone in possible_bones:
|
||||
match = nameMatch(perf_bone.name, end_bone.name)
|
||||
if match == 2 and not perf_bone.map:
|
||||
perf_bone.map = end_bone.name
|
||||
newPossibleBones = []
|
||||
for end_bone in possible_bones:
|
||||
newPossibleBones += list(end_bone.children)
|
||||
possible_bones = newPossibleBones
|
||||
|
||||
for child in perf_bone.children:
|
||||
guessSingleMapping(child)
|
||||
|
||||
guessSingleMapping(root)
|
||||
|
@ -143,7 +143,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene
|
||||
|
||||
for t in range(s_frame, e_frame):
|
||||
if (t - s_frame) % 10 == 0:
|
||||
print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
||||
print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
||||
scene.frame_set(t)
|
||||
for bone in inter_bones:
|
||||
retargetPerfToInter(bone)
|
||||
@ -202,7 +202,7 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
|
||||
|
||||
for t in range(s_frame, e_frame):
|
||||
if (t - s_frame) % 10 == 0:
|
||||
print("Second pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
|
||||
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))
|
||||
|
@ -196,16 +196,17 @@ class MocapPanel(bpy.types.Panel):
|
||||
row2.operator("mocap.looper", text='Loop animation')
|
||||
row2.operator("mocap.limitdof", text='Constrain Rig')
|
||||
self.layout.label("Retargeting")
|
||||
row3 = self.layout.row(align=True)
|
||||
column1 = row3.column(align=True)
|
||||
column1.label("Performer Rig")
|
||||
column2 = row3.column(align=True)
|
||||
column2.label("Enduser Rig")
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj]
|
||||
if enduser_obj is None or len(performer_obj) != 1:
|
||||
self.layout.label("Select performer rig and target rig (as active)")
|
||||
else:
|
||||
self.layout.operator("mocap.guessmapping", text="Guess Hiearchy Mapping")
|
||||
row3 = self.layout.row(align=True)
|
||||
column1 = row3.column(align=True)
|
||||
column1.label("Performer Rig")
|
||||
column2 = row3.column(align=True)
|
||||
column2.label("Enduser Rig")
|
||||
performer_obj = performer_obj[0]
|
||||
if performer_obj.data and enduser_obj.data:
|
||||
if performer_obj.data.name in bpy.data.armatures and enduser_obj.data.name in bpy.data.armatures:
|
||||
@ -532,6 +533,28 @@ class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator):
|
||||
return isinstance(context.active_object.data, bpy.types.Armature)
|
||||
|
||||
|
||||
class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator):
|
||||
'''Attemps to auto figure out hierarchy mapping'''
|
||||
bl_idname = "mocap.guessmapping"
|
||||
bl_label = "Attemps to auto figure out hierarchy mapping"
|
||||
|
||||
def execute(self, context):
|
||||
enduser_obj = bpy.context.active_object
|
||||
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
|
||||
mocap_tools.guessMapping(performer_obj, enduser_obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
|
||||
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
|
||||
if performer_obj:
|
||||
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user