from mathutils import Vector, Matrix
smpl_model = 'C:/Users/Uncharted Limbo/Downloads/SMPL_maya/basicModel_m_lbs_10_207_0_v1.0.2.fbx'
input_folder = 'C:/Users/Uncharted Limbo/Downloads/' # Input folder containing subfolders with .pkl files
output_folder = 'C:/Users/Uncharted Limbo/Downloads/fbx_output' # Output folder for FBX files
os.makedirs(output_folder, exist_ok=True)
# SMPL model bone name mapping
part_match_custom_less2 = {
'root': 'root', 'bone_00': 'Pelvis', 'bone_01': 'L_Hip', 'bone_02': 'R_Hip',
'bone_03': 'Spine1', 'bone_04': 'L_Knee', 'bone_05': 'R_Knee', 'bone_06': 'Spine2',
'bone_07': 'L_Ankle', 'bone_08': 'R_Ankle', 'bone_09': 'Spine3', 'bone_10': 'L_Foot',
'bone_11': 'R_Foot', 'bone_12': 'Neck', 'bone_13': 'L_Collar', 'bone_14': 'R_Collar',
'bone_15': 'Head', 'bone_16': 'L_Shoulder', 'bone_17': 'R_Shoulder', 'bone_18': 'L_Elbow',
'bone_19': 'R_Elbow', 'bone_20': 'L_Wrist', 'bone_21': 'R_Wrist',
'bone_22': 'L_Hand', 'bone_23': 'R_Hand',
"""Converts a rotation vector to a rotation matrix using Rodrigues' rotation formula."""
theta = np.linalg.norm(rotvec)
r = (rotvec / theta).reshape(3, 1)
mat = np.array([[0, -r[2][0], r[1][0]],
return cost * np.eye(3) + (1 - cost) * r.dot(r.T) + np.sin(theta) * mat
def rodrigues2bshapes(pose):
"""Converts pose data from rotation vectors to blendshapes."""
rod_rots = pose.reshape(int(pose.shape[0] / 3), 3)
mat_rots = [Rodrigues(rod_rot) for rod_rot in rod_rots]
bshapes = np.concatenate([(mat_rot - np.eye(3)).ravel()
for mat_rot in mat_rots[1:]])
def apply_trans_pose_shape(trans, pose, shape, ob, arm_ob, obname, scene, frame=None):
"""Applies translation, pose, and shape data to the SMPL model in Blender."""
mrots, bsh = rodrigues2bshapes(pose)
arm_ob.pose.bones['m_avg_Pelvis'].location = trans
arm_ob.pose.bones['m_avg_Pelvis'].keyframe_insert('location', frame=frame)
for ibone, mrot in enumerate(mrots):
bone_name = 'bone_%02d' % ibone
if bone_name in part_match_custom_less2:
bone = arm_ob.pose.bones[obname + '_' + part_match_custom_less2[bone_name]]
bone.rotation_quaternion = Matrix(mrot).to_quaternion()
bone.keyframe_insert('rotation_quaternion', frame=frame)
"""Initializes the Blender scene by clearing it and importing the SMPL model."""
bpy.ops.object.select_all(action='SELECT')
# Ensure the FBX file exists
if not os.path.exists(smpl_model):
raise FileNotFoundError(f"SMPL model FBX file not found at {smpl_model}")
bpy.ops.import_scene.fbx(filepath=smpl_model, axis_forward='-Y', axis_up='Z', global_scale=100)
ob = bpy.data.objects[obname]
ob.data.use_auto_smooth = False
ob.data.shape_keys.animation_data_clear()
arm_ob = bpy.data.objects[arm_obj]
arm_ob.animation_data_clear()
return ob, obname, arm_ob
"""Cleans the Blender scene by deleting all objects."""
bpy.ops.object.select_all(action='SELECT')
# Optionally, remove unused data blocks
bpy.ops.outliner.orphans_purge(do_recursive=True)
def process_pkl_to_fbx(pkl_file, output_file):
"""Processes a single PKL file and converts it to an FBX animation."""
data = joblib.load(pkl_file)
# Initialize Blender scene
scene = bpy.context.scene
ob, obname, arm_ob = init_scene(scene)
for character in range(len(data)):
qtd_frames = len(data[character]['pose'])
shape = data[character]['betas'].tolist()
for frame in range(qtd_frames):
trans = data[character]['trans_world'][frame]
pose = data[character]['pose_world'][frame]
apply_trans_pose_shape(Vector(trans), pose, shape, ob, arm_ob, obname, scene, frame)
# Set the animation end frame
scene.frame_end = qtd_frames - 1
bpy.ops.export_scene.fbx(filepath=output_file, use_selection=False)
# Clean the scene after export
"""Main function to process PKL files in subfolders and export to FBX."""
# Ensure output folder exists
os.makedirs(output_folder, exist_ok=True)
# Get list of all subdirectories in the input folder
subdirs = [os.path.join(input_folder, d) for d in os.listdir(input_folder)
if os.path.isdir(os.path.join(input_folder, d))]
# Process each subdirectory
# Find the .pkl file in the subdirectory
pkl_files = [f for f in os.listdir(subdir) if f.endswith('.pkl')]
print(f"No .pkl file found in {subdir}, skipping.")
continue # No .pkl file in this subdirectory, skip
# Assume only one .pkl file per subdirectory
pkl_file = os.path.join(subdir, pkl_files[0])
# Generate output .fbx file name based on subdirectory name
folder_name = os.path.basename(subdir)
output_file_name = folder_name + '.fbx'
output_file = os.path.join(output_folder, output_file_name)
print(f"Processing {pkl_file}...")
process_pkl_to_fbx(pkl_file, output_file)
print(f"FBX file saved to: {output_file}")
if __name__ == "__main__":