750 lines
36 KiB
Python
750 lines
36 KiB
Python
# ***** 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) |