[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [53369] trunk/blender/release/scripts: Fix [#33424] Inadequate bake action.

Bastien Montagne montagne29 at wanadoo.fr
Fri Dec 28 14:34:26 CET 2012


Revision: 53369
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53369
Author:   mont29
Date:     2012-12-28 13:34:19 +0000 (Fri, 28 Dec 2012)
Log Message:
-----------
Fix [#33424] Inadequate bake action.

bake_action tries to make kind of a 'visual keying'... On one side, this is rather stupid when you keep constraints (in this case, keying actual loc/rot/scale transforms, i.e. matrix_basis, is enough, doing more would lead to unexpected behavior with some constraints). On the other one, making a good visual keying of bones is *really* tricky, so now using the new object's convert_space() func to compute that (when the user chooses to remove the constraints).

Incidentally, this greatly simplifies the code of bake_action!

Modified Paths:
--------------
    trunk/blender/release/scripts/modules/bpy_extras/anim_utils.py
    trunk/blender/release/scripts/startup/bl_operators/anim.py

Modified: trunk/blender/release/scripts/modules/bpy_extras/anim_utils.py
===================================================================
--- trunk/blender/release/scripts/modules/bpy_extras/anim_utils.py	2012-12-28 13:21:57 UTC (rev 53368)
+++ trunk/blender/release/scripts/modules/bpy_extras/anim_utils.py	2012-12-28 13:34:19 UTC (rev 53369)
@@ -16,7 +16,7 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-# <pep8-80 compliant>
+# <pep8 compliant>
 
 __all__ = (
     "bake_action",
@@ -52,7 +52,7 @@
     :type do_pose: bool
     :arg do_object: Bake objects.
     :type do_object: bool
-    :arg do_constraint_clear: Remove constraints.
+    :arg do_constraint_clear: Remove constraints (and do 'visual keying').
     :type do_constraint_clear: bool
     :arg do_clean: Remove redundant keyframes after baking.
     :type do_clean: bool
@@ -65,96 +65,52 @@
     """
 
     # -------------------------------------------------------------------------
-    # Helper Functions
+    # Helper Functions and vars
 
-    def pose_frame_info(obj):
-        from mathutils import Matrix
+    def pose_frame_info(obj, do_visual_keying):
+        matrix = {}
+        for name, pbone in obj.pose.bones.items():
+            if do_visual_keying:
+                # Get the final transform of the bone in its own local space...
+                matrix[name] = obj.convert_space(pbone, pbone.matrix, 'POSE', 'LOCAL')
+            else:
+                matrix[name] = pbone.matrix_basis.copy()
+        return matrix
 
-        info = {}
+    def obj_frame_info(obj, do_visual_keying):
+        return obj.matrix_local.copy() if do_visual_keying else obj.matrix_basis.copy()
 
-        pose = obj.pose
-
-        pose_items = pose.bones.items()
-
-        for name, pbone in pose_items:
-            binfo = {}
-            bone = pbone.bone
-
-            binfo["parent"] = getattr(bone.parent, "name", None)
-            binfo["bone"] = bone
-            binfo["pbone"] = pbone
-            binfo["matrix_local"] = bone.matrix_local.copy()
-            try:
-                binfo["matrix_local_inv"] = binfo["matrix_local"].inverted()
-            except:
-                binfo["matrix_local_inv"] = Matrix()
-
-            binfo["matrix"] = bone.matrix.copy()
-            binfo["matrix_pose"] = pbone.matrix.copy()
-            try:
-                binfo["matrix_pose_inv"] = binfo["matrix_pose"].inverted()
-            except:
-                binfo["matrix_pose_inv"] = Matrix()
-
-            info[name] = binfo
-
-        for name, pbone in pose_items:
-            binfo = info[name]
-            binfo_parent = binfo.get("parent", None)
-            if binfo_parent:
-                binfo_parent = info[binfo_parent]
-
-            matrix = binfo["matrix_pose"]
-            rest_matrix = binfo["matrix_local"]
-
-            if binfo_parent:
-                matrix = binfo_parent["matrix_pose_inv"] * matrix
-                rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix
-
-            binfo["matrix_key"] = rest_matrix.inverted() * matrix
-
-        return info
-
-    def obj_frame_info(obj):
-        info = {}
-        # parent = obj.parent
-        info["matrix_key"] = obj.matrix_local.copy()
-        return info
-
     # -------------------------------------------------------------------------
     # Setup the Context
 
     # TODO, pass data rather then grabbing from the context!
     scene = bpy.context.scene
     obj = bpy.context.object
-    pose = obj.pose
     frame_back = scene.frame_current
 
-    if pose is None:
+    if obj.pose is None:
         do_pose = False
 
-    if do_pose is None and do_object is None:
+    if not (do_pose or do_object):
         return None
 
     pose_info = []
     obj_info = []
 
+    options = {'INSERTKEY_NEEDED'}
+
     frame_range = range(frame_start, frame_end + 1, frame_step)
 
     # -------------------------------------------------------------------------
     # Collect transformations
 
-    # could speed this up by applying steps here too...
     for f in frame_range:
         scene.frame_set(f)
-
         if do_pose:
-            pose_info.append(pose_frame_info(obj))
+            pose_info.append(pose_frame_info(obj, do_constraint_clear))
         if do_object:
-            obj_info.append(obj_frame_info(obj))
+            obj_info.append(obj_frame_info(obj, do_constraint_clear))
 
-        f += 1
-
     # -------------------------------------------------------------------------
     # Create action
 
@@ -164,58 +120,45 @@
         action = bpy.data.actions.new("Action")
     atd.action = action
 
-    if do_pose:
-        pose_items = pose.bones.items()
-    else:
-        pose_items = []  # skip
-
     # -------------------------------------------------------------------------
     # Apply transformations to action
 
     # pose
-    for name, pbone in (pose_items if do_pose else ()):
-        if only_selected and not pbone.bone.select:
-            continue
+    if do_pose:
+        for name, pbone in obj.pose.bones.items():
+            if only_selected and not pbone.bone.select:
+                continue
 
-        if do_constraint_clear:
-            while pbone.constraints:
-                pbone.constraints.remove(pbone.constraints[0])
+            if do_constraint_clear:
+                while pbone.constraints:
+                    pbone.constraints.remove(pbone.constraints[0])
 
-        # create compatible eulers
-        euler_prev = None
+            # create compatible eulers
+            euler_prev = None
 
-        for f in frame_range:
-            f_step = (f - frame_start) // frame_step
-            matrix = pose_info[f_step][name]["matrix_key"]
+            for (f, matrix) in zip(frame_range, pose_info):
+                pbone.matrix_basis = matrix[name].copy()
 
-            # pbone.location = matrix.to_translation()
-            # pbone.rotation_quaternion = matrix.to_quaternion()
-            pbone.matrix_basis = matrix
+                pbone.keyframe_insert("location", -1, f, name, options)
 
-            pbone.keyframe_insert("location", -1, f, name)
+                rotation_mode = pbone.rotation_mode
+                if rotation_mode == 'QUATERNION':
+                    pbone.keyframe_insert("rotation_quaternion", -1, f, name, options)
+                elif rotation_mode == 'AXIS_ANGLE':
+                    pbone.keyframe_insert("rotation_axis_angle", -1, f, name, options)
+                else:  # euler, XYZ, ZXY etc
+                    if euler_prev is not None:
+                        euler = pbone.rotation_euler.copy()
+                        euler.make_compatible(euler_prev)
+                        pbone.rotation_euler = euler
+                        euler_prev = euler
+                        del euler
+                    else:
+                        euler_prev = pbone.rotation_euler.copy()
+                    pbone.keyframe_insert("rotation_euler", -1, f, name, options)
 
-            rotation_mode = pbone.rotation_mode
+                pbone.keyframe_insert("scale", -1, f, name, options)
 
-            if rotation_mode == 'QUATERNION':
-                pbone.keyframe_insert("rotation_quaternion", -1, f, name)
-            elif rotation_mode == 'AXIS_ANGLE':
-                pbone.keyframe_insert("rotation_axis_angle", -1, f, name)
-            else:  # euler, XYZ, ZXY etc
-
-                if euler_prev is not None:
-                    euler = pbone.rotation_euler.copy()
-                    euler.make_compatible(euler_prev)
-                    pbone.rotation_euler = euler
-                    euler_prev = euler
-                    del euler
-
-                pbone.keyframe_insert("rotation_euler", -1, f, name)
-
-                if euler_prev is None:
-                    euler_prev = pbone.rotation_euler.copy()
-
-            pbone.keyframe_insert("scale", -1, f, name)
-
     # object. TODO. multiple objects
     if do_object:
         if do_constraint_clear:
@@ -225,18 +168,16 @@
         # create compatible eulers
         euler_prev = None
 
-        for f in frame_range:
-            matrix = obj_info[(f - frame_start) // frame_step]["matrix_key"]
-            obj.matrix_local = matrix
+        for (f, matrix) in zip(frame_range, obj_info):
+            obj.matrix_basis = matrix[name]
 
-            obj.keyframe_insert("location", -1, f)
+            obj.keyframe_insert("location", -1, f, options)
 
             rotation_mode = obj.rotation_mode
-
             if rotation_mode == 'QUATERNION':
-                obj.keyframe_insert("rotation_quaternion", -1, f)
+                obj.keyframe_insert("rotation_quaternion", -1, f, options)
             elif rotation_mode == 'AXIS_ANGLE':
-                obj.keyframe_insert("rotation_axis_angle", -1, f)
+                obj.keyframe_insert("rotation_axis_angle", -1, f, options)
             else:  # euler, XYZ, ZXY etc
                 if euler_prev is not None:
                     euler = obj.rotation_euler.copy()
@@ -244,16 +185,12 @@
                     obj.rotation_euler = euler
                     euler_prev = euler
                     del euler
-
-                obj.keyframe_insert("rotation_euler", -1, f)
-
-                if euler_prev is None:
+                else:
                     euler_prev = obj.rotation_euler.copy()
+                obj.keyframe_insert("rotation_euler", -1, f, options)
 
-            obj.keyframe_insert("scale", -1, f)
+            obj.keyframe_insert("scale", -1, f, options)
 
-    scene.frame_set(frame_back)
-
     # -------------------------------------------------------------------------
     # Clean
 
@@ -271,4 +208,6 @@
                 else:
                     i += 1
 
+    scene.frame_set(frame_back)
+
     return action

Modified: trunk/blender/release/scripts/startup/bl_operators/anim.py
===================================================================
--- trunk/blender/release/scripts/startup/bl_operators/anim.py	2012-12-28 13:21:57 UTC (rev 53368)
+++ trunk/blender/release/scripts/startup/bl_operators/anim.py	2012-12-28 13:34:19 UTC (rev 53369)
@@ -187,17 +187,20 @@
             )
     only_selected = BoolProperty(
             name="Only Selected",
+            description="Only key selected object/bones",
             default=True,
             )
     clear_constraints = BoolProperty(
             name="Clear Constraints",
+            description="Remove all constraints from keyed object/bones, and do 'visual' keying",
             default=False,

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list