# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # import bpy def pose_info(): from mathutils import Matrix info = {} obj = bpy.context.object 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"].copy().invert() 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"].copy().invert() except: binfo["matrix_pose_inv"] = Matrix() print(binfo["matrix_pose"]) 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 matrix = rest_matrix.copy().invert() * matrix binfo["matrix_key"] = matrix.copy() return info def bake(frame_start, frame_end, step=1, only_selected=False): # import nla; reload(nla); nla.bake() scene = bpy.context.scene obj = bpy.context.object pose = obj.pose info_ls = [] frame_range = range(frame_start, frame_end + 1, step) # could spped this up by applying steps here too... for f in frame_range: scene.set_frame(f) info = pose_info() info_ls.append(info) f += 1 action = bpy.data.actions.new("Action") bpy.context.object.animation_data.action = action pose_items = pose.bones.items() for name, pbone in pose_items: if only_selected and not pbone.select: continue for f in frame_range: matrix = info_ls[int((f - frame_start) / step)][name]["matrix_key"] #pbone.location = matrix.translation_part() #pbone.rotation_quaternion = matrix.to_quat() pbone.matrix_local = [f for v in matrix for f in v] 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 pbone.keyframe_insert("rotation_euler", -1, f, name) pbone.keyframe_insert("scale", -1, f, name) return action from bpy.props import * class BakeAction(bpy.types.Operator): '''Bake animation to an Action''' bl_idname = "nla.bake" bl_label = "Bake Action" bl_options = {'REGISTER', 'UNDO'} frame_start = IntProperty(name="Start Frame", description="Start frame for baking", default=1, min=1, max=300000) frame_end = IntProperty(name="End Frame", description="End frame for baking", default=250, min=1, max=300000) step = IntProperty(name="Frame Step", description="Frame Step", default=1, min=1, max=120) only_selected = BoolProperty(name="Only Selected", default=True) def execute(self, context): props = self.properties action = bake(props.frame_start, props.frame_end, props.step, props.show_only_selected) # basic cleanup, could move elsewhere for fcu in action.fcurves: keyframe_points = fcu.keyframe_points i = 1 while i < len(fcu.keyframe_points) - 1: val_prev = keyframe_points[i - 1].co[1] val_next = keyframe_points[i + 1].co[1] val = keyframe_points[i].co[1] if abs(val - val_prev) + abs(val - val_next) < 0.0001: keyframe_points.remove(keyframe_points[i]) else: i += 1 return {'FINISHED'} def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) #def menu_func(self, context): # self.layout.operator(BakeAction.bl_idname, text="Bake Armature Action") def register(): pass # bpy.types.INFO_MT_mesh_add.append(menu_func) def unregister(): pass # bpy.types.INFO_MT_mesh_add.remove(menu_func) if __name__ == "__main__": register()