# ***** 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 LICENCE BLOCK ***** import bpy from . import Rigger_Toolbox from . import multikey from . import Rigger_Toolbox from pathlib import Path from bpy.utils import register_class from bpy.utils import unregister_class import os ######################################################## MENUS ######################################################## class ANIMTOOLBOX_MT_Copy_Paste_Matrix(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Copy_Paste_Matrix' bl_label = "Copy Paste Matrix" @classmethod def poll(cls, context): return True def draw(self, context): layout = self.layout layout.operator('anim.copy_matrix', text = 'Copy World Matrix', icon ='DUPLICATE') layout.operator('anim.paste_matrix', text = 'Paste World Matrix', icon ='PASTEDOWN') layout.separator() layout.operator('anim.copy_relative_matrix', text = 'Copy Relative Matrix', icon ='DUPLICATE') layout.operator('anim.paste_relative_matrix', text = 'Paste Relative Matrix', icon ='PASTEDOWN') layout.separator() layout.prop_menu_enum(context.scene.animtoolbox, 'range_type', text = 'Frame Range Type') if context.scene.animtoolbox.range_type == 'RANGE': # split = layout.split(factor = 0.3) # layout.prop(context.scene.animtoolbox, 'bake_frame_start', text = 'Start') # layout.prop(context.scene.animtoolbox, 'bake_frame_end', text = 'End') layout.operator("anim.markers_bakerange", icon = 'MARKER', text ='Frame Range Markers Widget', depress = context.scene.animtoolbox.bake_frame_range) class ANIMTOOLBOX_MT_Temp_Ctrls(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Temp_Ctrls' bl_label = "Temp Ctrls" @classmethod def poll(cls, context): return True def draw(self, context): layout = self.layout custom_icons = preview_collections["main"] layout.operator('anim.bake_to_ctrl', text="World Space Ctrls", icon = 'WORLD') # layout.operator('anim.worldspace_cursor', text="World Space Cursor Ctrls", icon ='ORIENTATION_CURSOR') layout.operator('anim.bake_to_temp_fk', text="Temp FK Ctrls", icon = 'BONE_DATA') layout.operator('anim.bake_to_temp_ik', text="Temp IK Ctrls", icon = 'CON_KINEMATIC') layout.separator() layout.operator('anim.link_temp_chains', text="Link Temp Chains", icon = 'DECORATE_LINKED') layout.operator('anim.unlink_temp_chains', text="UnLink Temp Chains", icon = 'DECORATE_LINKED') layout.separator() layout.operator('anim.bake_temp_ctrls', text="Bake Temp Ctrls", icon_value = custom_icons["oven"].icon_id) layout.operator('anim.remove_bones_constraints', text="Cleanup", icon_value = custom_icons["trash"].icon_id) layout.prop_menu_enum(context.scene.btc, 'target', text = 'Bake To', icon = 'MOD_ARMATURE') layout.separator() ctrl = context.object.animtoolbox.controller if context.object.animtoolbox.controller else context.object if ctrl: layout.prop(ctrl.animtoolbox, 'ctrls_enabled', text ='Temp Ctrls On/Off') # layout.operator('anim.enable_tempctrls', text="On" , icon = 'HIDE_OFF')#icon = 'CONSTRAINT_BONE' # layout.operator('anim.disable_tempctrls', text="Off", icon = 'HIDE_ON') layout.separator() layout.prop_menu_enum(context.scene.btc, 'shape_type', icon = 'MESH_DATA') layout.prop(context.scene.btc, 'shape_size', slider = False, icon ='CON_SIZELIKE') layout.prop(context.scene.btc, 'color_set', text = '') # layout.prop(context.scene.btc, 'shape_type', icon = 'MESH_DATA', text ='') class ANIMTOOLBOX_MT_Keyframe_Offset(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Keyframe_Offset' bl_label = "Interactive Keyframe Offset" def draw(self, context): layout = self.layout layout.prop(context.scene.animtoolbox, 'keyframes_offset', slider = False) layout.operator('anim.select_keyframes_offset', text="Select Offset Keyframes", icon ='RESTRICT_SELECT_OFF') layout.operator('anim.apply_keyframes_offset', text="Apply Offset", icon = 'ANIM') class ANIMTOOLBOX_MT_Blendings(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Blendings' bl_label = "Blendings" def draw(self, context): layout = self.layout ui = context.window_manager.atb_ui layout.prop(ui, 'inbetween_worldmatrix', slider = True) layout.prop(context.scene.animtoolbox, 'inbetweener', slider = True) layout.prop(ui, 'blend_mirror', slider = True) class ANIMTOOLBOX_MT_Multikey(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Multikey' bl_label = "Multikey" def draw(self, context): layout = self.layout ui = context.window_manager.atb_ui layout.operator("animtoolbox.multikey", icon = 'ACTION_TWEAK') layout.prop(ui.multikey, 'scale', slider = True) layout.prop(ui.multikey, 'randomness', slider = True) class ANIMTOOLBOX_MT_Convert_Rotations(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_Convert_Rotations' bl_label = "Convert Rotations" def draw(self, context): layout = self.layout layout.operator("anim.convert_rotation_mode", icon = 'DRIVER_ROTATIONAL_DIFFERENCE', text = 'Convert Rotation To') layout.operator("anim.find_rotation_mode", icon = 'VIEWZOOM', text = 'Recommend Euler Rotation') layout.prop_menu_enum(context.scene.animtoolbox, 'rotation_mode', text = 'Convert To') class ANIMTOOLBOX_MT_operators(bpy.types.Menu): bl_idname = 'ANIMTOOLBOX_MT_menu_operators' bl_label = "AnimToolBox" @classmethod def poll(cls, context): return True def draw(self, context): layout = self.layout scene = context.scene ui = context.window_manager.atb_ui custom_icons = preview_collections["main"] if context.area.type == 'VIEW_3D': layout.menu('ANIMTOOLBOX_MT_Temp_Ctrls', text = 'Temp Ctrls', icon_value = custom_icons["puppet"].icon_id) layout.separator() layout.operator('anim.share_keyframes', text = 'Share Keyframes', icon_value = custom_icons["sharekeys"].icon_id) layout.operator('anim.relative_cursor', text = 'Relative Cursor Pivot', icon_value = custom_icons["relative_cursor"].icon_id, depress = ui.relative_cursor) layout.operator("anim.markers_retimer", icon_value = custom_icons["retime"].icon_id, text ='Markers Retimer', depress = ui.markers_retimer) layout.menu('ANIMTOOLBOX_MT_Convert_Rotations', text = 'Convert Rotations', icon = 'DRIVER_ROTATIONAL_DIFFERENCE') layout.separator() layout.menu('ANIMTOOLBOX_MT_Keyframe_Offset', text = 'Interactive Keyframe Offset', icon_value = custom_icons["keyframe_offset"].icon_id) layout.menu('ANIMTOOLBOX_MT_Blendings', text = 'Blendings and inbetweens', icon_value = custom_icons["sliders"].icon_id) layout.separator() layout.menu('ANIMTOOLBOX_MT_Copy_Paste_Matrix', text = 'Copy Paste Matrix', icon_value = custom_icons["copy_matrix"].icon_id) if bpy.context.preferences.addons[__package__].preferences.multikey: layout.separator() layout.menu('ANIMTOOLBOX_MT_Multikey', text = 'Multikey', icon_value = custom_icons["multikey"].icon_id) layout.separator(factor = 0.5) layout.operator('anim.switch_collections_visibility', icon = 'COLLECTION_COLOR_06', text = 'Animated Collections Visibilty') layout.operator('anim.isolate_pose_mode', icon_value = custom_icons["isolate"].icon_id, depress = scene.animtoolbox.isolate_pose_mode) layout.operator('object.motion_path_operator', text = 'Editable Motion Path', depress = scene.emp.motion_path, icon_value = custom_icons["mt"].icon_id) layout.separator() layout.prop(context.preferences.addons[__package__].preferences, 'quick_menu', text = 'Use Quick Icons Menu') # layout.prop(context.window_manager.atb_ui, 'quick_menu', text = 'Use Quick Icons Menu') # elif context.area.type == 'DOPESHEET_EDITOR': # layout.operator('wm.call_menu_pie', text="Pie AnimOffset").name = 'ANIMAIDE_MT_pie_anim_offset' # layout.menu('ANIMAIDE_MT_anim_offset') # layout.menu('ANIMAIDE_MT_anim_offset_mask') # elif context.area.type == 'GRAPH_EDITOR': # layout.menu('ANIMAIDE_MT_curve_tools_pie') ######################################################## WorkSpaceTools ######################################################## class KeyframeOffsetTool(bpy.types.WorkSpaceTool): bl_space_type = 'VIEW_3D' bl_context_mode = 'POSE' bl_idname = 'animtoolbox.keyframe_offset' bl_label = 'Keyframe Offset Tool' bl_description = ( 'Offset the keyframes of the selected bone\n' 'Shift + Click to select the bones with an offset\n' 'Ctrl + Alt + Click to Apply the offset' ) bl_icon = (Path(__file__).parent / "icons" / "ops.anim.keyframe_offset").as_posix() bl_keymap = ( ('anim.keyframe_offset', {'type': 'LEFTMOUSE', 'value': 'CLICK_DRAG'}, None), # ('anim.select_keyframes_offset', {'type': 'LEFTMOUSE', 'value': 'CLICK', 'shift': True}, None), ('anim.apply_keyframes_offset', {'type': 'LEFTMOUSE', 'value': 'CLICK', 'ctrl': True, 'alt': True}, None), ('view3d.select_box', {'type': 'LEFTMOUSE', 'value': 'CLICK_DRAG', 'shift': True}, {'properties': [('mode', 'ADD')]}), ('view3d.select_box', {'type': 'LEFTMOUSE', 'value': 'CLICK_DRAG', 'ctrl': True}, {'properties': [('mode', 'SUB')]}), ('view3d.select_box', {'type': 'LEFTMOUSE', 'value': 'CLICK_DRAG', 'shift': True, 'ctrl': True}, {'properties': [('mode', 'AND')]}), ('view3d.select', {'type': 'LEFTMOUSE', 'value': 'CLICK'}, {'properties': [('deselect_all', True)]}), ('view3d.select', {'type': 'LEFTMOUSE', 'value': 'CLICK', 'shift': True}, {'properties': [('toggle', True)]}), ('view3d.select', {'type': 'LEFTMOUSE', 'value': 'CLICK', 'alt': True}, {'properties': [('enumerate', True)]}), ('view3d.select', {'type': 'LEFTMOUSE', 'value': 'CLICK', 'shift': True, 'alt': True}, {'properties': [('toggle', True), ('enumerate', True)]}) ) ######################################################## PANELS ######################################################## class ANIMTOOLBOX_PT_Panel: bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Animation" #bl_options = {"DEFAULT_CLOSED"} @classmethod def poll(cls, context): return context.object is not None class ANIMTOOLBOX_PT_MainPanel(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): #bl_category = "Animation" bl_label = "AnimToolBox" bl_idname = "ANIMTOOLBOX_PT_MainPanel" bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): custom_icons = preview_collections["main"] self.layout.label(text='', icon_value = custom_icons["animtoolbox"].icon_id) def draw(self, context): obj = context.object if obj is None: return # layout = self.layout # row = layout.row(align = True) # row.label(text = 'Support me on ') # row.operator("wm.url_open", text="Patreon", icon = 'FUND').url = "https://www.patreon.com/animtoolbox" class TEMPCTRLS_PT_Panel(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): #bl_category = "Animation" bl_label = "Temp Controls" bl_idname = "TEMPCTRLS_PT_Panel" bl_parent_id = 'ANIMTOOLBOX_PT_MainPanel' bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): custom_icons = preview_collections["main"] self.layout.label(text="", icon_value = custom_icons["puppet"].icon_id) def draw(self, context): obj = context.object btc = context.scene.btc if obj is None: return custom_icons = preview_collections["main"] oven_icon = custom_icons["oven"] # row = layout.row() ui = context.window_manager.atb_ui layout = self.layout box = layout.box() col = box.column() row = col.row() row.operator('anim.bake_to_ctrl', text="WorldSpace Ctrls", icon ='WORLD') # row.operator('anim.add_empty_ctrl', text="", icon ='EMPTY_AXIS') # col.operator('anim.worldspace_cursor', text="WorldSpace Cursor Ctrls", icon ='ORIENTATION_CURSOR') col.operator('anim.bake_to_temp_fk', text="Temp FK Ctrls", icon = 'BONE_DATA') col.operator('anim.bake_to_temp_ik', text="Temp IK Ctrls", icon = 'CON_KINEMATIC') # layout.separator(factor = 0.1) # box = layout.box() row = box.row() row.operator('anim.link_temp_chains', text="Link Temp Chains", icon = 'DECORATE_LINKED') row.prop(btc, 'linksettings', text = '', icon = 'SETTINGS') if btc.linksettings: # insidebox = box.box() row = box.row() row.label(text = 'Link to Active Chain: ') row.prop(btc, 'link_to', text = '') row = box.row() row.operator('anim.unlink_temp_chains', text="UnLink Temp Chains", icon = 'UNLINKED') # row = insidebox.row() # row.label(text = 'Link from Chain: ') # row.prop(btc, 'link_from', text = '') # layout.separator(factor = 0.1) box = layout.box() row = box.row() row.operator('anim.bake_to_empties', text="WorldSpace Empties") row.operator('anim.empties_to_bones', text="", icon = 'EMPTY_AXIS') box = layout.box() row = box.row() # row.operator('anim.bake_constrained_bones', text="Quick Bake", icon = 'REC') row.operator('anim.bake_temp_ctrls', text="Bake Temp Ctrls", icon_value = oven_icon.icon_id) row.prop(btc, 'bakesettings', text = '', icon = 'SETTINGS') if btc.bakesettings: insidebox = box.box() # split = insidebox.split(factor = 0.9) # split.prop(btc, 'smartbake', text = 'Smart Bake') # split.operator('fcurves.filter_ui', icon ='FILTER', text = '') # if btc.smartbake: # row.prop(btc, 'inbetween_keyframes') split = insidebox.split(factor = 0.3) split.label(text = 'Bake To:') # split.split(factor = 0.9) split_2 = split.split(factor = 0.9) split_2.prop(btc, 'target', text = '') split_2.operator('fcurves.filter_ui', icon ='FILTER', text = '') split = insidebox.split(factor = 0.3) split.label(text = 'Bake Range:') split.prop(btc, 'bake_range_type', text = '') if btc.bake_range_type == 'KEYFRAMES': row = insidebox.row() row.label(text = 'Keyframes: ') row.prop(btc, 'smartbake', text = 'Smart Bake') row.prop(btc, 'bake_layers', text = 'All Layers') row = insidebox.row() row.label(text = 'From:') row.prop(btc, 'from_origin', text = 'Origin') row.prop(btc, 'from_ctrl', text = 'Ctrls') elif btc.bake_range_type == 'CUSTOM': row = insidebox.row() row.prop(btc, 'bake_range', text = '') insidebox.separator(factor=0.1) row = insidebox.row() row.label(text = 'Clean: ') row.prop(btc, 'clean_constraints', text = 'Constraints') if btc.clean_constraints: row.prop(btc, 'clean_ctrls', text = 'Ctrls') row = box.row() row.operator('anim.remove_bones_constraints', text="Cleanup", icon_value = custom_icons["trash"].icon_id) row.prop(btc, 'cleansettings', text = '', icon = 'SETTINGS') if btc.cleansettings: insidebox = box.box() split = insidebox.split(factor = 0.4) split.label(text = 'Target: ') split.prop(btc, 'target', text = '') row = insidebox.row() row.label(text = 'Clean: ') row.prop(btc, 'clean_constraints', text = 'Constraints') if btc.clean_constraints: row.prop(btc, 'clean_ctrls', text = 'Ctrls') if btc.target == 'SELECTED': split = insidebox.split(factor = 0.68) split.label(text = 'ReBake Link Ctrls to Original: ') split.prop(btc, 'rebake_to_org', text = '') box = layout.box() row = box.row() # row = layout.row() # row.prop(obj.animtoolbox, 'influence', text = 'Influence', slider = True) ctrl = obj.animtoolbox.controller if obj.animtoolbox.controller else obj if ctrl: enabled_icon = 'HIDE_OFF' if ctrl.animtoolbox.ctrls_enabled else 'HIDE_ON' row.prop(ctrl.animtoolbox, 'ctrls_enabled', text ='Temp Ctrls On/Off', icon = enabled_icon) # row = box.row() # row.prop(ui, 'temp_ctrls_switch', icon = 'RESTRICT_VIEW_OFF', text = '') # row.label(text = 'Temp Ctrls Switch: ') # row.prop(btc, 'target', text = '', icon = 'MOD_ARMATURE', icon_only = True) # if ui.temp_ctrls_switch: # # split = layout.split(factor = 0.225) # row = box.row() # row.operator('anim.enable_tempctrls', text="On" , icon = 'HIDE_OFF')#icon = 'CONSTRAINT_BONE' # row.operator('anim.disable_tempctrls', text="Off", icon = 'HIDE_ON') box.separator(factor=0.1) row = box.row() row.prop(ui, 'temp_ctrls_shapes', icon = 'MESH_DATA', text = '') row.label(text = 'Temp Ctrls Shapes: ') row.prop(btc, 'target', text = '', icon = 'MOD_ARMATURE', icon_only = True) if ui.temp_ctrls_shapes: row = box.row() row.prop(btc, 'shape_size', slider = False) row.prop(btc, 'shape_type', text = '') row = layout.row() # split.label(text = 'Theme Color: ') row.prop(btc, 'color_set', text = '') layout.separator() row = layout.row() row.operator('anim.btc_selections', text="Select", icon = 'CONSTRAINT_BONE') row.prop(btc, 'selection', text = '', icon = 'CON_ARMATURE') class ANIMTOOLBOX_PT_Tools(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): bl_label = "Anim Tools" bl_idname = "ANIMTOOLBOX_PT_Tools" bl_parent_id = 'ANIMTOOLBOX_PT_MainPanel' bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): custom_icons = preview_collections["main"] layout = self.layout layout.label(text="", icon_value = custom_icons["toolbox"].icon_id) def draw(self, context): obj = context.object scene = context.scene atb = scene.animtoolbox if obj is None: return custom_icons = preview_collections["main"] layout = self.layout ui = context.window_manager.atb_ui row = layout.row() filter_text = 'Filter Tools Properties' if atb.filter_name == '' else 'Filters: ' + atb.filter_name filter_depress = False if atb.filter_name == '' else True row.operator('fcurves.filter_ui', icon ='FILTER', text = filter_text, depress = filter_depress) layout.separator() #layout.operator('anim.keyframes_offset', text="Offset Keyframes", icon = 'NEXT_KEYFRAME') split = layout.split(factor = 0.9) split.prop(context.scene.animtoolbox, 'keyframes_offset', slider = True) split.operator('anim.apply_keyframes_offset', text="", icon_value = custom_icons["apply"].icon_id) split.operator('anim.select_keyframes_offset', text="", icon_value = custom_icons["select"].icon_id) row = layout.row() row.operator('anim.share_keyframes', text = 'Share Keyframes', icon_value = custom_icons["sharekeys"].icon_id) row = layout.row() row.operator("anim.relative_cursor", text ='Relative Cursor Pivot', icon_value = custom_icons["relative_cursor"].icon_id, depress = ui.relative_cursor) row = layout.row() row.operator("anim.markers_retimer", icon_value = custom_icons["retime"].icon_id, text ='Markers Retimer', depress = ui.markers_retimer) layout.separator(factor = 0.5) box = layout.box() # icon = 'DOWNARROW_HLT' if ui.copy_paste_matrix is True else 'RIGHTARROW_THIN' # split = box.split(factor = 0.9) # split.prop(ui, 'copy_paste_matrix', icon_value = custom_icons["copy_matrix"].icon_id, text = 'Copy Paste Matrices') # split.operator('fcurves.filter', icon ='FILTER', text = '') row = box.row() row.prop(ui, 'copy_paste_matrix', icon_value = custom_icons["copy_matrix"].icon_id, text = 'Copy Paste Matrices') if ui.copy_paste_matrix: row = box.row() row.label(text = 'Copy World Matrix :', icon = 'WORLD') row.operator('anim.copy_matrix', text = '', icon ='DUPLICATE') row.operator('anim.paste_matrix', text = '', icon ='PASTEDOWN') row = box.row() row.label(text = 'Copy Relative Matrix :', icon = 'LINKED') row.operator('anim.copy_relative_matrix', text = '', icon ='DUPLICATE') row.operator('anim.paste_relative_matrix', text = '', icon ='PASTEDOWN') split = box.split(factor = 0.32) split.label(text = 'Paste To') split.prop(context.scene.animtoolbox, 'range_type', text = '') if context.scene.animtoolbox.range_type == 'RANGE': row = box.row() row.prop(context.scene.animtoolbox, 'bake_frame_start', text = 'Start') row.prop(context.scene.animtoolbox, 'bake_frame_end', text = 'End') row.operator("anim.markers_bakerange", icon = 'MARKER', text ='', depress = context.scene.animtoolbox.bake_frame_range) layout.separator(factor = 0.5) box = layout.box() # icon = 'DOWNARROW_HLT' if ui.Inbetweens is True else 'RIGHTARROW_THIN' # split = box.split(factor = 0.9) # split.prop(ui, 'Inbetweens', icon_value = custom_icons["sliders"].icon_id, text = 'Blendings / Inbetweens') # split.operator('fcurves.filter', icon ='FILTER', text = '') row = box.row() row.prop(ui, 'Inbetweens', icon_value = custom_icons["sliders"].icon_id, text = 'Blendings / Inbetweens') if ui.Inbetweens: col = box.column() col.prop(ui, 'inbetween_worldmatrix', slider = True) col.prop(scene.animtoolbox, 'inbetweener', slider = True) col.prop(ui, 'blend_mirror', slider = True) layout.separator(factor = 1) # my_icon = custom_icons["rotations"] split = layout.split(factor = 0.6) split.operator("anim.convert_rotation_mode", icon = 'DRIVER_ROTATIONAL_DIFFERENCE', text = 'Convert Rotation To') # split.operator("anim.convert_rotation_mode", text = 'Convert Rotation To', icon_value = my_icon.icon_id) split = split.split(factor = 0.3) split.operator("anim.find_rotation_mode", icon = 'VIEWZOOM', text = '') split.prop(context.scene.animtoolbox, 'rotation_mode', text = '') class MULTIKEY_PT_Panel(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): """Add random value to selected keyframes""" bl_label = "Multikey" bl_idname = "MULTIKEY_PT_Panel" # bl_space_type = 'VIEW_3D' # bl_region_type = 'UI' # bl_category= 'Animation' bl_parent_id = 'ANIMTOOLBOX_PT_Tools' bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): custom_icons = preview_collections["main"] layout = self.layout layout.label(text="", icon_value = custom_icons["multikey"].icon_id) def draw(self, context): custom_icons = preview_collections["main"] layout = self.layout ui = context.window_manager.atb_ui # layout.separator() split = layout.split(factor=0.1, align = True) split.prop(ui.multikey, 'selectedbones', icon_value = custom_icons["selected_bones"].icon_id, text = '') # split = split.split(factor=0.1, align = True) #split.operator('fcurves.filter', icon ='FILTER', text = '') split.operator("animtoolbox.multikey", icon = 'ACTION_TWEAK') layout.separator() row = layout.row(align = True) row.prop(ui.multikey, 'scale') row.prop(ui.multikey, 'randomness', slider = True) class ANIMTOOLBOX_PT_Display(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): bl_label = "Display" bl_idname = "ANIMTOOLBOX_PT_Display" bl_parent_id = 'ANIMTOOLBOX_PT_MainPanel' bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): custom_icons = preview_collections["main"] self.layout.label(text="", icon_value = custom_icons["display"].icon_id) def draw(self, context): scene = context.scene layout = self.layout ui = context.window_manager.atb_ui custom_icons = preview_collections["main"] box = layout.box() row = box.row() icon = 'DOWNARROW_HLT' if ui.gizmo_size is True else 'RIGHTARROW_THIN' row.prop(ui, 'gizmo_size', icon = icon, text = 'Add to Gizmo Size:') if ui.gizmo_size: row = box.row() row.prop(scene.animtoolbox, 'gizmo_size', text = '') row.operator('view3d.gizmo_size_up', text="", icon ='ADD') row.operator('view3d.gizmo_size_down', text="", icon ='REMOVE') layout.separator(factor = 0.5) row = layout.row() col_vis_icon = 'OUTLINER_COLLECTION' if scene.animtoolbox.col_vis else 'COLLECTION_COLOR_06' row.operator('anim.switch_collections_visibility', icon = col_vis_icon, text = 'Animated Collections Visibilty', depress = scene.animtoolbox.col_vis) layout.separator(factor = 0.5) row = layout.row() row.operator('anim.isolate_pose_mode', icon_value = custom_icons["isolate"].icon_id, depress = scene.animtoolbox.isolate_pose_mode) layout.separator(factor = 0.5) box = layout.box() row = box.row() row.operator('object.motion_path_operator', text = 'Editable Motion Path', depress = scene.emp.motion_path, icon_value = custom_icons["mt"].icon_id) emp = scene.emp row.prop(scene.emp, 'settings', text = '', icon = 'SETTINGS') if scene.emp.settings: split = box.split(factor = 0.4) split.label(text ='Frame Range') # row.prop(scene.animtoolbox, 'mp_frame_range', text = '') split.prop(emp, 'frame_range', text = '') if emp.frame_range == 'MANUAL': row = box.row() row.prop(emp, 'frame_start', text = 'Start') row.prop(emp, 'frame_end', text = 'End') row.operator("anim.emp_markers_range", icon = 'MARKER', text ='', depress = scene.emp.marker_frame_range) elif emp.frame_range == 'AROUND': row = box.row() row.prop(emp, 'before', text = 'Before') row.prop(emp, 'after', text = 'After') box.separator(factor = 0.1) col = box.column() col.prop(emp, 'display_size', icon = 'DOWNARROW_HLT') if emp.display_size: col.prop(emp, 'thickness', text = 'Line Thickness') col.prop(emp, 'keyframe_size', text = 'Keyframes Size') col.prop(emp, 'frame_size', text = 'Frames Size') box.separator(factor = 0.1) row = box.row() row.label(text = 'Visualization Type') row.prop(emp, 'vis_type', text = '') if emp.vis_type == 'VELOCITY': row = box.row() row.prop(emp, 'velocity_factor', text = '') row.prop(emp, 'clamp_min', text = '') row.prop(emp, 'clamp_max', text = '') row = box.row() row.prop(emp, 'color_before', text = '') row.prop(emp, 'color_after', text = '') row = box.row() row.prop(emp, 'points', text = 'Points') row.prop(emp, 'lines', text = 'Lines') row = box.row() row.prop(emp, 'handles', text = 'Handles') row.prop(emp, 'infront', text = 'In Front') row = box.row() row.prop(emp, 'display_frames', text = 'Display Keyframe Numbers') row.separator() box = layout.box() split = box.split(factor = 0.35) split.label(text = 'Channels') split.prop(emp, 'channels', text ='') row = box.row() if bpy.app.version >= (4, 2, 0): row.prop(emp, 'camera_space', icon = 'CON_CAMERASOLVER') #row = box.row() row.prop(emp, 'cursor_offset', icon = 'PIVOT_CURSOR') box.separator(factor = 0.05) row = box.row() row.operator('anim.go_to_keyframe', icon = 'NEXT_KEYFRAME') box.separator(factor = 0.05) # split = box.split(factor = 0.75) row = box.row() row.prop(emp, 'select_circle', text = 'Selection Brush', icon = 'MESH_CIRCLE') row = box.row() prop_edit_icon = 'PROP_ON' if emp.prop_edit else 'PROP_OFF' row.prop(emp, 'prop_edit', text = 'Proportional Editing', icon = prop_edit_icon) row.prop(emp, 'smooth', text = 'Smooth', icon = 'MOD_SMOOTH') row = box.row() if emp.prop_edit: row.prop(emp, 'prop_edit_falloff', text = '') row.prop(emp, 'radius', text = '') elif emp.smooth: row.prop(emp, 'smooth_strength', text = '') row.prop(emp, 'radius', text = '') # row = box.row() # row.prop(context.preferences.addons[__package__].preferences, 'keyframes_range', text = 'Keyframe Distance Range') # if context.object is None: # return # row.operator("anim.markers_bakerange", icon = 'MARKER', text ='', depress = scene.animtoolbox.bake_frame_range) class RIGGERTOOLBOX_PT_Panel(ANIMTOOLBOX_PT_Panel, bpy.types.Panel): bl_label = "Rigger Toolbox" bl_idname = "RIGGERTOOLBOX_PT_Panel" bl_parent_id = 'ANIMTOOLBOX_PT_MainPanel' bl_options = {"DEFAULT_CLOSED"} def draw(self, context): obj = context.object if obj is None: return layout = self.layout col = layout.column() col.operator("armature.add_bbone_widgets", text = "Add Bbone Widgets", icon = 'IPO_BEZIER') col.separator() col.operator("armature.add_chain_ctrls", text = "Add Chain Controls", icon = 'OUTLINER_DATA_ARMATURE') col.separator() col.operator("armature.merge", text = "Merge Rigs", icon = 'MOD_ARMATURE') # Add-ons Preferences Update Panel # Define Panel classes for updating panels = [ANIMTOOLBOX_PT_MainPanel, TEMPCTRLS_PT_Panel, ANIMTOOLBOX_PT_Tools, ANIMTOOLBOX_PT_Display] def update_panel(self, context): message = "Animtoolbox: Updating Panel locations has failed" try: for panel in panels: if "bl_rna" in panel.__dict__: unregister_class(panel) for panel in panels: panel.bl_category = context.preferences.addons[__package__].preferences.category register_class(panel) except Exception as e: print("\n[{}]\n{}\n\nError:\n{}".format(__package__, message, e)) pass def add_multikey(self, context): if bpy.context.preferences.addons[__package__].preferences is None: return if bpy.context.preferences.addons[__package__].preferences.multikey: multikey.register() register_class(MULTIKEY_PT_Panel) register_class(ANIMTOOLBOX_MT_Multikey) panels.append(MULTIKEY_PT_Panel) #panels = panels + (MULTIKEY_PT_Panel) elif hasattr(bpy.types, 'MULTIKEY_PT_Panel'): multikey.unregister() panels.remove(MULTIKEY_PT_Panel) unregister_class(MULTIKEY_PT_Panel) unregister_class(ANIMTOOLBOX_MT_Multikey) def add_riggertoolbox(self, context): if bpy.context.preferences.addons[__package__].preferences is None: return if bpy.context.preferences.addons[__package__].preferences.riggertoolbox: Rigger_Toolbox.register() register_class(RIGGERTOOLBOX_PT_Panel) panels.append(RIGGERTOOLBOX_PT_Panel) #panels = panels + (RIGGERTOOLBOX_PT_Panel) elif hasattr(bpy.types, 'RIGGERTOOLBOX_PT_Panel'): Rigger_Toolbox.unregister() panels.remove(RIGGERTOOLBOX_PT_Panel) unregister_class(RIGGERTOOLBOX_PT_Panel) classes = (ANIMTOOLBOX_MT_Copy_Paste_Matrix, ANIMTOOLBOX_MT_Temp_Ctrls, ANIMTOOLBOX_MT_operators, ANIMTOOLBOX_MT_Keyframe_Offset, ANIMTOOLBOX_MT_Blendings, ANIMTOOLBOX_MT_Convert_Rotations) + tuple(panels) preview_collections = {} #list of all the custom icons icons_list = {'relative_cursor' : 'relative_cursor.png', 'puppet' : 'puppet.png', 'animtoolbox' : 'animtoolbox.png', 'sliders' : 'slider-navigation.png', 'world_space' : 'earth-rotation.png', 'isolate' : 'isolation.png', 'mt' : 'curve.png', 'toolbox' : 'tools.png', 'oven' : 'oven.png', 'retime' : 'retime.png', 'copy_matrix' : 'copy_world.png', 'sharekeys' : 'sharekeys.png', 'sliders' : 'sliders.png', 'keyframe_offset' : 'keyframes_offset.png', 'display' : 'display.png', 'switch' : 'switch.png', 'trash' : 'trash.png', 'apply' : 'apply.png', 'select' : 'select.png', 'worldspace' : 'WorldSpace.png', 'multikey' : 'multikey.png', 'selected_bones' : 'selected_bones.png'} def register_custom_icon(): # Note that preview collections returned by bpy.utils.previews # are regular py objects - you can use them to store custom data. import bpy.utils.previews custom_icons = bpy.utils.previews.new() # path to the folder where the icon is # the path is calculated relative to this py file inside the addon folder icons_dir = os.path.join(os.path.dirname(__file__), "icons") # load a preview thumbnail of a file and store in the previews collection for iconname, icon_file in icons_list.items(): custom_icons.load(iconname, os.path.join(icons_dir, icon_file), 'IMAGE') preview_collections["main"] = custom_icons def draw_menu(self, context): if context.mode == 'OBJECT' or context.mode == 'POSE': layout = self.layout scene = context.scene ui = context.window_manager.atb_ui custom_icons = preview_collections["main"] layout.menu('ANIMTOOLBOX_MT_menu_operators') #, icon_value = custom_icons["toolbox"].icon_id, text ='' if not context.preferences.addons[__package__].preferences.quick_menu: return #Only Icons Menu temp_ctrls = custom_icons["puppet"] layout.menu('ANIMTOOLBOX_MT_Temp_Ctrls' , icon_value = temp_ctrls.icon_id, text ='') # layout.separator() layout.menu('ANIMTOOLBOX_MT_Copy_Paste_Matrix' , icon_value = custom_icons["copy_matrix"].icon_id, text ='') layout.separator() layout.operator('anim.relative_cursor', text = '', depress = ui.relative_cursor, icon_value = custom_icons["relative_cursor"].icon_id) layout.separator() layout.operator('anim.markers_retimer', text = '', depress = ui.markers_retimer, icon_value = custom_icons["retime"].icon_id) layout.separator() layout.operator('anim.isolate_pose_mode', text = '', depress = scene.animtoolbox.isolate_pose_mode, icon_value = custom_icons["isolate"].icon_id) layout.separator() layout.operator('object.motion_path_operator', text = '', depress = scene.emp.motion_path, icon_value = custom_icons["mt"].icon_id)