[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [39309] branches/soc-2011-pepper/release/ scripts: Additional work on animation stitching, now with auto-guess capability.

Benjy Cook benjycook at hotmail.com
Thu Aug 11 18:46:27 CEST 2011


Revision: 39309
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=39309
Author:   benjycook
Date:     2011-08-11 16:46:27 +0000 (Thu, 11 Aug 2011)
Log Message:
-----------
Additional work on animation stitching, now with auto-guess capability. Only a few bugs left, regarding animations translation

Modified Paths:
--------------
    branches/soc-2011-pepper/release/scripts/modules/mocap_tools.py
    branches/soc-2011-pepper/release/scripts/modules/retarget.py
    branches/soc-2011-pepper/release/scripts/startup/ui_mocap.py

Modified: branches/soc-2011-pepper/release/scripts/modules/mocap_tools.py
===================================================================
--- branches/soc-2011-pepper/release/scripts/modules/mocap_tools.py	2011-08-11 16:43:36 UTC (rev 39308)
+++ branches/soc-2011-pepper/release/scripts/modules/mocap_tools.py	2011-08-11 16:46:27 UTC (rev 39309)
@@ -105,64 +105,75 @@
         self.u = u
 
 
-def autoloop_anim():
-    context = bpy.context
-    obj = context.active_object
-    fcurves = [x for x in obj.animation_data.action.fcurves if x.select]
+def crossCorrelationMatch(curvesA, curvesB, margin):
+    dataA = []
+    dataB = []
+    end = len(curvesA[0].keyframe_points)
 
-    data = []
-    end = len(fcurves[0].keyframe_points)
-
     for i in range(1, end):
         vec = []
-        for fcurve in fcurves:
+        for fcurve in curvesA:
             vec.append(fcurve.evaluate(i))
-        data.append(NdVector(vec))
+        dataA.append(NdVector(vec))
+        vec = []
+        for fcurve in curvesB:
+            vec.append(fcurve.evaluate(i))
+        dataB.append(NdVector(vec))
 
     def comp(a, b):
         return a * b
 
-    N = len(data)
+    N = len(dataA)
     Rxy = [0.0] * N
     for i in range(N):
         for j in range(i, min(i + N, N)):
-            Rxy[i] += comp(data[j], data[j - i])
+            Rxy[i] += comp(dataA[j], dataB[j - i])
         for j in range(i):
-            Rxy[i] += comp(data[j], data[j - i + N])
+            Rxy[i] += comp(dataA[j], dataB[j - i + N])
         Rxy[i] /= float(N)
-
     def bestLocalMaximum(Rxy):
         Rxyd = [Rxy[i] - Rxy[i - 1] for i in range(1, len(Rxy))]
         maxs = []
         for i in range(1, len(Rxyd) - 1):
             a = Rxyd[i - 1]
             b = Rxyd[i]
-            print(a, b)
             #sign change (zerocrossing) at point i, denoting max point (only)
             if (a >= 0 and b < 0) or (a < 0 and b >= 0):
                 maxs.append((i, max(Rxy[i], Rxy[i - 1])))
-        return max(maxs, key=lambda x: x[1])[0]
-    flm = bestLocalMaximum(Rxy[0:int(len(Rxy))])
+        return [x[0] for x in maxs]
+        #~ return max(maxs, key=lambda x: x[1])[0]
+        
+    flms = bestLocalMaximum(Rxy[0:int(len(Rxy))])
+    ss = []
+    for flm in flms:
+        diff = []
 
-    diff = []
+        for i in range(len(dataA) - flm):
+            diff.append((dataA[i] - dataB[i + flm]).lengthSq)
 
-    for i in range(len(data) - flm):
-        diff.append((data[i] - data[i + flm]).lengthSq)
+        def lowerErrorSlice(diff, e):
+            #index, error at index
+            bestSlice = (0, 100000)
+            for i in range(e, len(diff) - e):
+                errorSlice = sum(diff[i - e:i + e + 1])
+                if errorSlice < bestSlice[1]:
+                    bestSlice = (i, errorSlice, flm)
+            return bestSlice
+            
+        s = lowerErrorSlice(diff, margin)
+        ss.append(s)
 
-    def lowerErrorSlice(diff, e):
-        #index, error at index
-        bestSlice = (0, 100000)
-        for i in range(e, len(diff) - e):
-            errorSlice = sum(diff[i - e:i + e + 1])
-            if errorSlice < bestSlice[1]:
-                bestSlice = (i, errorSlice)
-        return bestSlice[0]
+    ss.sort(key = lambda x: x[1])
+    return ss[0][2], ss[0][0], dataA
 
-    margin = 2
+def autoloop_anim():
+    context = bpy.context
+    obj = context.active_object
+    fcurves = [x for x in obj.animation_data.action.fcurves if x.select]
 
-    s = lowerErrorSlice(diff, margin)
+    margin = 10
 
-    print(flm, s)
+    flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin)
     loop = data[s:s + flm + margin]
 
     #find *all* loops, s:s+flm, s+flm:s+2flm, etc...
@@ -824,3 +835,18 @@
                     pt.handle_left.y-=offset[i]
                     pt.handle_right.y-=offset[i]
 
+
+def guess_anim_stitch(context, enduser_obj):
+    stitch_settings = enduser_obj.data.stitch_settings
+    action_1 = stitch_settings.first_action
+    action_2 = stitch_settings.second_action
+    TrackNamesA = enduser_obj.data.mocapNLATracks[action_1]
+    TrackNamesB = enduser_obj.data.mocapNLATracks[action_2]
+    mocapA = bpy.data.actions[TrackNamesA.base_track]
+    mocapB = bpy.data.actions[TrackNamesB.base_track]
+    curvesA = mocapA.fcurves
+    curvesB = mocapB.fcurves
+    flm, s, data = crossCorrelationMatch(curvesA, curvesB, 10)
+    print(flm,s)
+    enduser_obj.data.stitch_settings.blend_frame = flm
+    enduser_obj.data.stitch_settings.second_offset = s
\ No newline at end of file

Modified: branches/soc-2011-pepper/release/scripts/modules/retarget.py
===================================================================
--- branches/soc-2011-pepper/release/scripts/modules/retarget.py	2011-08-11 16:43:36 UTC (rev 39308)
+++ branches/soc-2011-pepper/release/scripts/modules/retarget.py	2011-08-11 16:46:27 UTC (rev 39309)
@@ -305,6 +305,7 @@
 
 
 def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene):
+    bpy.ops.object.select_name(name=enduser_obj.name, extend=False)
     end_bones = enduser_obj.pose.bones
     for pose_bone in end_bones:
         ik_constraint = hasIKConstraint(pose_bone)
@@ -313,9 +314,12 @@
             # 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 = orgLocTrg
+                ik_constraint.target = enduser_obj
+                ik_constraint.subtarget = pose_bone.name+"IK"
                 target = orgLocTrg
 
             # There is a target now
@@ -337,6 +341,7 @@
                 target.keyframe_insert("location")
             ik_constraint.mute = False
     scene.frame_set(s_frame)
+    bpy.ops.object.mode_set(mode='OBJECT')
 
 
 def turnOffIK(enduser_obj):
@@ -379,14 +384,17 @@
 
 #create (or return if exists) the related IK empty to the bone
 def originalLocationTarget(end_bone, enduser_obj):
-    if not end_bone.name + "Org" in bpy.data.objects:
-        bpy.ops.object.add()
-        empty = bpy.context.active_object
-        empty.name = end_bone.name + "Org"
-        empty.empty_draw_size = 0.1
-        empty.parent = enduser_obj
-    empty = bpy.data.objects[end_bone.name + "Org"]
-    return empty
+    if not end_bone.name + "IK" in enduser_obj.data.bones:
+        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))
+        #~ empty = bpy.context.active_object
+        #~ empty.name = end_bone.name + "Org"
+        #~ empty.empty_draw_size = 0.1
+        #~ empty.parent = enduser_obj
+    else:
+        newBone = enduser_obj.pose.bones[end_bone.name + "IK"]
+    return newBone
 
 
 #create the specified NLA setup for base animation, constraints and tweak layer.
@@ -530,6 +538,7 @@
     stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat)
     if not advanced:
         IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene)
+        bpy.ops.object.select_name(name=stride_bone.name, extend=False)
     restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone)
     bpy.ops.object.mode_set(mode='OBJECT')
     if not advanced:

Modified: branches/soc-2011-pepper/release/scripts/startup/ui_mocap.py
===================================================================
--- branches/soc-2011-pepper/release/scripts/startup/ui_mocap.py	2011-08-11 16:43:36 UTC (rev 39308)
+++ branches/soc-2011-pepper/release/scripts/startup/ui_mocap.py	2011-08-11 16:46:27 UTC (rev 39309)
@@ -382,6 +382,7 @@
             layout.prop(settings, "blend_amount")
             layout.prop(settings, "second_offset")
             layout.prop_search(settings, "stick_bone", context.active_object.pose, "bones")
+            layout.operator('mocap.animstitchguess', text="Guess Settings")
             layout.operator('mocap.animstitch', text="Stitch Animations")
 
 
@@ -765,6 +766,25 @@
         return False
     
 
+class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator):
+    '''Guesses the stitch frame and second offset for animation stitch'''
+    bl_idname = "mocap.animstitchguess"
+    bl_label = "Guesses the stitch frame and second offset for animation stitch"
+
+    def execute(self, context):
+        mocap_tools.guess_anim_stitch(context, context.active_object)
+        return {"FINISHED"}
+
+    @classmethod
+    def poll(cls, context):
+        activeIsArmature = False
+        if context.active_object:
+            activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
+            if activeIsArmature:
+                stitch_settings = context.active_object.data.stitch_settings
+                return (stitch_settings.first_action and stitch_settings.second_action)
+        return False
+
 def register():
     bpy.utils.register_module(__name__)
 




More information about the Bf-blender-cvs mailing list