Advanced Retargeting option: If the end user armature is complex, on the level of...
[blender.git] / release / scripts / modules / retarget.py
index a0d89ae6e8b9299a232cca47a9b21031ceb58829..88a5c3a620a297720d8ce0b52dff6526acb8bd28 100644 (file)
@@ -21,6 +21,7 @@
 import bpy
 from mathutils import *
 from math import radians, acos
+from bl_operators import nla
 import cProfile
 
 
@@ -264,6 +265,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
             v = locDeriv[key][i]
             hipV = locDeriv[perfRoot][i]
             endV = locDeriv[perf_bones[key].bone.map][i]
+            print(v.length,)
             if (v.length < 0.1):
                 #this is a plant frame.
                 #lets see what the original hip delta is, and the corresponding
@@ -275,6 +277,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
     #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:
@@ -283,6 +286,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
         stride_bone.name = "stride_bone"
     print(stride_bone)
     stride_bone.location = Vector((0, 0, 0))
+    print(linearAvg)
     if linearAvg:
         #determine the average change in scale needed
         avg = sum(linearAvg) / len(linearAvg)
@@ -294,6 +298,8 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
             newTranslation = (tailLoc(perf_bones[perfRoot]) / avg)
             stride_bone.location = enduser_obj_mat * (newTranslation - initialPos)
             stride_bone.keyframe_insert("location")
+    else:
+        stride_bone.keyframe_insert("location")
     stride_bone.animation_data.action.name = ("Stride Bone " + action_name)
 
     return stride_bone
@@ -410,6 +416,7 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name):
         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()
@@ -419,6 +426,7 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name):
         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 = "MULITPLY" - doesn't work due to work, will be activated soon
@@ -432,40 +440,104 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name):
             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]
+    for bone in map_bones:
+        perf_bone = bone.bone.reverseMap[0].name
+        addLocalRot = False;
+        if bone.bone.use_connect or not bone.constraints:
+            locks = bone.lock_location
+            if not (locks[0] or locks[1] or locks[2]):  
+                cons = bone.constraints.new('COPY_LOCATION')
+                cons.name = "retargetTemp"
+                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
+                addLocalRot = True
+
+       
+        cons2 = bone.constraints.new('COPY_ROTATION')
+        cons2.name = "retargetTemp"
+        locks = bone.lock_rotation
+        cons2.use_x = not locks[0]
+        cons2.use_y = not locks[1]
+        cons2.use_z = not locks[2]
+        cons2.target = performer_obj
+        cons2.subtarget = perf_bone
+
+        if addLocalRot:
+            for constraint in bone.constraints:
+                if constraint.type == 'COPY_ROTATION':
+                    constraint.target_space = 'LOCAL'
+                    constraint.owner_space = 'LOCAL_WITH_PARENT'
+
+
+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
     
     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)
-    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)
+    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)
+        print("First pass: retargeting from intermediate to end user")
+        retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene)
+    else:
+        prepareForBake(enduser_obj)
+        print("Retargeting pose (Advanced Retarget)")
+        nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False)
     name = performer_obj.animation_data.action.name
     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)
-    IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene)
+    if not advanced:
+        IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene)
     restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone)
     bpy.ops.object.mode_set(mode='OBJECT')
-    bpy.ops.object.select_name(name=inter_obj.name, extend=False)
-    bpy.ops.object.delete()
+    if not advanced:
+        bpy.ops.object.select_name(name=inter_obj.name, extend=False)
+        bpy.ops.object.delete()
+    else:
+        cleanTempConstraints(enduser_obj)
     bpy.ops.object.select_name(name=enduser_obj.name, extend=False)
 
     if not name in [tracks.name for tracks in end_arm.mocapNLATracks]: