import bpy from . import fn class AW_OT_world_space_copy(bpy.types.Operator): bl_idname = "pose.world_space_copy" bl_label = "World Copy" bl_description = "Copy world space transforms. Store active bone matrix" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): return context.object and context.mode == 'POSE' and context.active_pose_bone def execute(self, context): bpy.types.ViewLayer.world_space_store = context.object.matrix_world @ context.active_pose_bone.matrix return {"FINISHED"} class AW_OT_world_space_paste(bpy.types.Operator): bl_idname = "pose.world_space_paste" bl_label = "World Paste" bl_description = "Paste world space transforms. Apply stored matrix to active bone" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): if not hasattr(bpy.context.view_layer, 'world_space_store'): cls.poll_message_set("Nothing To Paste") return False return context.object and context.mode == 'POSE' and context.active_pose_bone def execute(self, context): context.active_pose_bone.matrix = context.object.matrix_world.inverted() @ bpy.context.view_layer.world_space_store if context.scene.tool_settings.use_keyframe_insert_auto: bpy.ops.anim.keyframe_insert_menu(type='Available') return {"FINISHED"} class AW_OT_world_space_paste_next(bpy.types.Operator): bl_idname = "pose.world_space_paste_next" bl_label = "World Paste Jump" bl_description = "Paste world space transforms and keyframe available chanels\nThen jump to prev/next key" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): if not hasattr(bpy.context.view_layer, 'world_space_store'): cls.poll_message_set("Nothing To Paste") return False return context.object and context.mode == 'POSE' and context.active_pose_bone prev: bpy.props.BoolProperty() def execute(self, context): # apply matrix context.active_pose_bone.matrix = context.object.matrix_world.inverted() @ bpy.context.view_layer.world_space_store # insert keyframe at value # context.object.keyframe_insert(data_path, index=-1, frame=bpy.context.scene.frame_current, group="", options={'INSERTKEY_AVAILABLE'}) bpy.ops.anim.keyframe_insert_menu(type='Available') # jump to next key act = fn.get_obj_action(context.object) if not act: self.report({'ERROR'}, 'No action on armature') return {'CANCELLED'} kx = [k.co.x for fcu in act.fcurves if fcu.data_path.split('"')[1] == context.active_pose_bone.bone.name for k in fcu.keyframe_points] if not kx: self.report({'ERROR'}, 'No keys on action available (no keyframe added)') return {'CANCELLED'} # for fcu in act.fcurves: # if fcu.data_path.split('"')[1] == context.active_pose_bone.bone.name: if self.prev: new_frame = next((k for k in reversed(kx) if k < context.scene.frame_current), None) else: new_frame = next((k for k in kx if k > context.scene.frame_current), None) if not new_frame: self.report({'WARNING'}, 'No next frame to jump on') return {'FINISHED'} context.scene.frame_current = int(new_frame) return {"FINISHED"} class AW_OT_world_space_paste_next_frame(bpy.types.Operator): bl_idname = "pose.world_space_paste_next_frame" bl_label = "World Paste Jump Frame" bl_description = "Paste world space transforms and keyframe available chanels\nThen jump to prev/next frame" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): if not hasattr(bpy.context.view_layer, 'world_space_store'): cls.poll_message_set("Nothing To Paste") return False return context.object and context.mode == 'POSE' and context.active_pose_bone prev: bpy.props.BoolProperty() def execute(self, context): # apply matrix context.active_pose_bone.matrix = context.object.matrix_world.inverted() @ bpy.context.view_layer.world_space_store # insert keyframe at value # context.object.keyframe_insert(data_path, index=-1, frame=bpy.context.scene.frame_current, group="", options={'INSERTKEY_AVAILABLE'}) bpy.ops.anim.keyframe_insert_menu(type='Available') # jump to next frame act = fn.get_obj_action(context.object) if not act: self.report({'ERROR'}, 'No action on armature') return {'CANCELLED'} offset = -1 if self.prev else 1 new_frame = context.scene.frame_current + offset context.scene.frame_current = new_frame return {"FINISHED"} classes=( AW_OT_world_space_copy, AW_OT_world_space_paste, AW_OT_world_space_paste_next, AW_OT_world_space_paste_next_frame, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls)