2025-12-01

This commit is contained in:
2026-03-17 14:58:51 -06:00
parent 183e865f8b
commit 4b82b57113
6846 changed files with 954887 additions and 162606 deletions
@@ -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",
@@ -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
]
}
}