work: restore shift+spacebar for media play/pause

maybe put in maya config? idk what funiman's preference is
This commit is contained in:
Nathan
2026-05-29 14:58:59 -06:00
parent 2f8e5f472f
commit 6c3b78075b
130 changed files with 10461 additions and 19696 deletions
+23 -2
View File
@@ -20,7 +20,7 @@
bl_info = {
"name": "Animation Layers",
"author": "Tal Hershkovich",
"version" : (2, 4, 0),
"version" : (2, 4, 1),
"blender" : (3, 2, 0),
"location": "View3D - Properties - Animation Panel",
"description": "Simplifying the NLA editor into an animation layers UI and workflow",
@@ -73,7 +73,10 @@ class AnimLayersSceneSettings(bpy.types.PropertyGroup):
class AnimLayersSettings(bpy.types.PropertyGroup):
turn_on: bpy.props.BoolProperty(name="Turn Animation Layers On", description="Turn on and start Animation Layers", default=False, options={'HIDDEN'}, update = anim_layers.turn_animlayers_on, override = {'LIBRARY_OVERRIDABLE'})
# Active row in obj.Anim_Layers. Post-migration, 1:1 with NLA-track index.
layer_index: bpy.props.IntProperty(update = anim_layers.update_layer_index, options={'LIBRARY_EDITABLE'}, default = 0, override = {'LIBRARY_OVERRIDABLE'})
# Schema version. 0 = pre-hierarchical (legacy GROUP rows); 1 = hierarchical (parent_layer refs).
schema_version: bpy.props.IntProperty(name="Schema Version", default=0, options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
linked: bpy.props.BoolProperty(name="Linked", description="Duplicate a layer with a linked action", default=False, options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
#Bake settings
@@ -125,13 +128,31 @@ class AnimLayersItems(bpy.types.PropertyGroup):
action_range: bpy.props.FloatVectorProperty(name='action range', description="used to check if layer needs to update frame range", override = {'LIBRARY_OVERRIDABLE'}, size = 2)
custom_frame_range: bpy.props.BoolProperty(name="Custom Frame Range", description="Use a custom frame range per layer instead of the scene frame range", default=False, options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_frame_range)
frame_start: bpy.props.FloatProperty(name='Action Start Frame', description="First frame of the layer's action",min = 0, default=0, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_frame_start)
frame_end: bpy.props.FloatProperty(name='Action End Frame', description="End frame of the layer's action", default=0, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_frame_end)
speed: bpy.props.FloatProperty(name='Speed of the action', description="Speed of the action strip", default = 1, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_speed)
offset: bpy.props.FloatProperty(name='Offset when the action starts', description="Offseting the whole layer animation", default = 0, precision = 2, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_offset)
repeat: bpy.props.FloatProperty(name="Repeat", description="Repeat the action", min = 0.1, default = 1, options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'}, update = anim_layers.layer_repeat)
# Hierarchical layer fields. Every layer is NLA-backed; a layer is a
# "group" iff something else points to it via parent_layer.
expanded: bpy.props.BoolProperty(name="Expanded", description="Show this layer's children in the UI list", default=True, options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
parent_layer: bpy.props.StringProperty(name="Parent Layer", description="Name of this layer's parent (empty = root)", default="", options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
group_color: bpy.props.FloatVectorProperty(name="Group Color", subtype='COLOR', size=4, min=0.0, max=1.0, default=(0.3, 0.5, 0.8, 1.0), options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
# Dynamic-enum dropdown for picking parent_layer in the UI. The setter
# refuses cycles (self + descendants are excluded from the options).
assigned_group: bpy.props.EnumProperty(name='Parent', description='Set this layer\'s parent (None = root)',
items=anim_layers.layer_group_enum_items, get=anim_layers.layer_group_get, set=anim_layers.layer_group_set,
options={'HIDDEN'})
# Legacy field for pre-migration data. Read by migrate_object_to_hierarchical
# and ignored thereafter. Schema retained so old .blend files load cleanly.
type: bpy.props.EnumProperty(name="Item Type (legacy)", default='LAYER',
items=[('LAYER', 'Layer', 'NLA-backed layer'),
('GROUP', 'Group', 'Legacy phantom group row')],
options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
parent_group: bpy.props.StringProperty(name="Parent Group (legacy)", description="Pre-migration field. Read once by the schema-v1 migration, then unused.", default="", options={'HIDDEN'}, override = {'LIBRARY_OVERRIDABLE'})
class AnimLayersObjects(bpy.types.PropertyGroup):
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
{
"last_check": "2026-04-21 12:47:39.126086",
"backup_date": "April-21-2026",
"last_check": "2026-05-27 14:41:11.263249",
"backup_date": "May-27-2026",
"update_ready": false,
"ignore": false,
"just_restored": false,
@@ -20,7 +20,7 @@
bl_info = {
"name": "Animation Layers",
"author": "Tal Hershkovich",
"version" : (2, 3, 8),
"version" : (2, 4, 0),
"blender" : (3, 2, 0),
"location": "View3D - Properties - Animation Panel",
"description": "Simplifying the NLA editor into an animation layers UI and workflow",
@@ -153,7 +153,6 @@ def update_panel(self, context):
bpy.utils.unregister_class(panel)
for panel in panels:
#print (panel.bl_category)
panel.bl_category = context.preferences.addons[__name__].preferences.category
bpy.utils.register_class(panel)
@@ -176,8 +175,9 @@ class AnimLayersAddonPreferences(bpy.types.AddonPreferences):
items = [('ANIMLAYERS', 'Anim Layers Settings', 'Use Anim Layers properties to adjust custom frame range'),
('NLA', 'NLA Settings', 'Use the nla properties to adjust custom frame range')])
lock_nlatracks: bpy.props.BoolProperty(name="Automatically lock the nla tracks for safety measures", description="Automatically lock nla tracks when creating layers for safety", default = True)
lock_nlatracks: bpy.props.BoolProperty(name="Automatically lock the NLA tracks", description="Automatically lock nla tracks when creating layers for safety", default = True)
auto_custom_range: bpy.props.BoolProperty(name="Switch automatically to custom frame range when editing NLA Strips", description="Automatically use custom frame range when adjusting NLA Strips manually", default = False)
#Property for ClearActiveAction
proceed: bpy.props.EnumProperty(name="Choose how to proceed", description="Select an option how to proceed with Anim Layers", override = {'LIBRARY_OVERRIDABLE'},
items = [
@@ -260,8 +260,9 @@ class AnimLayersAddonPreferences(bpy.types.AddonPreferences):
row.label(text = "Custom Frame Range Settings")
row.prop(self, "frame_range_settings", text = '')
col.prop(self, "lock_nlatracks")
row = col.row()
row.prop(self, "auto_custom_range")
row.prop(self, "lock_nlatracks")
classes = (AnimLayersSettings, AnimLayersSceneSettings, AnimLayersItems, AnimLayersObjects)
@@ -399,7 +399,7 @@ def get_fcu_layer_keyframes(obj, context, track):
keyframes = []
# fcurves = get_fcurves(track.strips[0].action)
# fcurves = track.strips[0].action.fcurves
fcurves = get_fcurves(obj, track.strips[0].action)
fcurves = get_fcurves(obj, track.strips[0].action, obj.als.data_type)
#store all the keyframe locations from the fcurves of the layer
for fcu in fcurves:
if fcu.group is not None:
@@ -1546,6 +1546,8 @@ def strip_action_recalc(self, strip):
###################################################### HELPER FUNCTIONS ################################################
def redraw_areas(areas):
if not len(bpy.context.window_manager.windows):
return
for area in bpy.context.window_manager.windows[0].screen.areas:
if area.type in areas:
area.tag_redraw()
@@ -1621,7 +1623,7 @@ def select_layer_bones(self, context):
###################################################### CLASSES ###########################################################
class SelectBonesInLayer(bpy.types.Operator):
"""Select bones with keyframes in the current layer"""
"""Select bones with keyframes in the current layer, use shift to add to the current selection"""
bl_idname = "anim.bones_in_layer"
bl_label = "Select layer bones"
bl_icon = "BONE_DATA"
@@ -1864,18 +1866,9 @@ class AutoCustomFrameRange(bpy.types.Operator):
# return {'CANCELLED'}
def restore(self, context):
if hasattr(subscriptions, 'frame_range'):
frame_start, frame_end = subscriptions.frame_range
else:
frame_start, frame_end = subscriptions.get_frame_range(context.scene)
print('restore')
subscriptions.frameend_update_callback()
self.strip.repeat = 1 #change strip repeat but keep self.repeat value stored
self.strip.use_reverse = False
self.strip.frame_start = frame_start
self.strip.scale = self.layer.speed
self.strip.frame_end = frame_end
# update_action_frame_range(frame_start, frame_end, layer, strip)
subscriptions.subscriptions_add(context.scene)
def update_action_list(scene):
@@ -2596,9 +2589,7 @@ class RemoveFcurves(bpy.types.Operator):
if mod.type == 'CYCLES':
fcu.modifiers.remove(mod)
fcu.update()
for area in context.window_manager.windows[0].screen.areas:
if area.type == 'GRAPH_EDITOR' or area.type == 'VIEW_3D':
area.tag_redraw()
redraw_areas(['GRAPH_EDITOR', 'VIEW_3D'])
break
return {'FINISHED'}
@@ -3301,25 +3292,36 @@ def copy_action(action):
return new_action
def get_obj_slot(obj, action, data_type = 'OBJECT'):
def get_obj_slot(obj, action, data_type = None):
'''Get the slot in the action that this object is using either it's object, or shapekeys'''
if data_type is None:
data_type = obj.als.data_type
if not hasattr(action, 'slots'):
return None
if not len(action.slots):
# If no slots exist, create one for the object and return it
slot = add_action_slot(obj, action)
return slot
# data_type = obj.als.data_type
for slot in action.slots:
if slot.target_id_type != data_type:
continue
# if obj.als.data_type == 'OBJECT' and obj in slot.users():
# return slot
if data_type == 'KEY' and obj.data.shape_keys in slot.users():
return slot
elif obj in slot.users():
return slot
return None
return add_action_slot(obj, action)
def get_fcurves(obj: bpy.types.Object, action: bpy.types.Action, data_type = 'OBJECT'):
def get_fcurves(obj: bpy.types.Object, action: bpy.types.Action, data_type = None):
if data_type is None:
data_type = obj.als.data_type
if hasattr(action, 'layers'):
slot = get_obj_slot(obj, action, data_type)
@@ -3335,10 +3337,13 @@ def get_fcurves(obj: bpy.types.Object, action: bpy.types.Action, data_type = 'OB
return action.fcurves
return []
def get_channelbag(obj: bpy.types.Object, action: bpy.types.Action, data_type = 'OBJECT'):
def get_channelbag(obj: bpy.types.Object, action: bpy.types.Action, data_type = None):
'''Getting the container of the fcurves, either the action or channelbag
Using this when adding a new group to the action'''
if data_type is None:
data_type = obj.als.data_type
if hasattr(action, 'layers'):
slot = get_obj_slot(obj, action, data_type)
channelbag = None
@@ -1,16 +1,16 @@
{
"last_check": "2026-04-21 12:47:39.126086",
"backup_date": "March-27-2026",
"last_check": "2026-05-27 14:41:11.263249",
"backup_date": "April-21-2026",
"update_ready": true,
"ignore": false,
"just_restored": false,
"just_updated": false,
"version_text": {
"link": "https://gitlab.com/api/v4/projects/22294607/repository/archive.zip?sha=dddd6932039b8a3e5fae3ce2de957f21a5942c84",
"link": "https://gitlab.com/api/v4/projects/22294607/repository/archive.zip?sha=321d411a449bc9acee2a759e30cd3d0f36bbd2ab",
"version": [
2,
4,
0
1
]
}
}
@@ -645,6 +645,7 @@ def AL_bake(frame_start, frame_end, nla_tracks, fcu_keys, additive, step, action
baked_action = track.strips[0].action
clean_no_user_slots(baked_action)
#create the baked fcurve
# baked_channelbag = anim_layers.get_channelbag(obj, baked_action, obj.als.data_type)
baked_channelbag = anim_layers.get_channelbag(obj, baked_action)
baked_fcurves = baked_channelbag.fcurves
@@ -73,7 +73,6 @@ def animlayers_frame(scene, context):
scene['framerange_preview'] = scene.use_preview_range
frameend_update_callback()
return
frame_start, frame_end = bake_ops.frame_start_end(scene)
# frame_start, frame_end = get_frame_range(scene)
reset_subscription = False
@@ -106,7 +105,6 @@ def animlayers_frame(scene, context):
for i, track in enumerate(nla_tracks):
if len(track.strips) != 1:
continue
#checks if the layer has a custom frame range
layer = obj.Anim_Layers[i]
if layer.custom_frame_range:
@@ -127,6 +125,7 @@ def animlayers_frame(scene, context):
if strip.frame_start < 0:
strip.frame_start = 0
anim_layers.update_action_frame_range(0, frame_end, layer, strip)
return
anim_layers.update_action_frame_range(strip.frame_start, current + 10.0, layer, strip)
strip.frame_end = current + 10.0
@@ -231,6 +230,9 @@ def track_layer_synchronization(obj, nla_tracks):
if obj.als.layer_index > len(obj.Anim_Layers)-1:
obj.als.layer_index = len(obj.Anim_Layers)-1
if not bpy.context.preferences.addons[__package__].preferences.auto_custom_range:
return
#update new layer with strip settings
frame_start, frame_end = get_frame_range(bpy.context.scene)
@@ -243,7 +245,6 @@ def track_layer_synchronization(obj, nla_tracks):
continue
if (strip.frame_start, strip.frame_end) != (frame_start, frame_end):
subscriptions_remove()
# print(f'strip.frame_start {strip.frame_start} strip.frame_end {strip.frame_end} frame_start {frame_start} frame_end {frame_end}')
bpy.ops.anim.custom_frame_range_warning('INVOKE_DEFAULT')
return
@@ -311,11 +312,16 @@ def sync_frame_range(scene, track, layer):
return
#Turn on custom frame range if the current strip is not following the scene frame range
# Should be activated when nla strips are edited manually in the nla editor, only when auto custom range is turned on, otherwise just update the strip frame range to the scene frame range
if (round(strip.frame_start, 2), round(strip.frame_end, 2)) != (round(frame_start, 2), round(frame_end, 2)):
subscriptions_remove()
# print('315 custom frame range')
bpy.ops.anim.custom_frame_range_warning('INVOKE_DEFAULT')
return
if bpy.context.preferences.addons[__package__].preferences.auto_custom_range:
subscriptions_remove()
# print('321 custom frame range')
bpy.ops.anim.custom_frame_range_warning('INVOKE_DEFAULT')
return
else:
frameend_update_callback()
return
def sync_strip_range(scene):
'''Checking all the strips if a value was changed in the nla (not including UI changes)
@@ -356,7 +362,6 @@ def sync_strip_range(scene):
if (strip_frame_start, round(strip_frame_end, 2)) != (frame_start, float(frame_end)):
subscriptions_remove()
# print('357 custom_frame_range_warning ')
# print(f'strip_frame_start {strip_frame_start} strip_frame_end {round(strip_frame_end, 2)} frame_start {frame_start} frame_end {float(frame_end)}')
bpy.ops.anim.custom_frame_range_warning('INVOKE_DEFAULT')
return
@@ -567,7 +572,6 @@ def subscribe_to_preview_frame_end(scene):
# Subscribing to preview frame end since it's not registering in the depsgraph
subscribe_preview_end = scene.path_resolve("frame_preview_end", False)
subscribe_use_preview = scene.path_resolve("use_preview_range", False)
# print('subscribe_to_preview_frame_end')
for subscribe in [subscribe_preview_end, subscribe_use_preview]:
bpy.msgbus.subscribe_rna(
+44 -19
View File
@@ -180,10 +180,11 @@ def smart_bake(context):
fcurves = anim_layers.get_fcurves(obj, track.strips[0].action)
total_iterations += len(fcurves)
wm.progress_begin(0, total_iterations)
wm.progress_begin(0, total_iterations)
processed = 0
for layer, track in zip(obj.Anim_Layers, anim_data.nla_tracks):
layer_items = [l for l in obj.Anim_Layers if l.type == 'LAYER']
for layer, track in zip(layer_items, anim_data.nla_tracks):
if track.mute:
continue
if len(track.strips) != 1 or track.strips[0].action is None:
@@ -242,9 +243,9 @@ def smart_bake(context):
smartkeys = smart_start_end(smartkeys, strip.frame_start, strip.frame_end)
smartkeys = remove_outofrange_keys(smartkeys, strip.frame_start, strip.frame_end)
#if the strip is cutting with a different strip, then add keyframes in the cut
#if the strip is cutting with a different strip, then add keyframes in the cut
for layercut in obj.Anim_Layers:
if layercut.mute or not layercut.custom_frame_range or layercut == layer:
if layercut.type == 'GROUP' or layercut.mute or not layercut.custom_frame_range or layercut == layer:
continue
if strip_start < layercut.frame_start < strip_end:
smartkeys = smart_start_end(smartkeys, (layercut.frame_start-1), strip.frame_end-1)
@@ -407,7 +408,7 @@ def unmute_modifiers(obj, nla_tracks, modifier_rec):
for mod in fcu.modifiers:
if mod in modifier_rec:
mod.mute = False
elif obj.als.mergefcurves and track == nla_tracks[obj.als.layer_index]:
elif obj.als.mergefcurves and track == nla_tracks[anim_layers.nla_idx(obj)]:
mod.mute = True
def invisible_layers(b_layers):
@@ -426,7 +427,14 @@ def select_keyframed_bones(self, context, obj):
if obj.mode != 'POSE':
bpy.ops.object.posemode_toggle()
bpy.ops.pose.select_all(action='DESELECT')
for i in range(0, obj.als.layer_index+1):
# Iterate over LAYER rows up to (and including) the active row, skipping
# group headers (they have no NLA track and no bones to select).
current_row = obj.als.layer_index
for i, it in enumerate(obj.Anim_Layers):
if i > current_row:
break
if it.type != 'LAYER':
continue
obj.als['layer_index'] = i
anim_layers.select_layer_bones(self, context)
@@ -446,7 +454,7 @@ def smartbake_apply(obj, nla_tracks, fcu_keys, extrapolations):
#apply smartbake for blenders bake
#smart bake - delete unnecessery keyframes:
# transform_types = ['location', 'rotation_euler', 'rotation_quaternion', 'scale']
strip = nla_tracks[obj.als.layer_index].strips[0]
strip = nla_tracks[anim_layers.nla_idx(obj)].strips[0]
# if strip.action is None:
# return
@@ -641,7 +649,7 @@ def AL_bake(frame_start, frame_end, nla_tracks, fcu_keys, additive, step, action
return
anim_data = anim_layers.anim_data_type(obj)
# baked_action = anim_data.action
track = nla_tracks[obj.als.layer_index]
track = nla_tracks[anim_layers.nla_idx(obj)]
baked_action = track.strips[0].action
clean_no_user_slots(baked_action)
#create the baked fcurve
@@ -1065,10 +1073,18 @@ class MergeAnimLayerDown(bpy.types.Operator):
bl_idname = "anim.layers_merge_down"
bl_label = "Merge_Layers_Down"
bl_options = {'REGISTER', 'UNDO'}
step: bpy.props.IntProperty(name='Step', description='Bake every number of frame steps', default=1)
actioncopy: bpy.props.BoolProperty(name='Copy original merged action', description='Create a copy of the original action that is being overwritten', default = False)
@classmethod
def poll(cls, context):
obj = context.object
if obj is None:
return False
# Disable when active row is a group header — merge only applies to NLA-backed layers.
return anim_layers.is_layer_row_active(obj)
def invoke(self, context, event):
obj = context.object
bake_range_type(context.scene.als, context)
@@ -1143,7 +1159,8 @@ class MergeAnimLayerDown(bpy.types.Operator):
# Incase the strips are shorter then the keyframe range (because scene is shorter)
# Then updating the strips length
for layer, track in zip(obj.Anim_Layers, anim_data.nla_tracks):
layer_items = [l for l in obj.Anim_Layers if l.type == 'LAYER']
for layer, track in zip(layer_items, anim_data.nla_tracks):
if layer.custom_frame_range:
continue
if len(track.strips) != 1:
@@ -1181,7 +1198,7 @@ class MergeAnimLayerDown(bpy.types.Operator):
if obj.als.direction == 'DOWN':
obj.als.layer_index = 0
baked_layer = None
strip = anim_data.nla_tracks[obj.als.layer_index].strips[0]
strip = anim_data.nla_tracks[anim_layers.nla_idx(obj)].strips[0]
action = strip.action
if hasattr(strip, 'action_slot'):
action_slot = strip.action_slot
@@ -1190,21 +1207,29 @@ class MergeAnimLayerDown(bpy.types.Operator):
#if baking to a new layer then setup the new index and layer
elif obj.als.operator == 'NEW':
self.actioncopy = False
# `add_at_nla` is the NLA-track index passed to add_animlayer.
if obj.als.direction == 'UP' and additive and 'REPLACE' in blendings:
obj.als.layer_index = layer_index + blendings.index('REPLACE') - 1
add_at_nla = layer_index + blendings.index('REPLACE') - 1
elif obj.als.direction == 'UP' or obj.als.direction == 'ALL':
obj.als.layer_index = len(obj.Anim_Layers)-1
add_at_nla = anim_layers.nla_layer_count(obj) - 1
else:
add_at_nla = anim_layers.nla_idx(obj)
layer_names = [layer.name for layer in obj.Anim_Layers]
baked_layer = anim_layers.add_animlayer(layer_name = anim_layers.unique_name(layer_names, 'Baked_Layer') , duplicate = False, index = obj.als.layer_index, blend_type = blend)
layer_names = [layer.name for layer in obj.Anim_Layers if layer.type == 'LAYER']
baked_layer = anim_layers.add_animlayer(layer_name = anim_layers.unique_name(layer_names, 'Baked_Layer') , duplicate = False, index = add_at_nla, blend_type = blend)
anim_layers.register_layers(obj, nla_tracks)
obj.als.layer_index += 1
# Point layer_index at the newly-added baked layer's collection row.
if baked_layer is not None:
for ridx, it in enumerate(obj.Anim_Layers):
if it.type == 'LAYER' and it.name == baked_layer.name:
obj.als.layer_index = ridx
break
#remove subsciption again after adding a layer there was new subsciption applied
subscriptions.subscriptions_remove()
track = nla_tracks[obj.als.layer_index]
track = nla_tracks[anim_layers.nla_idx(obj)]
#use internal bake
if obj.als.baketype =='NLA':
modifier_rec, extrapolations = mute_modifiers(obj, nla_tracks)
@@ -1282,7 +1307,7 @@ class MergeAnimLayerDown(bpy.types.Operator):
strip.action_slot = anim_layers.get_obj_slot(obj, action)
#reset layer settings
baked_layer = obj.Anim_Layers[obj.als.layer_index]
baked_layer = obj.Anim_Layers[obj.als.layer_index]
baked_layer.repeat, baked_layer.speed, baked_layer.offset = 1, 1, 0
strip.use_sync_length = False
if baked_layer.custom_frame_range:
@@ -106,7 +106,10 @@ def animlayers_frame(scene, context):
if len(track.strips) != 1:
continue
#checks if the layer has a custom frame range
layer = obj.Anim_Layers[i]
row_idx = anim_layers.layer_to_row_index(obj, i)
if row_idx < 0 or row_idx >= len(obj.Anim_Layers):
continue
layer = obj.Anim_Layers[row_idx]
if layer.custom_frame_range:
continue
if not reset_subscription:
@@ -169,6 +172,10 @@ def check_handler(scene):
return
anim_layers.add_obj_to_animlayers(obj, [item.object for item in scene.AL_objects])
nla_tracks = anim_data.nla_tracks
# When the active UIList row is a group header (no NLA track of its own),
# skip the LAYER-specific syncs below — they assume a real layer.
if not anim_layers.is_layer_row_active(obj):
return
layer = obj.Anim_Layers[obj.als.layer_index]
active_action_update(obj, anim_data, nla_tracks)
#check if a keyframe was removed
@@ -189,7 +196,7 @@ def check_handler(scene):
if track_layer_synchronization(obj, nla_tracks):
return
track = nla_tracks[obj.als.layer_index]
track = nla_tracks[anim_layers.nla_idx(obj)]
sync_frame_range(scene, track, layer)
# sync_strip_range(scene)
@@ -217,18 +224,20 @@ def check_handler(scene):
anim_layers.hide_view_all_keyframes(obj, anim_data)
check_selected_bones(obj)
influence_check(nla_tracks[obj.als.layer_index])
influence_check(nla_tracks[anim_layers.nla_idx(obj)])
def track_layer_synchronization(obj, nla_tracks):
'''check if track and layers are synchronized, running only when adding/removing tracks via the nla'''
if len(nla_tracks) == len(obj.Anim_Layers):
if len(nla_tracks) == anim_layers.nla_layer_count(obj):
return False
new_layers_names = set(track.name for track in nla_tracks).difference(set(layer.name for layer in obj.Anim_Layers))
layer_items = [layer for layer in obj.Anim_Layers if layer.type == 'LAYER']
new_layers_names = set(track.name for track in nla_tracks).difference(set(layer.name for layer in layer_items))
anim_layers.visible_layers(obj, nla_tracks)
if obj.als.layer_index > len(obj.Anim_Layers)-1:
obj.als.layer_index = len(obj.Anim_Layers)-1
row_count = len(obj.Anim_Layers)
if row_count and obj.als.layer_index > row_count - 1:
obj.als.layer_index = row_count - 1
if not bpy.context.preferences.addons[__package__].preferences.auto_custom_range:
return
@@ -261,9 +270,9 @@ def active_action_update(obj, anim_data, nla_tracks):
anim_data.action = None
subscriptions_add(bpy.context.scene)
return
if anim_data.action == nla_tracks[obj.als.layer_index].strips[0].action:
if anim_data.action == nla_tracks[anim_layers.nla_idx(obj)].strips[0].action:
return
if not len(nla_tracks[obj.als.layer_index].strips):
if not len(nla_tracks[anim_layers.nla_idx(obj)].strips):
return
if not anim_data.action or anim_data.is_property_readonly('action'):
return
@@ -434,7 +443,7 @@ def influence_sync(scene, obj, nla_tracks):
if action.name == scene.name + 'Action' and not len(scene.animation_data.nla_tracks) and not len(fcurves):
bpy.data.actions.remove(action)
strip = nla_tracks[obj.als.layer_index].strips[0]
strip = nla_tracks[anim_layers.nla_idx(obj)].strips[0]
if strip.fcurves[0].mute:
return
strip.fcurves[0].lock = False
@@ -546,9 +555,10 @@ def frameend_update_callback():
for anim_data in anim_datas:
if anim_data is None:
continue
if len(anim_data.nla_tracks) != len(obj.Anim_Layers):
if len(anim_data.nla_tracks) != anim_layers.nla_layer_count(obj):
continue
for layer, track in zip(obj.Anim_Layers, anim_data.nla_tracks):
layer_items = [l for l in obj.Anim_Layers if l.type == 'LAYER']
for layer, track in zip(layer_items, anim_data.nla_tracks):
if layer.custom_frame_range:
continue
if len(track.strips) != 1:
@@ -653,10 +663,12 @@ def action_name_callback():
nla_tracks = anim_data.nla_tracks
if not len(nla_tracks):
return
layer = obj.Anim_Layers[obj.als.layer_index]
if not len(nla_tracks[obj.als.layer_index].strips):
if not anim_layers.is_layer_row_active(obj):
return
action = nla_tracks[obj.als.layer_index].strips[0].action
layer = obj.Anim_Layers[obj.als.layer_index]
if not len(nla_tracks[anim_layers.nla_idx(obj)].strips):
return
action = nla_tracks[anim_layers.nla_idx(obj)].strips[0].action
if action is None:
return
if not obj.als.auto_rename or layer.name == action.name:
@@ -774,10 +786,10 @@ def slot_update_callback():
if not len(obj.Anim_Layers):
return
if not len(anim_data.nla_tracks[obj.als.layer_index].strips):
if not len(anim_data.nla_tracks[anim_layers.nla_idx(obj)].strips):
return
strip = anim_data.nla_tracks[obj.als.layer_index].strips[0]
strip = anim_data.nla_tracks[anim_layers.nla_idx(obj)].strips[0]
anim_data.action_slot = strip.action_slot
@@ -848,9 +860,11 @@ def strip_settings_callback():
return
# sync_strip_range()
if not len(anim_data.nla_tracks[obj.als.layer_index].strips):
if not anim_layers.is_layer_row_active(obj):
return
strip = anim_data.nla_tracks[obj.als.layer_index].strips[0]
if not len(anim_data.nla_tracks[anim_layers.nla_idx(obj)].strips):
return
strip = anim_data.nla_tracks[anim_layers.nla_idx(obj)].strips[0]
layer = obj.Anim_Layers[obj.als.layer_index]
update_strip_layer_settings(strip, layer)