Added blender scripts
This commit is contained in:
203
assets/blender/scripts/mixamo/lib/animation.py
Normal file
203
assets/blender/scripts/mixamo/lib/animation.py
Normal file
@@ -0,0 +1,203 @@
|
||||
import bpy
|
||||
from .maths_geo import *
|
||||
from .bones_pose import *
|
||||
from .version import *
|
||||
|
||||
def bake_anim(frame_start=0, frame_end=10, only_selected=False, bake_bones=True, bake_object=False, ik_data=None):
|
||||
scn = bpy.context.scene
|
||||
obj_data = []
|
||||
bones_data = []
|
||||
armature = bpy.data.objects.get(bpy.context.active_object.name)
|
||||
|
||||
def get_bones_matrix():
|
||||
matrix = {}
|
||||
for pbone in armature.pose.bones:
|
||||
if only_selected and not pbone.bone.select:
|
||||
continue
|
||||
|
||||
bmat = pbone.matrix
|
||||
|
||||
# IK poles
|
||||
if pbone.name.startswith("Ctrl_ArmPole") or pbone.name.startswith("Ctrl_LegPole"):
|
||||
b1 = b2 = None
|
||||
src_arm = ik_data["src_arm"]
|
||||
type = ""
|
||||
if "Leg" in pbone.name:
|
||||
type = "Leg"
|
||||
elif "Arm" in pbone.name:
|
||||
type = "Arm"
|
||||
|
||||
name_split = pbone.name.split('_')
|
||||
side = name_split[len(name_split)-1]
|
||||
b1_name = ik_data[type+side][0]
|
||||
b2_name = ik_data[type+side][1]
|
||||
b1 = src_arm.pose.bones.get(b1_name)
|
||||
b2 = src_arm.pose.bones.get(b2_name)
|
||||
|
||||
_axis = None
|
||||
if type == "Leg":
|
||||
_axis = (b1.z_axis*0.5) + (b2.z_axis*0.5)#b1.z_axis#
|
||||
elif type == "Arm":
|
||||
if side == "Left":
|
||||
_axis = b2.x_axis
|
||||
elif side == "Right":
|
||||
_axis = -b2.x_axis
|
||||
|
||||
pole_pos = get_ik_pole_pos(b1, b2, method=2, axis=_axis)
|
||||
#pole_pos = b2.head + (b2.z_axis.normalized() * (b2.tail-b2.head).magnitude)
|
||||
bmat = Matrix.Translation(pole_pos)
|
||||
|
||||
# Child Of constraints are preserved after baking
|
||||
# need to compensate the matrix with the Child Of transformation
|
||||
child_of_cns = pbone.constraints.get("Child Of")
|
||||
if child_of_cns:
|
||||
if child_of_cns.influence == 1.0 and child_of_cns.mute == False:
|
||||
bmat = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ bmat
|
||||
|
||||
matrix[pbone.name] = armature.convert_space(pose_bone=pbone, matrix=bmat, from_space="POSE", to_space="LOCAL")
|
||||
|
||||
return matrix
|
||||
|
||||
def get_obj_matrix():
|
||||
parent = armature.parent
|
||||
matrix = armature.matrix_world
|
||||
if parent:
|
||||
return parent.matrix_world.inverted_safe() @ matrix
|
||||
else:
|
||||
return matrix.copy()
|
||||
|
||||
# store matrices
|
||||
current_frame = scn.frame_current
|
||||
|
||||
for f in range(int(frame_start), int(frame_end+1)):
|
||||
scn.frame_set(f)
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
if bake_bones:
|
||||
bones_data.append((f, get_bones_matrix()))
|
||||
if bake_object:
|
||||
obj_data.append((f, get_obj_matrix()))
|
||||
|
||||
# set new action
|
||||
action = bpy.data.actions.new("Action")
|
||||
anim_data = armature.animation_data_create()
|
||||
anim_data.action = action
|
||||
|
||||
def store_keyframe(bn, prop_type, fc_array_index, fra, val):
|
||||
fc_data_path = 'pose.bones["' + bn + '"].' + prop_type
|
||||
fc_key = (fc_data_path, fc_array_index)
|
||||
if not keyframes.get(fc_key):
|
||||
keyframes[fc_key] = []
|
||||
keyframes[fc_key].extend((fra, val))
|
||||
|
||||
|
||||
# set transforms and store keyframes
|
||||
if bake_bones:
|
||||
for pb in armature.pose.bones:
|
||||
if only_selected and not pb.bone.select:
|
||||
continue
|
||||
|
||||
euler_prev = None
|
||||
quat_prev = None
|
||||
keyframes = {}
|
||||
|
||||
for (f, matrix) in bones_data:
|
||||
pb.matrix_basis = matrix[pb.name].copy()
|
||||
|
||||
for arr_idx, value in enumerate(pb.location):
|
||||
store_keyframe(pb.name, "location", arr_idx, f, value)
|
||||
|
||||
rotation_mode = pb.rotation_mode
|
||||
|
||||
if rotation_mode == 'QUATERNION':
|
||||
if quat_prev is not None:
|
||||
quat = pb.rotation_quaternion.copy()
|
||||
quat.make_compatible(quat_prev)
|
||||
pb.rotation_quaternion = quat
|
||||
quat_prev = quat
|
||||
del quat
|
||||
else:
|
||||
quat_prev = pb.rotation_quaternion.copy()
|
||||
|
||||
for arr_idx, value in enumerate(pb.rotation_quaternion):
|
||||
store_keyframe(pb.name, "rotation_quaternion", arr_idx, f, value)
|
||||
|
||||
elif rotation_mode == 'AXIS_ANGLE':
|
||||
for arr_idx, value in enumerate(pb.rotation_axis_angle):
|
||||
store_keyframe(pb.name, "rotation_axis_angle", arr_idx, f, value)
|
||||
|
||||
else: # euler, XYZ, ZXY etc
|
||||
if euler_prev is not None:
|
||||
euler = pb.rotation_euler.copy()
|
||||
euler.make_compatible(euler_prev)
|
||||
pb.rotation_euler = euler
|
||||
euler_prev = euler
|
||||
del euler
|
||||
else:
|
||||
euler_prev = pb.rotation_euler.copy()
|
||||
|
||||
for arr_idx, value in enumerate(pb.rotation_euler):
|
||||
store_keyframe(pb.name, "rotation_euler", arr_idx, f, value)
|
||||
|
||||
for arr_idx, value in enumerate(pb.scale):
|
||||
store_keyframe(pb.name, "scale", arr_idx, f, value)
|
||||
|
||||
|
||||
# Add keyframes
|
||||
for fc_key, key_values in keyframes.items():
|
||||
data_path, index = fc_key
|
||||
fcurve = action.fcurves.find(data_path=data_path, index=index)
|
||||
if fcurve == None:
|
||||
fcurve = action.fcurves.new(data_path, index=index, action_group=pb.name)
|
||||
|
||||
num_keys = len(key_values) // 2
|
||||
fcurve.keyframe_points.add(num_keys)
|
||||
fcurve.keyframe_points.foreach_set('co', key_values)
|
||||
|
||||
if blender_version._float >= 290:# internal error when doing so with Blender 2.83, only for Blender 2.90 and higher
|
||||
linear_enum_value = bpy.types.Keyframe.bl_rna.properties['interpolation'].enum_items['LINEAR'].value
|
||||
fcurve.keyframe_points.foreach_set('interpolation', (linear_enum_value,) * num_keys)
|
||||
else:
|
||||
for kf in fcurve.keyframe_points:
|
||||
kf.interpolation = 'LINEAR'
|
||||
|
||||
|
||||
if bake_object:
|
||||
euler_prev = None
|
||||
quat_prev = None
|
||||
|
||||
for (f, matrix) in obj_data:
|
||||
name = "Action Bake"
|
||||
armature.matrix_basis = matrix
|
||||
|
||||
armature.keyframe_insert("location", index=-1, frame=f, group=name)
|
||||
|
||||
rotation_mode = armature.rotation_mode
|
||||
if rotation_mode == 'QUATERNION':
|
||||
if quat_prev is not None:
|
||||
quat = armature.rotation_quaternion.copy()
|
||||
quat.make_compatible(quat_prev)
|
||||
armature.rotation_quaternion = quat
|
||||
quat_prev = quat
|
||||
del quat
|
||||
else:
|
||||
quat_prev = armature.rotation_quaternion.copy()
|
||||
armature.keyframe_insert("rotation_quaternion", index=-1, frame=f, group=name)
|
||||
elif rotation_mode == 'AXIS_ANGLE':
|
||||
armature.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name)
|
||||
else: # euler, XYZ, ZXY etc
|
||||
if euler_prev is not None:
|
||||
euler = armature.rotation_euler.copy()
|
||||
euler.make_compatible(euler_prev)
|
||||
armature.rotation_euler = euler
|
||||
euler_prev = euler
|
||||
del euler
|
||||
else:
|
||||
euler_prev = armature.rotation_euler.copy()
|
||||
armature.keyframe_insert("rotation_euler", index=-1, frame=f, group=name)
|
||||
|
||||
armature.keyframe_insert("scale", index=-1, frame=f, group=name)
|
||||
|
||||
|
||||
# restore current frame
|
||||
scn.frame_set(current_frame)
|
||||
Reference in New Issue
Block a user