Skip to content

Instantly share code, notes, and snippets.

@UuuNyaa
Created December 14, 2022 18:58
Show Gist options
  • Select an option

  • Save UuuNyaa/9a113e58adcd9b14c3e59bf78689cd5e to your computer and use it in GitHub Desktop.

Select an option

Save UuuNyaa/9a113e58adcd9b14c3e59bf78689cd5e to your computer and use it in GitHub Desktop.
Apply SMPL motion to a SMPL model.
import json
import bpy
from mathutils import Matrix, Quaternion, Vector
SMPL_JOINT_NAMES = [
'pelvis', # 0
'left_hip', # 1
'right_hip', # 2
'spine1', # 3
'left_knee', # 4
'right_knee', # 5
'spine2', # 6
'left_ankle', # 7
'right_ankle', # 8
'spine3', # 9
'left_foot', # 10
'right_foot', # 11
'neck', # 12
'left_collar', # 13
'right_collar', # 14
'head', # 15
'left_shoulder', # 16
'right_shoulder', # 17
'left_elbow', # 18
'right_elbow', # 19
'left_wrist', # 20
'right_wrist', # 21
]
t2m_kinematic_chain = [[0, 3, 6, 9, 12, 15], [2, 5, 8, 11], [1, 4, 7, 10], [14, 17, 19, 21], [13, 16, 18, 20]]
def to_blender_vector(smpl_xyz: Vector) -> Vector:
return Vector([-smpl_xyz.x, smpl_xyz.z, smpl_xyz.y])
def to_blender_quaternion(smpl_wxyz: Quaternion) -> Quaternion:
return Quaternion([smpl_wxyz.w, -smpl_wxyz.x, smpl_wxyz.z, smpl_wxyz.y])
class TestOperator(bpy.types.Operator):
bl_idname = 'mmd_tools.test_operator'
bl_label = 'Test Operator'
bl_options = {'REGISTER', 'UNDO'}
def apply_motion(self, context: bpy.types.Context, armature_object: bpy.types.Object):
scale = 1.109527
pose_bones = armature_object.pose.bones
motion_sample_json = bpy.data.texts['motion_sample.json'].as_string()
motion_sample = json.loads(motion_sample_json)
for frame, joints in enumerate(motion_sample, start=1):
for bone_index_chain in t2m_kinematic_chain:
chain_length = len(bone_index_chain)
parent_tail_location = (
None if bone_index_chain[0] == 0
else pose_bones[SMPL_JOINT_NAMES[bone_index_chain[0]]].head
)
for chain_index, bone_index in enumerate(bone_index_chain[:-1], start=1):
pose_bone = pose_bones[SMPL_JOINT_NAMES[bone_index]]
head_joint_location = to_blender_vector(Vector(joints[bone_index][:3]) / scale)
head_location = parent_tail_location or head_joint_location
tail_joint_index = bone_index_chain[chain_index]
tail_joint_location = to_blender_vector(Vector(joints[tail_joint_index][:3]) / scale)
quaternion = Vector((0, 1, 0)).rotation_difference(tail_joint_location-head_location)
parent_tail_location = head_location + quaternion @ Vector((0, pose_bone.length, 0))
pose_bone_matrix = armature_object.convert_space(
pose_bone=pose_bone,
matrix=Matrix.LocRotScale(head_location, quaternion, None),
from_space='WORLD',
to_space='LOCAL'
)
if not (pose_bone.bone.use_connect or any(pose_bone.lock_location)):
pose_bone.location = pose_bone_matrix.translation
pose_bone.keyframe_insert('location', frame=frame)
pose_bone.rotation_quaternion = pose_bone_matrix.to_quaternion()
pose_bone.keyframe_insert('rotation_quaternion', frame=frame)
context.view_layer.update()
def execute(self, context: bpy.types.Context):
try:
self.apply_motion(context, context.active_object)
except Exception as ex:
self.report(type={'ERROR'}, message=str(ex))
return {'CANCELLED'}
return {'FINISHED'}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment