2025-12-01
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1803,7 +1803,7 @@ def reorder_bones_matrices(bones_matrices, constrained):
|
||||
|
||||
return re_bones_matrices
|
||||
|
||||
def paste_bone_matrix(bone, matrix_copied, constrained, x_filter = True):
|
||||
def paste_bone_matrix(bone, matrix_copied, constrained, bones = {}, x_filter = True):
|
||||
#running again separatly in case the bones are in a hierarchy and influencing each other
|
||||
# for bone, matrix_copied in bones_matrices.items():
|
||||
# Determine whether to use bone.matrix or bone.matrix_world
|
||||
@@ -1818,10 +1818,14 @@ def paste_bone_matrix(bone, matrix_copied, constrained, x_filter = True):
|
||||
if x_filter : matrix_copied = filter_matrix_properties(context, getattr(bone, matrix_attr), matrix_copied)
|
||||
# bone.matrix = bone.id_data.matrix_world.inverted() @ matrix_copied
|
||||
setattr(bone, matrix_attr, matrix_copied) # bone.id_data.matrix_world.inverted() @
|
||||
|
||||
children = set(bone.children_recursive).intersection(bones)
|
||||
if children or bone in constrained:
|
||||
# print(f'found children {[child.name for child in children]} in bone {bone.name}' )
|
||||
context.view_layer.update()
|
||||
#Check if the bone has constrainsts on it that need extra iteration
|
||||
if bone not in constrained:
|
||||
# filter_matrix_properties(context, bone.matrix, matrix_copied)
|
||||
return
|
||||
context.view_layer.update()
|
||||
|
||||
matrix_copied = reverse_bone_constraints(context, bone, matrix_copied)
|
||||
if x_filter : matrix_copied = filter_matrix_properties(context, bone.matrix, matrix_copied)
|
||||
@@ -1832,8 +1836,12 @@ def paste_bone_matrix(bone, matrix_copied, constrained, x_filter = True):
|
||||
|
||||
def paste_bones_matrices(bones_matrices, constrained, x_filter = True):
|
||||
#running again separatly in case the bones are in a hierarchy and influencing each other
|
||||
pasted_bones = set()
|
||||
for bone, matrix_copied in bones_matrices.items():
|
||||
paste_bone_matrix(bone, matrix_copied, constrained, x_filter)
|
||||
#Get the rest of the bones to check if they are children of the current bone
|
||||
bones = set(bones_matrices.keys()).difference(pasted_bones)
|
||||
paste_bone_matrix(bone, matrix_copied, constrained, bones, x_filter)
|
||||
pasted_bones.add(bone)
|
||||
|
||||
class PasteRelativeMatrix(bpy.types.Operator):
|
||||
"""paste the relative matrix of the selection"""
|
||||
@@ -2522,7 +2530,15 @@ class ConvertRotationMode(bpy.types.Operator):
|
||||
# @classmethod
|
||||
# def poll(cls, context):
|
||||
# return context.object.type == 'ARMATURE'
|
||||
|
||||
def switch_rot_mode_keyframes(self, obj, posebone):
|
||||
# Switching any rotation mode keyframes to the new rotation mode value using to_rot_mode_index
|
||||
bone_path = posebone.path_from_id() + '.' if type(posebone) == bpy.types.PoseBone else ''
|
||||
fcurves = get_fcurves_channelbag(obj, obj.animation_data.action)
|
||||
fcu_rotation_mode = fcurves.find(data_path = bone_path + 'rotation_mode', index = 0)
|
||||
if fcu_rotation_mode:
|
||||
for keyframe in fcu_rotation_mode.keyframe_points:
|
||||
keyframe.co[1] = self.to_rot_mode_index
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
selected_bones = context.selected_pose_bones
|
||||
@@ -2530,7 +2546,8 @@ class ConvertRotationMode(bpy.types.Operator):
|
||||
|
||||
to_rot_mode = scene.animtoolbox.rotation_mode
|
||||
to_rot_mode_fcu = rot_mode_to_channel(to_rot_mode)
|
||||
|
||||
#Getting the index of the rotation mode we want to convert to
|
||||
self.to_rot_mode_index = list(scene.animtoolbox.bl_rna.properties['rotation_mode'].enum_items.keys()).index(to_rot_mode)
|
||||
#get the keyframes from the bones
|
||||
for posebone in selected_bones:
|
||||
|
||||
@@ -2548,16 +2565,21 @@ class ConvertRotationMode(bpy.types.Operator):
|
||||
|
||||
keyframes = emp.get_bone_keyframes(posebone, transform)
|
||||
|
||||
#get all interpolations and handle types of the keyframes
|
||||
handle_types = emp.get_bone_keyframes(posebone, transform, property = 'interpolation')
|
||||
interpolations = handle_types[::3]
|
||||
handle_left_type = handle_types[1::3]
|
||||
handle_right_type = handle_types[2::3]
|
||||
|
||||
# rotation_mode_keyframes = emp.get_bone_keyframes(posebone, 'rotation_mode')
|
||||
self.switch_rot_mode_keyframes(obj, posebone)
|
||||
|
||||
inbetweens = []
|
||||
smartframes = sorted(set(map(lambda x: round(x, 2), keyframes[::2])))
|
||||
inbetweens = add_inbetweens(smartframes)
|
||||
all_frames = sorted(smartframes + inbetweens)
|
||||
#get all interpolations and handle types of the keyframes
|
||||
if len(smartframes) > 1:
|
||||
handle_types = emp.get_bone_keyframes(posebone, transform, property = 'interpolation')
|
||||
interpolations = handle_types[::3]
|
||||
handle_left_type = handle_types[1::3]
|
||||
handle_right_type = handle_types[2::3]
|
||||
|
||||
inbetweens = add_inbetweens(smartframes)
|
||||
|
||||
all_frames = sorted(smartframes + inbetweens)
|
||||
new_path = posebone.path_from_id() + '.' + to_rot_mode_fcu
|
||||
|
||||
#define array length
|
||||
@@ -2632,13 +2654,15 @@ class ConvertRotationMode(bpy.types.Operator):
|
||||
keyframe = new_fcu.keyframe_points[-1]
|
||||
|
||||
keyframe.co = (frame, fcu_keyframes[new_fcu][frame])
|
||||
keyframe.interpolation = interpolations[frame_index]
|
||||
keyframe.handle_left_type = handle_left_type[frame_index]
|
||||
keyframe.handle_right_type = handle_right_type[frame_index]
|
||||
if inbetweens:
|
||||
keyframe.interpolation = interpolations[frame_index]
|
||||
keyframe.handle_left_type = handle_left_type[frame_index]
|
||||
keyframe.handle_right_type = handle_right_type[frame_index]
|
||||
|
||||
new_fcu.update()
|
||||
|
||||
add_interpolations(new_fcurves, fcu_inbetweens)
|
||||
if inbetweens:
|
||||
add_interpolations(new_fcurves, fcu_inbetweens)
|
||||
|
||||
posebone.rotation_mode = to_rot_mode
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
bl_info = {
|
||||
"name": "AnimToolBox",
|
||||
"author": "Tal Hershkovich",
|
||||
"version" : (0, 0, 7, 3),
|
||||
"version" : (0, 0, 8),
|
||||
"blender" : (3, 2, 0),
|
||||
"location": "View3D - Properties - Animation Panel",
|
||||
"description": "A set of animation tools",
|
||||
@@ -133,15 +133,16 @@ class TempCtrlsBoneSettings(bpy.types.PropertyGroup):
|
||||
setup: bpy.props.EnumProperty(name = 'Setup Type', description="Describes what kind of setup the bone is part of", override = {'LIBRARY_OVERRIDABLE'},
|
||||
items = [('NONE', 'No Setup','No Setup Applied', 0),
|
||||
('WORLDSPACE', 'World Space Ctrl','World Space Ctrl setup', 1),
|
||||
('TEMPFK', 'Temporary FK setup','Temporary FK chain setup', 2),
|
||||
('TEMPFK_FLIP', 'Temporary flipped FK setup','Temporary flipped FK chain setup', 3),
|
||||
('TEMPIK', 'Temporary IK setup','Temporary IK setup', 4),
|
||||
('POLE', 'Temporary IK Pole setup','Temporary IK Pole', 5),
|
||||
('PARENTCTRL', 'Parent Ctrl from cursor setup','Parent Ctrl from cursor setup', 6),
|
||||
('ROOT', 'Root', 'Root Ctrl for all the setups', 7),
|
||||
('EMPTY', 'Root', 'Root Ctrl for all the setups', 8),
|
||||
('TRACK_TO', 'Track To','World Space Track to Ctrl setup', 9),
|
||||
('TRACK_TO_EMPTY', 'Track To Empty','World Space Track to Empty Ctrl setup', 10)])
|
||||
('WORLDSPACE_CURSOR', 'World Space Cursor Ctrl','World Space Cursor pivot', 2),
|
||||
('TEMPFK', 'Temporary FK setup','Temporary FK chain setup', 3),
|
||||
('TEMPFK_FLIP', 'Temporary flipped FK setup','Temporary flipped FK chain setup', 4),
|
||||
('TEMPIK', 'Temporary IK setup','Temporary IK setup', 5),
|
||||
('POLE', 'Temporary IK Pole setup','Temporary IK Pole', 6),
|
||||
('PARENTCTRL', 'Parent Ctrl from cursor setup','Parent Ctrl from cursor setup', 7),
|
||||
('ROOT', 'Root', 'Root Ctrl for all the setups', 8),
|
||||
('EMPTY', 'Root', 'Root Ctrl for all the setups', 9),
|
||||
('TRACK_TO', 'Track To','World Space Track to Ctrl setup', 10),
|
||||
('TRACK_TO_EMPTY', 'Track To Empty','World Space Track to Empty Ctrl setup', 11)])
|
||||
|
||||
#using org mostly to decide if it needs a custom shape
|
||||
org: bpy.props.EnumProperty(name = 'Org Type', description="Describes what if the function of the bone", override = {'LIBRARY_OVERRIDABLE'},
|
||||
@@ -152,21 +153,29 @@ class TempCtrlsBoneSettings(bpy.types.PropertyGroup):
|
||||
|
||||
shape: bpy.props.BoolProperty(name = "Apply shape", description = "Mark if the bone needs a shape", default = False, override = {'LIBRARY_OVERRIDABLE'})
|
||||
|
||||
class TempCtrlsOrgIds(bpy.types.PropertyGroup):
|
||||
# A collection of the org ids used in each setup. Org ID is the direct connection
|
||||
# between original bones and ctrls
|
||||
pass
|
||||
|
||||
class TempCtrlsObjectSetups(bpy.types.PropertyGroup):
|
||||
#located at obj.animtoolbox.ctrl_setups
|
||||
#name: using string of the id
|
||||
setup: bpy.props.EnumProperty(name = 'Setup Type', description="Describes what kind of setup the bone is part of",
|
||||
items = [('NONE', 'No Setup','No Setup Applied', 0),
|
||||
('WORLDSPACE', 'World Space Ctrl','World Space Ctrl setup', 1),
|
||||
('TEMPFK', 'Temporary FK setup','Temporary FK chain setup', 2),
|
||||
('TEMPFK_FLIP', 'Temporary flipped FK setup','Temporary flipped FK chain setup', 3),
|
||||
('TEMPIK', 'Temporary IK setup','Temporary IK setup', 4),
|
||||
('PARENTCTRL', 'Parent Ctrl from cursor setup','Parent Ctrl from cursor setup', 5),
|
||||
('ROOT', 'Root', 'Root Ctrl for all the setups', 6),
|
||||
('EMPTY', 'Root', 'Root Ctrl for all the setups', 8),
|
||||
('TRACK_TO', 'Track To','World Space Track to Ctrl setup', 9),
|
||||
('TRACK_TO_EMPTY', 'Track To Empty','World Space Track to Empty Ctrl setup', 10),
|
||||
])
|
||||
('WORLDSPACE_CURSOR', 'World Space Cursor Ctrl','World Space Cursor pivot', 2),
|
||||
('TEMPFK', 'Temporary FK setup','Temporary FK chain setup', 3),
|
||||
('TEMPFK_FLIP', 'Temporary flipped FK setup','Temporary flipped FK chain setup', 4),
|
||||
('TEMPIK', 'Temporary IK setup','Temporary IK setup', 5),
|
||||
('POLE', 'Temporary IK Pole setup','Temporary IK Pole', 6),
|
||||
('PARENTCTRL', 'Parent Ctrl from cursor setup','Parent Ctrl from cursor setup', 7),
|
||||
('ROOT', 'Root', 'Root Ctrl for all the setups', 8),
|
||||
('EMPTY', 'Root', 'Root Ctrl for all the setups', 9),
|
||||
('TRACK_TO', 'Track To','World Space Track to Ctrl setup', 10),
|
||||
('TRACK_TO_EMPTY', 'Track To Empty','World Space Track to Empty Ctrl setup', 11)])
|
||||
|
||||
org_ids: bpy.props.CollectionProperty(type = TempCtrlsOrgIds)
|
||||
|
||||
class MultikeyProperties(bpy.types.PropertyGroup):
|
||||
|
||||
@@ -238,37 +247,6 @@ class AnimToolBoxGlobalSettings(bpy.types.PropertyGroup):
|
||||
#Blendings
|
||||
inbetweener : bpy.props.FloatProperty(name='Inbetween Keyframe', description="Adds an inbetween Keyframe between the Layer's neighbor keyframes", soft_min = -1, soft_max = 1, default=0.0, options = set(), update = Tools.add_inbetween_key, override = {'LIBRARY_OVERRIDABLE'})
|
||||
|
||||
motion_path: bpy.props.BoolProperty(name = "Motion Path", description = "Flag when Motion Path is on", default = False, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_settings: bpy.props.BoolProperty(name = "Motion Path Settings", description = "Open the settings Menu", default = False, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_keyframe_scale: bpy.props.FloatProperty(name = "Scale Selecgted Keyframes Bounding Box", description = "Change the scale of the bounding box around the selected keyframes ", default = 0.1, step = 0.1, precision = 3)
|
||||
mp_color_before: bpy.props.FloatVectorProperty(name="Motion Path Before Color", subtype='COLOR', default=(1.0, 0.0, 0.0), min=0.0, max=1.0, description="Motion path color before the current frame")
|
||||
mp_color_after: bpy.props.FloatVectorProperty(name="Motion Path After Color", subtype='COLOR', default=(0.0, 1.0, 0.0), min=0.0, max=1.0, description="Motion path color before the current frame")
|
||||
mp_infront: bpy.props.BoolProperty(name = "Motion Path In Front", description = "Display motion path in front of all the objects", default = True, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_points: bpy.props.BoolProperty(name = "Motion Path Points", description = "Display motion path points", default = True, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_lines: bpy.props.BoolProperty(name = "Motion Path Lines", description = "Display motion path lines", default = True, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_handles: bpy.props.BoolProperty(name = "Motion Path Handles", description = "Display motion path handles on keyframe selection", default = True, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_display_frames: bpy.props.BoolProperty(name = "Frame Numbers", description = "Display frame numbers on all the keyframes", default = False, override = {'LIBRARY_OVERRIDABLE'})
|
||||
mp_handle_types: bpy.props.EnumProperty(name = 'Set Keyframe Handle Type', description="Set handle type for selected keyframes", default = 'AUTO', update = emp.update_handle_type_prop,
|
||||
items = [('FREE', 'Free', 'Free', 'HANDLE_FREE', 0),
|
||||
('ALIGNED','Aligned', 'Aligned', 'HANDLE_ALIGNED', 1),
|
||||
('VECTOR', 'Vector', 'Vector', 'HANDLE_VECTOR', 2),
|
||||
('AUTO','Automatic', 'Automatic', 'HANDLE_AUTO', 3),
|
||||
('AUTO_CLAMPED','Auto Clamped', 'Auto Clamped', 'HANDLE_AUTOCLAMPED', 4)])
|
||||
|
||||
mp_interpolation: bpy.props.EnumProperty(name = 'Set Interpolation', description="Set Keyframe Interpolation", default = 'BEZIER', update = emp.update_interpolation_prop,
|
||||
items = [('BEZIER', 'Bezier', 'Bezier', 'IPO_BEZIER', 0),
|
||||
('LINEAR','Linear', 'Linear', 'IPO_LINEAR', 1),
|
||||
('CONSTANT', 'Constant', 'Constant', 'IPO_CONSTANT', 2)])
|
||||
|
||||
mp_frame_range: bpy.props.EnumProperty(name = 'Frame Range', description="Type of Frame Range", default = 'SCENE', #update = emp.mp_frame_range_update,
|
||||
items = [('KEYS_ALL', 'All_Keys','Use the Scene Frame Length for the Range', 0),
|
||||
('SCENE', 'Scene','Use the Scene Frame Length for the Range', 1),
|
||||
('MANUAL', 'Manual','Custom Frame range using numerical input or the markers frame ranger', 2),
|
||||
('AROUND', 'Around Frames','Show only around the current frame', 3)])
|
||||
mp_before: bpy.props.IntProperty(name = "Before", description = "Show the frames Before the current frame", min = 0, default = 10)
|
||||
mp_after: bpy.props.IntProperty(name = "After", description = "Show the frames After the current frame", min = 0, default = 10)
|
||||
selected_keyframes: bpy.props.StringProperty(name="Selected Keyframes", description="Serialized representation of selected keyframes")
|
||||
|
||||
gizmo_size: bpy.props.IntProperty(name = "Add to Gizmo Size", description = "Addition to Gizmo Size", max = 100, min = -100, default = 10)
|
||||
|
||||
#Copy/Pase Matrix
|
||||
@@ -312,6 +290,13 @@ class AnimToolBoxPreferences(bpy.types.AddonPreferences):
|
||||
|
||||
#Editable motion path
|
||||
keyframes_range: bpy.props.IntProperty(name = "Keyframe Range", description = "The range of distance from the keyframes while hovering over them", min = 5, max = 100, default = 15)
|
||||
mp_pref: bpy.props.BoolProperty(name = "Editable Motion Path Colors Theme", description = "Set the Color them of editable motion path visualization", default = False)
|
||||
mp_keyframe_color: bpy.props.FloatVectorProperty(name="Keyframes", subtype='COLOR', default=(1.0, 1.0, 0.0, 1.0), size=4, min=0.0, max=1.0, description="Handles selection color")
|
||||
mp_handle_color: bpy.props.FloatVectorProperty(name="Handles", subtype='COLOR', default=(1.0, 0.8, 0.2, 1.0), size=4, min=0.0, max=1.0, description="Handles selection color")
|
||||
mp_remove_color: bpy.props.FloatVectorProperty(name="Remove Keyframes", subtype='COLOR', default=(0.0, 0.5, 1.0, 1.0), size=4, min=0.0, max=1.0, description="Keyframe color displayed before removing")
|
||||
mp_hover_color: bpy.props.FloatVectorProperty(name="Hover", subtype='COLOR', default=(1.0, 0.4, 0.2, 1.0), size=4, min=0.0, max=1.0, description="Color during Hovering")
|
||||
mp_handle_selection_color: bpy.props.FloatVectorProperty(name="Handles Selection", subtype='COLOR', default=(0.8, 0.65, 0.6, 0.8), size=4, min=0.0, max=1.0, description="Handles selection color")
|
||||
mp_key_selection_color: bpy.props.FloatVectorProperty(name="Keyframe Selection", subtype='COLOR', default=(0.8, 0.8, 0.6, 0.8), size=4, min=0.0, max=1.0, description="Keyframe selection color")
|
||||
|
||||
# addon updater preferences from `__init__`, be sure to copy all of them
|
||||
auto_check_update: bpy.props.BoolProperty(
|
||||
@@ -371,6 +356,22 @@ class AnimToolBoxPreferences(bpy.types.AddonPreferences):
|
||||
row.prop(self, 'clear_setup')
|
||||
row.prop(self, 'in_front')
|
||||
|
||||
layout.separator()
|
||||
box = layout.box()
|
||||
col = box.column()
|
||||
col.prop(self, 'mp_pref', icon = 'DOWNARROW_HLT', text = 'Editable Motion Path Preferences')
|
||||
if self.mp_pref:
|
||||
col.prop(self, 'keyframes_range', text = 'Keyframe Distance Range')
|
||||
col.label(text = 'Colors Theme')
|
||||
row = box.row()
|
||||
row.prop(self, 'mp_keyframe_color')
|
||||
row.prop(self, 'mp_handle_color')
|
||||
row.prop(self, 'mp_remove_color')
|
||||
row = box.row()
|
||||
row.prop(self, 'mp_hover_color')
|
||||
row.prop(self, 'mp_key_selection_color')
|
||||
row.prop(self, 'mp_handle_selection_color')
|
||||
|
||||
layout.separator()
|
||||
col = layout.column()
|
||||
col.label(text = 'Include Extras: ')
|
||||
@@ -385,12 +386,14 @@ def loadanimtoolbox_pre(self, context):
|
||||
if scene.animtoolbox.bake_frame_range:
|
||||
scene.animtoolbox.bake_frame_range = False
|
||||
|
||||
if scene.animtoolbox.motion_path:
|
||||
scene.animtoolbox.motion_path = False
|
||||
if scene.emp.motion_path:
|
||||
scene.emp.motion_path = False
|
||||
bpy.context.workspace.status_text_set(None)
|
||||
if 'mp_dh' in dns:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(dns['mp_dh'], 'WINDOW')
|
||||
bpy.app.driver_namespace.pop('mp_dh')
|
||||
|
||||
bpy.context.scene.emp.selected_keyframes = '{}'
|
||||
|
||||
if 'markers_retimer_dh' in dns:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(dns['markers_retimer_dh'], 'WINDOW')
|
||||
bpy.app.driver_namespace.pop('markers_retimer_dh')
|
||||
@@ -411,16 +414,20 @@ def loadanimtoolbox_post(self, context):
|
||||
if Display.isolate_pose_mode not in bpy.app.handlers.depsgraph_update_pre:
|
||||
bpy.app.handlers.depsgraph_update_pre.append(Display.isolate_pose_mode)
|
||||
|
||||
if scene.animtoolbox.motion_path:
|
||||
scene.animtoolbox.motion_path = False
|
||||
if scene.emp.motion_path:
|
||||
scene.emp.motion_path = False
|
||||
bpy.context.workspace.status_text_set(None)
|
||||
if 'mp_dh' in dns:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(dns['mp_dh'], 'WINDOW')
|
||||
bpy.app.driver_namespace.pop('mp_dh')
|
||||
# Reset keyframe selection for motion paths it is not used in window manager
|
||||
# Because it is used for undo
|
||||
bpy.context.scene.emp.selected_keyframes = '{}'
|
||||
|
||||
Tools.selection_order(self, context)
|
||||
|
||||
classes = (TempCtrlsItems, TempCtrlsObjectSetups, TempCtrlsSceneSettings,TempCtrlsBoneSettings, MultikeyProperties, IsolatedRigs,
|
||||
AnimToolBoxObjectSettings, AnimToolBoxUILayout, AnimToolBoxGlobalSettings) + ui.classes
|
||||
classes = (TempCtrlsItems, TempCtrlsOrgIds, TempCtrlsObjectSetups, TempCtrlsSceneSettings,TempCtrlsBoneSettings, MultikeyProperties,
|
||||
IsolatedRigs, AnimToolBoxObjectSettings, AnimToolBoxUILayout, AnimToolBoxGlobalSettings) + ui.classes
|
||||
|
||||
addon_keymaps = []
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_check": "2025-06-19 15:30:39.178174",
|
||||
"backup_date": "June-19-2025",
|
||||
"last_check": "2025-09-23 10:58:29.201165",
|
||||
"backup_date": "September-23-2025",
|
||||
"update_ready": false,
|
||||
"ignore": false,
|
||||
"just_restored": false,
|
||||
|
||||
@@ -2101,6 +2101,9 @@ def smartbake(context, org_action, posebones, ids):
|
||||
for fcu in fcurves:
|
||||
if path not in fcu.data_path:
|
||||
continue
|
||||
#apply the bake only to the transformation channels
|
||||
if fcu.data_path.split('"].')[-1] not in transformations:
|
||||
continue
|
||||
if Tools.filter_properties(context.scene.animtoolbox, fcu):
|
||||
continue
|
||||
if posebone.btc.setup in {'TRACK_TO', 'TRACK_TO_EMPTY'} and 'rotation' not in fcu.data_path:
|
||||
@@ -2108,6 +2111,8 @@ def smartbake(context, org_action, posebones, ids):
|
||||
if scene.btc.from_origin:
|
||||
smartfcurve = get_smartfcurve(fcu, smartfcus, posebone, fcu.data_path, fcu.array_index, scene.btc.smartbake)
|
||||
|
||||
if smartfcurve is None:
|
||||
continue
|
||||
#if it's not the original action then create a new fcurve in org_action where it's baked to and assign it to smartfcurve
|
||||
#Usefull especially when working inside a new empty layer
|
||||
if action != org_action and (fcu.data_path, fcu.array_index) not in org_action_fcus:
|
||||
@@ -2201,7 +2206,7 @@ def smartbake(context, org_action, posebones, ids):
|
||||
smartfcurve.modifiers.append(mod)
|
||||
|
||||
#Remove frames to not include in the bake, in case of custom frame range
|
||||
if frames_remove:
|
||||
if frames_remove and smartfcurve:
|
||||
smartframes = {frame for frame in smartframes if round(frame) not in frames_remove}
|
||||
smartfcurve.frames = [frame for frame in smartfcurve.frames if round(frame) not in frames_remove]
|
||||
smartfcurve.interpolations = {frame : interpolation for frame, interpolation in smartfcurve.interpolations.items() if round(frame) not in frames_remove}
|
||||
@@ -2367,6 +2372,7 @@ def remove_constraints(context, ids, controllers, controlled_objs):
|
||||
ctrls = list(controllers) + [ctrl.children[0] for ctrl in controllers if ctrl.children]
|
||||
|
||||
scene = context.scene
|
||||
|
||||
for obj in controlled_objs:
|
||||
if obj.type != 'ARMATURE':
|
||||
continue
|
||||
@@ -2378,16 +2384,17 @@ def remove_constraints(context, ids, controllers, controlled_objs):
|
||||
#check if the bone has anything assigned to it
|
||||
if not bone.btc.setup_id:
|
||||
continue
|
||||
|
||||
#reset the setup_id
|
||||
bone.btc.setup_id = 0
|
||||
bone.btc.org_id = 0
|
||||
bone.btc.org = bone.btc.setup ='NONE'
|
||||
|
||||
for con in bone.constraints:
|
||||
if not hasattr(con, 'target'):
|
||||
continue
|
||||
if con.target not in ctrls:
|
||||
continue
|
||||
|
||||
#reset the setup_id
|
||||
bone.btc.setup_id = 0
|
||||
bone.btc.org_id = 0
|
||||
bone.btc.org = bone.btc.setup ='NONE'
|
||||
con_path = con.path_from_id()
|
||||
#Removing target because of a bug in Blender 4.4, otherwise it crashes when switching mode
|
||||
con.target = None
|
||||
@@ -2481,6 +2488,13 @@ def get_controllers_controlled(context):
|
||||
controlled_objs = {item.controlled for item in scene.btc.ctrl_items if item.controlled}
|
||||
controllers = {item.controller for item in scene.btc.ctrl_items if item.controller}
|
||||
|
||||
#In case controlled objs are not found and there are still bones with ids
|
||||
#The add the object of this bones to reset them
|
||||
if not controlled_objs and context.selected_pose_bones:
|
||||
for bone in context.selected_pose_bones:
|
||||
if bone.btc.setup_id or bone.btc.org_id:
|
||||
controlled_objs.add(bone.id_data)
|
||||
|
||||
return controllers, controlled_objs
|
||||
|
||||
def unhide_org(obj):
|
||||
@@ -2652,9 +2666,9 @@ def get_bone_ids(context, id = 'setup_id'):
|
||||
|
||||
if select_filter == 'ALL':
|
||||
#get ids from All the controller rigs and bones
|
||||
# org_ids = {bone.btc.org_id for obj in scene.objects if obj.animtoolbox.controlled for bone in obj.pose.bones if bone.btc.org_id}
|
||||
for obj in scene.objects:
|
||||
if not obj.animtoolbox.controlled:
|
||||
if not obj.animtoolbox.controlled and obj not in context.selected_objects:
|
||||
#still checking selected objects in case animtoolbox.controlled is empty
|
||||
continue
|
||||
if obj.type == 'ARMATURE':
|
||||
for bone in obj.pose.bones:
|
||||
@@ -2663,11 +2677,6 @@ def get_bone_ids(context, id = 'setup_id'):
|
||||
elif obj.type == 'EMPTY' and id == 'setup_id':
|
||||
if getattr(obj.animtoolbox, id):
|
||||
ids.add(getattr(obj.animtoolbox, id))
|
||||
# ids = {getattr(bone.btc, id) for obj in scene.objects if obj.animtoolbox.controlled and obj.type == 'ARMATURE'
|
||||
# for bone in obj.pose.bones if getattr(bone.btc, id)}
|
||||
# if id == 'setup_id':
|
||||
# ids = {getattr(obj.animtoolbox, id) for obj in scene.objects
|
||||
# if obj.animtoolbox.controlled and getattr(obj.animtoolbox, id)}
|
||||
|
||||
return ids
|
||||
|
||||
@@ -2814,14 +2823,7 @@ def rebake_connection_ctrls(self, context, obj, rebake_bones, parent, root_name
|
||||
|
||||
# constraints_clear(ctrls)
|
||||
|
||||
#remove extra channels from the temporary ctrls before removing them
|
||||
action = obj.animation_data.action
|
||||
ctrl_paths = [ctrl.path_from_id() for ctrl in ctrls]
|
||||
fcurves = Tools.get_fcurves_channelbag(obj, action)
|
||||
for fcu in fcurves:
|
||||
path = fcu.data_path.split('"].')[0] + ('"]')
|
||||
if path in ctrl_paths:
|
||||
fcurves.remove(fcu)
|
||||
remove_tempctrls_channels(obj, ctrls)
|
||||
|
||||
#removing the temporary ctrls
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
@@ -2840,6 +2842,22 @@ def rebake_connection_ctrls(self, context, obj, rebake_bones, parent, root_name
|
||||
|
||||
return
|
||||
|
||||
def remove_tempctrls_channels(obj, ctrls):
|
||||
|
||||
#remove extra channels from the temporary ctrls before removing them
|
||||
if not obj.animation_data:
|
||||
return
|
||||
if not obj.animation_data.action:
|
||||
return
|
||||
|
||||
action = obj.animation_data.action
|
||||
ctrl_paths = [ctrl.path_from_id() for ctrl in ctrls]
|
||||
fcurves = Tools.get_fcurves_channelbag(obj, action)
|
||||
for fcu in fcurves:
|
||||
path = fcu.data_path.split('"].')[0] + ('"]')
|
||||
if path in ctrl_paths:
|
||||
fcurves.remove(fcu)
|
||||
|
||||
def get_rebake_bones(rig, ids):
|
||||
#get all the bones with connection (Ctrls and their children)
|
||||
rebake_bones = []
|
||||
@@ -2967,8 +2985,8 @@ class Cleanup(bpy.types.Operator):
|
||||
scene = context.scene
|
||||
|
||||
ids = get_bone_ids(context)
|
||||
|
||||
controllers, controlled_objs = get_controllers_controlled(context)
|
||||
|
||||
if not scene.btc.clean_constraints:
|
||||
return {'FINISHED'}
|
||||
#in case the controllers are empties add their ids and if the root is selected
|
||||
@@ -2977,7 +2995,6 @@ class Cleanup(bpy.types.Operator):
|
||||
continue
|
||||
ids.add(ctrl.animtoolbox.setup_id)
|
||||
controllers_remove_child(ctrl, controllers)
|
||||
|
||||
remove_constraints(context, ids, controllers, controlled_objs)
|
||||
|
||||
if not scene.btc.clean_ctrls:
|
||||
|
||||
@@ -1573,7 +1573,8 @@ class PasteMatrix(bpy.types.Operator):
|
||||
if bone.id_data.name in objs_matrix:
|
||||
if bone.name in objs_matrix[bone.id_data.name]:
|
||||
matrix_copied = objs_matrix[bone.id_data.name][bone.name]
|
||||
|
||||
|
||||
matrix_copied = obj.matrix_world.inverted() @ matrix_copied
|
||||
#Store the matrices for each bone that will use it
|
||||
matrix_copied = reverse_childof_constraint(bone, matrix_copied, constrained)
|
||||
|
||||
@@ -1662,11 +1663,11 @@ class CopyRelativeMatrix(bpy.types.Operator):
|
||||
#create a dictionary for all the realtive distance of the bones and objects
|
||||
objs_matrix_dist = dict()
|
||||
|
||||
#get the source bofne or object
|
||||
#get the source bone or object
|
||||
if context.active_pose_bone:
|
||||
source_active = context.active_pose_bone
|
||||
source_rig_name = source_active.id_data.name
|
||||
source_matrix = source_active.matrix
|
||||
source_matrix = source_active.matrix
|
||||
else:
|
||||
source_active = context.active_object
|
||||
source_matrix = source_active.matrix_world
|
||||
@@ -1681,8 +1682,14 @@ class CopyRelativeMatrix(bpy.types.Operator):
|
||||
continue
|
||||
if bone_relative == source_active:
|
||||
continue
|
||||
matrix_dist = source_matrix.inverted() @ obj.matrix_world @ bone_relative.matrix# @ obj.matrix_world
|
||||
|
||||
#Adding the offset from the armature transform both for the active and relative
|
||||
rig_offset = obj.matrix_world
|
||||
if source_active.id_data.type == 'ARMATURE':
|
||||
rig_offset = source_active.id_data.matrix_world.inverted() @ rig_offset
|
||||
|
||||
matrix_dist = source_matrix.inverted() @ rig_offset @ bone_relative.matrix
|
||||
|
||||
#store each bone matrix distance in a dictionary
|
||||
if obj.name in objs_matrix_dist:
|
||||
objs_matrix_dist[obj.name].update({bone_relative.name : matrix_dist})
|
||||
@@ -1735,41 +1742,59 @@ def reverse_childof_constraint(source, matrix_source, constrained = set()):
|
||||
offsets_lerp = []
|
||||
offset_inv = Matrix.Identity(4)
|
||||
offset_inv_lerp = Matrix.Identity(4)
|
||||
|
||||
#If the source is a bone then get the Armature matrix to add to the calculation
|
||||
if type(source) == bpy.types.PoseBone:
|
||||
obj_offset = obj.matrix_world
|
||||
else:
|
||||
obj_offset = Matrix.Identity(4)
|
||||
|
||||
#iterate and store all the inverted offsets of all the child constraints
|
||||
for con in source.constraints:
|
||||
if con.mute or not con.influence:
|
||||
continue
|
||||
if hasattr(con, 'target') and con.target is None:
|
||||
continue
|
||||
if con.type != 'CHILD_OF':
|
||||
constrained.add(source)
|
||||
continue
|
||||
parent_matrix = con.target.matrix_world
|
||||
if con.subtarget != '':
|
||||
if con.subtarget == '':
|
||||
parent_matrix = obj_offset.inverted() @ con.target.matrix_world
|
||||
#remove obj.matrix_world when connected to an object
|
||||
offset = parent_matrix @ con.inverse_matrix - Matrix.Identity(4)
|
||||
#offset for the scale with influence already included
|
||||
offset_lerp = Matrix.Identity(4).lerp(parent_matrix @ con.inverse_matrix, con.influence)
|
||||
else:
|
||||
|
||||
parent_matrix = con.target.pose.bones[con.subtarget].matrix
|
||||
offsets.append(Matrix.Identity(4) + con.influence * (parent_matrix @ con.inverse_matrix - Matrix.Identity(4)))
|
||||
offsets_lerp.append(Matrix.Identity(4).lerp(parent_matrix @ con.inverse_matrix, con.influence))
|
||||
|
||||
if con.target != obj:
|
||||
parent_matrix = obj_offset.inverted() @ con.target.matrix_world @ parent_matrix
|
||||
|
||||
#Include armature object matrix
|
||||
offset = parent_matrix @ con.inverse_matrix - Matrix.Identity(4)
|
||||
offset_lerp = Matrix.Identity(4).lerp(parent_matrix @ con.inverse_matrix, con.influence)
|
||||
|
||||
#Adding the influence to the offset
|
||||
offset = Matrix.Identity(4) + con.influence * offset
|
||||
|
||||
offset_inv = offset_inv @ offset.inverted()
|
||||
offset_inv_lerp = offset_inv_lerp @ offset_lerp.inverted()
|
||||
|
||||
offsets.append(offset)
|
||||
|
||||
if not offsets:
|
||||
return matrix_source #@ obj.matrix_world.inverted()
|
||||
return matrix_source
|
||||
|
||||
#Multiply all the child constraint inverted offsets
|
||||
for offset, offsets_lerp in zip(offsets, offsets_lerp):
|
||||
#offset_inv = offset_inv @ parent_offset_inv
|
||||
offset_inv = offset_inv @ offset.inverted()
|
||||
offset_inv_lerp = offset_inv_lerp @ offsets_lerp.inverted()
|
||||
# add object space if it's a bone
|
||||
if source != obj:
|
||||
offset_inv = offset_inv #@ obj.matrix_world.inverted()
|
||||
|
||||
#final Matrix values
|
||||
matrix_basis = offset_inv @ matrix_source
|
||||
matrix_lerp = offset_inv_lerp @ matrix_source
|
||||
matrix_lerp = offset_inv_lerp @ matrix_source
|
||||
loc, rot, scale = matrix_basis.decompose()
|
||||
loc_lerp, rot_lerp, scale_lerp = matrix_lerp.decompose()
|
||||
|
||||
matrix_basis = Matrix.LocRotScale(loc, rot_lerp, scale_lerp)
|
||||
|
||||
return matrix_basis
|
||||
return matrix_basis
|
||||
|
||||
def reorder_bones_matrices(bones_matrices, constrained):
|
||||
#Reordering the bones, so that we apply first the matrix offset to the constrained bones
|
||||
@@ -1837,9 +1862,11 @@ class PasteRelativeMatrix(bpy.types.Operator):
|
||||
if 'source_rig_name' in globals():
|
||||
if source_rig_name in bpy.data.objects:
|
||||
source_rig = bpy.data.objects[source_rig_name]
|
||||
source_obj = source_rig
|
||||
source_bone = source_rig.pose.bones[source_active_name]
|
||||
#Get the current matrix of the source bone
|
||||
matrix_source = source_bone.matrix
|
||||
|
||||
elif 'source_active_name' in globals():
|
||||
if source_active_name in bpy.data.objects:
|
||||
source_obj = bpy.data.objects[source_active_name]
|
||||
@@ -1854,10 +1881,14 @@ class PasteRelativeMatrix(bpy.types.Operator):
|
||||
#if the source object was in object mode during copy and now it's pose mode then quit
|
||||
frame_range, inbetweens = get_frame_range(context, obj)
|
||||
fcu_inbetweens = dict()
|
||||
|
||||
|
||||
for frame in sorted(frame_range+inbetweens):
|
||||
scene.frame_set(int(frame))
|
||||
if obj.mode == 'POSE':
|
||||
for bone in context.selected_pose_bones:
|
||||
if bone.id_data != obj:
|
||||
continue
|
||||
#check that the selected bone is not the source bone
|
||||
if bone == source_bone:
|
||||
continue
|
||||
@@ -1868,14 +1899,18 @@ class PasteRelativeMatrix(bpy.types.Operator):
|
||||
if bone.name in objs_matrix_dist[bone.id_data.name]:
|
||||
bone_matrix_dist = objs_matrix_dist[bone.id_data.name][bone.name]
|
||||
|
||||
matrix_new = matrix_source @ bone_matrix_dist
|
||||
#Adding the offset from the armature transform both for the active and relative
|
||||
#If it's the same Armature it will cancel each other
|
||||
rig_offset = obj.matrix_world.inverted()
|
||||
if source_rig:
|
||||
rig_offset = rig_offset @ source_rig.matrix_world
|
||||
matrix_new = rig_offset @ matrix_source @ bone_matrix_dist
|
||||
|
||||
#Store the matrices for each bone that will use it
|
||||
matrix_new = reverse_childof_constraint(bone, matrix_new, constrained)
|
||||
|
||||
bones_matrices.update({bone : matrix_new})
|
||||
|
||||
matrix_copied = reverse_childof_constraint(bone, matrix_new, constrained)
|
||||
# if bone not in constrained:
|
||||
# matrix_copied = filter_matrix_properties(context, bone.matrix, matrix_copied)
|
||||
|
||||
|
||||
#Reordering the bones, so that we apply first the matrix offset to the constrained bones
|
||||
bones_matrices = reorder_bones_matrices(bones_matrices, constrained)
|
||||
|
||||
@@ -1896,16 +1931,16 @@ class PasteRelativeMatrix(bpy.types.Operator):
|
||||
else:
|
||||
obj_matrix_dist = matrix_dist
|
||||
|
||||
matrix_new = matrix_source @ obj_matrix_dist
|
||||
matrix_copied = reverse_childof_constraint(target, matrix_new)
|
||||
matrix_new = matrix_source @ obj_matrix_dist
|
||||
matrix_new = reverse_childof_constraint(target, matrix_new)
|
||||
if target not in constrained:
|
||||
matrix_copied = filter_matrix_properties(context, target.matrix_world, matrix_copied)
|
||||
target.matrix_world = matrix_copied
|
||||
matrix_new = filter_matrix_properties(context, target.matrix_world, matrix_new)
|
||||
target.matrix_world = matrix_new
|
||||
|
||||
if target in constrained:
|
||||
context.view_layer.update()
|
||||
matrix_copied = reverse_constraint_offset(target.matrix_world, matrix_copied)
|
||||
target.matrix_world = filter_matrix_properties(context, target.matrix_world, matrix_copied)
|
||||
matrix_new = reverse_constraint_offset(target.matrix_world, matrix_new)
|
||||
target.matrix_world = filter_matrix_properties(context, target.matrix_world, matrix_new)
|
||||
|
||||
paste_keyframes_get_inbetweens(scene, target, inbetweens, frame, frame_range, fcu_inbetweens)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
bl_info = {
|
||||
"name": "AnimToolBox",
|
||||
"author": "Tal Hershkovich",
|
||||
"version" : (0, 0, 7, 1),
|
||||
"version" : (0, 0, 7, 3),
|
||||
"blender" : (3, 2, 0),
|
||||
"location": "View3D - Properties - Animation Panel",
|
||||
"description": "A set of animation tools",
|
||||
|
||||
+4
-5
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"last_check": "2025-06-19 15:30:39.178174",
|
||||
"backup_date": "",
|
||||
"last_check": "2025-09-23 10:57:14.918240",
|
||||
"backup_date": "June-19-2025",
|
||||
"update_ready": true,
|
||||
"ignore": false,
|
||||
"just_restored": false,
|
||||
"just_updated": false,
|
||||
"version_text": {
|
||||
"link": "https://gitlab.com/api/v4/projects/45739913/repository/archive.zip?sha=198933935077e89aa87550163d3747388f2552e8",
|
||||
"link": "https://gitlab.com/api/v4/projects/45739913/repository/archive.zip?sha=cb445f00491a54eb763f0d8e72eaae3e44e7d0ba",
|
||||
"version": [
|
||||
0,
|
||||
0,
|
||||
7,
|
||||
3
|
||||
8
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
+1149
-477
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@ def attr_default(obj, fcu_key):
|
||||
if attr not in obj.data.shape_keys.key_blocks:
|
||||
return [0]
|
||||
shapekey = obj.data.shape_keys.key_blocks[attr]
|
||||
return 0 if shapekey.slider_min <= 0 else shapekey.slider_min
|
||||
return [0] if shapekey.slider_min <= 0 else shapekey.slider_min
|
||||
#in case of transforms in object mode
|
||||
else:# fcu_key[0] in transform_types:
|
||||
source = obj
|
||||
@@ -273,6 +273,9 @@ def random_value(self, context):
|
||||
for key in fcu.keyframe_points:
|
||||
add_value(key, value * random.uniform(-threshold, threshold))
|
||||
fcu.update()
|
||||
|
||||
self['randomness'] = 0.1
|
||||
|
||||
|
||||
def evaluate_combine(data_path, added_array, eval_array, array_default, influence):
|
||||
|
||||
@@ -293,23 +296,17 @@ def evaluate_array(fcurves, fcu_path, frame, array_default = [0, 0, 0]):
|
||||
'''Create an array from all the indexes'''
|
||||
|
||||
array_len = len(array_default)
|
||||
fcu_array = []
|
||||
|
||||
#assigning the default array in case
|
||||
fcu_array = array_default.copy()
|
||||
#get the missing arrays in case quaternion is not complete
|
||||
missing_arrays = []
|
||||
for i in range(array_len):
|
||||
fcu = fcurves.find(fcu_path, index = i)
|
||||
if fcu is None:
|
||||
missing_arrays.append(i)
|
||||
continue
|
||||
fcu_array[i] = fcu.evaluate(frame)
|
||||
|
||||
fcu_array.append(fcu.evaluate(frame))
|
||||
|
||||
#In case it's a quaternion and missing attributes, then adding from default value
|
||||
if fcu_array and array_len == 4 and missing_arrays:
|
||||
for i in missing_arrays:
|
||||
fcu_array.insert(i, array_default[i])
|
||||
|
||||
if not len(fcu_array):
|
||||
if (fcu_array == array_default).all():
|
||||
return None
|
||||
return np.array(fcu_array)
|
||||
|
||||
@@ -325,7 +322,7 @@ def evaluate_layers(context, obj, anim_data, fcu, array_default):
|
||||
blend_types = {'ADD' : '+', 'SUBTRACT' : '-', 'MULTIPLY' : '*'}
|
||||
fcu_path = fcu.data_path
|
||||
|
||||
eval_array = array_default
|
||||
eval_array = array_default.copy()
|
||||
|
||||
for track in nla_tracks:
|
||||
if track.mute:
|
||||
@@ -373,6 +370,7 @@ def evaluate_layers(context, obj, anim_data, fcu, array_default):
|
||||
tweak_mode = anim_data.use_tweak_mode
|
||||
if tweak_mode:
|
||||
anim_data.use_tweak_mode = False
|
||||
|
||||
action = anim_data.action
|
||||
if action:
|
||||
influence = anim_data.action_influence
|
||||
@@ -425,6 +423,7 @@ def evaluate_value(self, context):
|
||||
for fcu in fcurves:
|
||||
if fcu in fcu_paths:
|
||||
continue
|
||||
current_value = None
|
||||
if Tools.filter_properties(context.scene.animtoolbox, fcu):
|
||||
continue
|
||||
if obj.mode == 'POSE':
|
||||
@@ -447,10 +446,11 @@ def evaluate_value(self, context):
|
||||
else:
|
||||
transform = fcu.data_path
|
||||
current_value = getattr(obj, transform)
|
||||
|
||||
#In case it was completly filtered out and not current value available
|
||||
if not current_value:
|
||||
continue
|
||||
|
||||
array_default = np.array(attr_default(obj, (fcu.data_path, fcu.array_index)))
|
||||
# array_default = np.array([attr_default(obj, (fcu.data_path, i)) for i in range(4)
|
||||
# if fcurves.find(fcu.data_path, index = i) is not None])
|
||||
eval_array = evaluate_layers(context, obj, anim_data, fcu, array_default)
|
||||
if eval_array is None:
|
||||
fcurves = Tools.get_fcurves_channelbag(obj, action)
|
||||
|
||||
@@ -63,6 +63,7 @@ class ANIMTOOLBOX_MT_Temp_Ctrls(bpy.types.Menu):
|
||||
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()
|
||||
@@ -163,7 +164,7 @@ class ANIMTOOLBOX_MT_operators(bpy.types.Menu):
|
||||
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.animtoolbox.motion_path, icon_value = custom_icons["mt"].icon_id)
|
||||
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')
|
||||
@@ -257,7 +258,10 @@ class TEMPCTRLS_PT_Panel(ANIMTOOLBOX_PT_Panel, bpy.types.Panel):
|
||||
layout = self.layout
|
||||
box = layout.box()
|
||||
col = box.column()
|
||||
col.operator('anim.bake_to_ctrl', text="WorldSpace Ctrls", icon ='WORLD') #
|
||||
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')
|
||||
|
||||
@@ -540,44 +544,65 @@ class ANIMTOOLBOX_PT_Display(ANIMTOOLBOX_PT_Panel, bpy.types.Panel):
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.operator('object.motion_path_operator', text = 'Editable Motion Path', depress = scene.animtoolbox.motion_path, icon_value = custom_icons["mt"].icon_id)
|
||||
row.prop(scene.animtoolbox, 'mp_settings', text = '', icon = 'SETTINGS')
|
||||
if scene.animtoolbox.mp_settings:
|
||||
row = box.row()
|
||||
row.prop(scene.animtoolbox, 'mp_color_before', text = '')
|
||||
row.prop(scene.animtoolbox, 'mp_color_after', text = '')
|
||||
row = box.row()
|
||||
row.prop(scene.animtoolbox, 'mp_keyframe_scale', text = 'Scale Keyframes Box', icon = 'CUBE')
|
||||
row = box.row()
|
||||
row.prop(scene.animtoolbox, 'mp_points', text = 'Points')
|
||||
row.prop(scene.animtoolbox, 'mp_lines', text = 'Lines')
|
||||
row = box.row()
|
||||
row.prop(scene.animtoolbox, 'mp_handles', text = 'Handles')
|
||||
row.prop(scene.animtoolbox, 'mp_infront', text = 'In Front')
|
||||
row = box.row()
|
||||
row.prop(scene.animtoolbox, 'mp_display_frames', text = 'Display Keyframe Numbers')
|
||||
row = box.row()
|
||||
row.prop(context.preferences.addons[__package__].preferences, 'keyframes_range', text = 'Keyframe Distance Range')
|
||||
if context.object is None:
|
||||
return
|
||||
|
||||
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(context.scene.animtoolbox, 'mp_frame_range', text = '')
|
||||
if context.scene.animtoolbox.mp_frame_range == 'MANUAL':
|
||||
split.prop(emp, 'frame_range', text = '')
|
||||
if emp.frame_range == 'MANUAL':
|
||||
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 = scene.animtoolbox.bake_frame_range)
|
||||
|
||||
elif context.scene.animtoolbox.mp_frame_range == 'AROUND':
|
||||
row = box.row()
|
||||
row.prop(context.scene.animtoolbox, 'mp_before', text = 'Before')
|
||||
row.prop(context.scene.animtoolbox, 'mp_after', text = 'After')
|
||||
row.prop(emp, 'frame_start', text = 'Start')
|
||||
row.prop(emp, 'frame_end', text = 'End')
|
||||
# row.operator("anim.markers_bakerange", icon = 'MARKER', text ='', depress = scene.animtoolbox.bake_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()
|
||||
row = box.row()
|
||||
row.operator('anim.go_to_keyframe')
|
||||
# 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"
|
||||
@@ -697,4 +722,4 @@ def draw_menu(self, context):
|
||||
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.animtoolbox.motion_path, icon_value = custom_icons["mt"].icon_id)
|
||||
layout.operator('object.motion_path_operator', text = '', depress = scene.emp.motion_path, icon_value = custom_icons["mt"].icon_id)
|
||||
Reference in New Issue
Block a user