diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2012-12-28 17:34:19 +0400 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2012-12-28 17:34:19 +0400 |
commit | c587c985e81c8fdd4ab47470d0f2d2d4a0773647 (patch) | |
tree | e94b109344787d32c0378347a7ff7f2e41d2c0de /release | |
parent | 921750149bdd303ad7d93922d4ce739867c1013c (diff) |
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!
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/bpy_extras/anim_utils.py | 189 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/anim.py | 19 |
2 files changed, 75 insertions, 133 deletions
diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py index b8d08628de4..7a5d1692971 100644 --- a/release/scripts/modules/bpy_extras/anim_utils.py +++ b/release/scripts/modules/bpy_extras/anim_utils.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8-80 compliant> +# <pep8 compliant> __all__ = ( "bake_action", @@ -52,7 +52,7 @@ def bake_action(frame_start, :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,61 +65,20 @@ def bake_action(frame_start, """ # ------------------------------------------------------------------------- - # 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 = {} - - 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 + def obj_frame_info(obj, do_visual_keying): + return obj.matrix_local.copy() if do_visual_keying else obj.matrix_basis.copy() # ------------------------------------------------------------------------- # Setup the Context @@ -127,33 +86,30 @@ def bake_action(frame_start, # 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)) - - f += 1 + obj_info.append(obj_frame_info(obj, do_constraint_clear)) # ------------------------------------------------------------------------- # Create action @@ -164,57 +120,44 @@ def bake_action(frame_start, 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_constraint_clear: - while pbone.constraints: - pbone.constraints.remove(pbone.constraints[0]) - - # 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"] - - # pbone.location = matrix.to_translation() - # pbone.rotation_quaternion = matrix.to_quaternion() - pbone.matrix_basis = matrix - - pbone.keyframe_insert("location", -1, f, name) - - rotation_mode = pbone.rotation_mode - - 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) + 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]) + + # create compatible eulers + euler_prev = None + + for (f, matrix) in zip(frame_range, pose_info): + pbone.matrix_basis = matrix[name].copy() + + pbone.keyframe_insert("location", -1, f, name, options) + + 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) + + pbone.keyframe_insert("scale", -1, f, name, options) # object. TODO. multiple objects if do_object: @@ -225,18 +168,16 @@ def bake_action(frame_start, # 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,15 +185,11 @@ def bake_action(frame_start, 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) - - scene.frame_set(frame_back) + obj.keyframe_insert("scale", -1, f, options) # ------------------------------------------------------------------------- # Clean @@ -271,4 +208,6 @@ def bake_action(frame_start, else: i += 1 + scene.frame_set(frame_back) + return action diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 902c7007fb9..e34e9a981a6 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -187,17 +187,20 @@ class BakeAction(Operator): ) 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, ) bake_types = EnumProperty( name="Bake Data", + description="Which data's transformations to bake", options={'ENUM_FLAG'}, - items=(('POSE', "Pose", ""), - ('OBJECT', "Object", ""), + items=(('POSE', "Pose", "Bake bones transformations"), + ('OBJECT', "Object", "Bake object transformations"), ), default={'POSE'}, ) @@ -208,12 +211,12 @@ class BakeAction(Operator): action = anim_utils.bake_action(self.frame_start, self.frame_end, - self.step, - self.only_selected, - 'POSE' in self.bake_types, - 'OBJECT' in self.bake_types, - self.clear_constraints, - True, + frame_step=self.step, + only_selected=self.only_selected, + do_pose='POSE' in self.bake_types, + do_object='OBJECT' in self.bake_types, + do_constraint_clear=self.clear_constraints, + do_clean=True, ) if action is None: |