Changed name of Mocap constraints to mocap fixes, for user clarity.
[blender.git] / release / scripts / modules / retarget.py
index f8d424fbb5a579a2189129774bbd93f370128c14..bec7b8aaa3e532bee3460bb265d64c89549a41ea 100644 (file)
@@ -21,6 +21,7 @@
 import bpy
 from mathutils import *
 from math import radians, acos
+import cProfile
 
 
 def hasIKConstraint(pose_bone):
@@ -44,6 +45,7 @@ def createDictionary(perf_arm, end_arm):
             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
@@ -105,10 +107,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene
             else:
                 perf_bone = performer_bones[perf_bone_name[0].name]
                 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
     inter_obj = enduser_obj.copy()
@@ -117,7 +116,20 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene
     inter_obj.name = "intermediate"
     bpy.context.scene.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
+    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
     #resets roll
+    print("retargeting to intermediate")
     bpy.ops.armature.calculate_roll(type='Z')
     bpy.ops.object.mode_set(mode="OBJECT")
     inter_obj.data.name = "inter_arm"
@@ -126,12 +138,17 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene
     inter_bones = inter_obj.pose.bones
     #clears inheritance
     for inter_bone in inter_bones:
-        inter_bone.bone.use_inherit_rotation = False
+        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):
+        if (t - s_frame) % 10 == 0:
+            print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame))
         scene.frame_set(t)
-        inter_bone = inter_bones[root]
-        retargetPerfToInter(inter_bone)
+        for bone in inter_bones:
+            retargetPerfToInter(bone)
 
     return inter_obj
 
@@ -156,7 +173,10 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
         rest_matrix = trg_bone.bone.matrix_local
 
         if trg_bone.parent and trg_bone.bone.use_inherit_rotation:
-            parent_mat = src_bone.parent.matrix
+            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.copy()
             parent_rest_inv.invert()
@@ -168,7 +188,7 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
         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.matrix_basis = bake_matrix
         rot_mode = end_bone.rotation_mode
         if rot_mode == "QUATERNION":
             end_bone.keyframe_insert("rotation_quaternion")
@@ -176,11 +196,15 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
             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):
+        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))
@@ -197,7 +221,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
     perf_bones = performer_obj.pose.bones
     end_bones = enduser_obj.pose.bones
 
-    perfRoot = end_bones[root].bone.reverseMap[0].name
+    perfRoot = perf_bones[0].name
     endFeet = [perf_bones[perfBone].bone.map for perfBone in perfFeet]
     locDictKeys = perfFeet + endFeet + [perfRoot]
 
@@ -360,10 +384,16 @@ def NLASystemInitialize(enduser_obj, s_frame):
     mocapTrack.name = "Base Mocap Track"
     mocapStrip = mocapTrack.strips.new("Base Mocap", s_frame, mocapAction)
     constraintTrack = anim_data.nla_tracks.new()
-    constraintTrack.name = "Mocap constraints"
-    constraintAction = bpy.data.actions.new("Mocap constraints")
-    constraintStrip = constraintTrack.strips.new("Mocap constraints", s_frame, constraintAction)
+    constraintTrack.name = "Mocap fixes"
+    constraintAction = bpy.data.actions.new("Mocap fixes")
+    constraintStrip = constraintTrack.strips.new("Mocap fixes", s_frame, constraintAction)
     constraintStrip.extrapolation = "NOTHING"
+    userTrack = anim_data.nla_tracks.new()
+    userTrack.name = "Mocap manual fix"
+    userAction = bpy.data.actions.new("Mocap manual fix")
+    userStrip = userTrack.strips.new("Mocap manual fix", s_frame, userAction)
+    userStrip.extrapolation = "HOLD"
+    #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon
     anim_data.nla_tracks.active = constraintTrack
     anim_data.action = constraintAction
     anim_data.action_extrapolation = "NOTHING"
@@ -373,11 +403,16 @@ def NLASystemInitialize(enduser_obj, s_frame):
 def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
     perf_arm = performer_obj.data
     end_arm = enduser_obj.data
+    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)
     turnOffIK(enduser_obj)
+    print("Creating intermediate armature (for first pass)")
     inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene)
+    print("First pass: retargeting from intermediate to end user")
     retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene)
+    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)
     IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene)
     restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone)
@@ -385,7 +420,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
     bpy.ops.object.select_name(name=inter_obj.name, extend=False)
     bpy.ops.object.delete()
     NLASystemInitialize(enduser_obj, s_frame)
-
+    print("retargeting done!")
 
 if __name__ == "__main__":
     totalRetarget()