2025-12-01
This commit is contained in:
@@ -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.
Reference in New Issue
Block a user