I am fairly new to Blender and retargeting methods, however I am looking for a way to skin bvh declared motion. I imported a skin and fbx format animation from Mixamo, which means that armatures in fbx files containing skin and action are the same. Since my goal is to make it work with bvh, I converted action fbx to bvh file. However, when I attempt to transfer animation, skin from the fbx is deformed. I am a bit confused as I haven't found too much information on how to do it properly. I would be very grateful for any help! my current code: import bpy import sys import numpy as np import argparse import os def clean_scene(): bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() def load_fbx(source): bpy.ops.import_scene.fbx(filepath=source) def load_bvh(source): bpy.ops.import_anim.bvh(filepath=source) return source.split('/')[-1][:-4] def extract_weight(me): verts = me.data.vertices vgrps = me.vertex_groups weight = np.zeros((len(verts), len(vgrps))) mask = np.zeros(weight.shape, dtype=int) vgrp_label = vgrps.keys() for i, vert in enumerate(verts): for g in vert.groups: j = g.group weight[i, j] = g.weight mask[i, j] = 1 return weight, vgrp_label, mask def clean_vgrps(me): vgrps = me.vertex_groups for _ in range(len(vgrps)): vgrps.remove(vgrps[0]) def load_weight(me, label, weight): clean_vgrps(me) verts = me.data.vertices vgrps = me.vertex_groups for name in label: vgrps.new(name=name) for j in range(weight.shape[1]): idx = vgrps.find(label[j]) if idx == -1: continue for i in range(weight.shape[0]): vgrps[idx].add(, weight[i, j], 'REPLACE') def set_modifier(me, arm): modifiers = me.modifiers for modifier in modifiers: if modifier.type == 'ARMATURE': modifier.object = arm modifier.use_vertex_groups = True modifier.use_deform_preserve_volume = True return modifier = modifiers.new(name='Armature', type='ARMATURE') modifier.object = arm modifier.use_vertex_groups = True modifier.use_deform_preserve_volume = True def adapt_weight(source_weight, source_label, source_arm, dest_arm): dest_bone_names = {bone.name for bone in dest_arm.data.bones} # Check for exact matches only missing_bones = [name for name in source_label if name not in dest_bone_names] if missing_bones: print("\n[ERROR] The following vertex group names were not found in the destination armature bones:") for name in missing_bones: print(f" - {name}") raise ValueError("Aborting weight transfer due to missing bones.") # Proceed with safe mapping weight = np.zeros((source_weight.shape[0], len(dest_arm.data.bones))) dest_bone_index = {bone.name: i for i, bone in enumerate(dest_arm.data.bones)} for j, name in enumerate(source_label): idx = dest_bone_index[name] weight[:, idx] += source_weight[:, j] return weight def main(): parser = argparse.ArgumentParser() parser.add_argument('--fbx_file', type=str, required=True, help='path of skinned model fbx file') parser.add_argument('--bvh_file', type=str, required=True, help='path of animation bvh file') if "--" not in sys.argv: argv = [] else: argv = sys.argv[sys.argv.index("--") + 1:] args = parser.parse_args(argv) clean_scene() load_fbx(args.fbx_file) source_arm = bpy.data.objects['Armature'] source_arm.scale = [1.0, 1.0, 1.0] bvh_name = load_bvh(args.bvh_file) dest_arm = bpy.data.objects[bvh_name] dest_arm.scale = source_arm.scale bpy.context.view_layer.update() meshes = [obj for obj in bpy.data.objects if obj.type == 'MESH'] for mesh in meshes: weight, label, _ = extract_weight(mesh) weight = adapt_weight(weight, label, source_arm, dest_arm) load_weight(mesh, dest_arm.data.bones.keys(), weight) set_modifier(mesh, dest_arm) bpy.context.view_layer.update() # source_arm.hide_viewport = True if __name__ == "__main__": main() Continue reading...