Skip to content

Instantly share code, notes, and snippets.

@avramovic
Last active October 3, 2025 22:01
Show Gist options
  • Select an option

  • Save avramovic/2c50405b32dece773882693b0d251ad1 to your computer and use it in GitHub Desktop.

Select an option

Save avramovic/2c50405b32dece773882693b0d251ad1 to your computer and use it in GitHub Desktop.
Blender animation retargeting
import bpy, sys, os
argv = sys.argv
argv = argv[argv.index("--") + 1:]
src_file = os.path.abspath(argv[0])
tgt_file = os.path.abspath(argv[1])
out_file = os.path.abspath(argv[2])
print("Source:", src_file)
print("Target:", tgt_file)
print("Output:", out_file)
# Reset scene
bpy.ops.wm.read_factory_settings(use_empty=True)
# Import source
print("Importing source...")
bpy.ops.import_scene.gltf(filepath=src_file)
# Find source armature
arm_source = None
for obj in bpy.context.scene.objects:
if obj.type == "ARMATURE":
arm_source = obj
break
if not arm_source:
raise Exception("No armature in source file")
print("Source armature:", arm_source.name)
# Import target (mesh for retarget)
print("Importing target...")
bpy.ops.import_scene.gltf(filepath=tgt_file)
arm_target = None
for obj in bpy.context.scene.objects:
if obj.type == "ARMATURE" and obj != arm_source:
arm_target = obj
break
if not arm_target:
raise Exception("No armature in target file")
print("Target armature:", arm_target.name)
# Bone map
bone_map = {
# Root / Spine
"DEF-hips": "Hips",
"DEF-spine.001": "Spine",
"DEF-spine.002": "Spine1",
"DEF-spine.003": "Spine2",
"DEF-neck": "Neck",
"DEF-head": "Head",
# Left arm
"DEF-shoulder.L": "LeftShoulder",
"DEF-upper_arm.L": "LeftArm",
"DEF-forearm.L": "LeftForeArm",
"DEF-hand.L": "LeftHand",
"DEF-thumb.01.L": "LeftHandThumb1",
"DEF-thumb.02.L": "LeftHandThumb2",
"DEF-thumb.03.L": "LeftHandThumb3",
"DEF-f_index.01.L": "LeftHandIndex1",
"DEF-f_index.02.L": "LeftHandIndex2",
"DEF-f_index.03.L": "LeftHandIndex3",
"DEF-f_middle.01.L": "LeftHandMiddle1",
"DEF-f_middle.02.L": "LeftHandMiddle2",
"DEF-f_middle.03.L": "LeftHandMiddle3",
"DEF-f_ring.01.L": "LeftHandRing1",
"DEF-f_ring.02.L": "LeftHandRing2",
"DEF-f_ring.03.L": "LeftHandRing3",
"DEF-f_pinky.01.L": "LeftHandPinky1",
"DEF-f_pinky.02.L": "LeftHandPinky2",
"DEF-f_pinky.03.L": "LeftHandPinky3",
# Right arm
"DEF-shoulder.R": "RightShoulder",
"DEF-upper_arm.R": "RightArm",
"DEF-forearm.R": "RightForeArm",
"DEF-hand.R": "RightHand",
"DEF-thumb.01.R": "RightHandThumb1",
"DEF-thumb.02.R": "RightHandThumb2",
"DEF-thumb.03.R": "RightHandThumb3",
"DEF-f_index.01.R": "RightHandIndex1",
"DEF-f_index.02.R": "RightHandIndex2",
"DEF-f_index.03.R": "RightHandIndex3",
"DEF-f_middle.01.R": "RightHandMiddle1",
"DEF-f_middle.02.R": "RightHandMiddle2",
"DEF-f_middle.03.R": "RightHandMiddle3",
"DEF-f_ring.01.R": "RightHandRing1",
"DEF-f_ring.02.R": "RightHandRing2",
"DEF-f_ring.03.R": "RightHandRing3",
"DEF-f_pinky.01.R": "RightHandPinky1",
"DEF-f_pinky.02.R": "RightHandPinky2",
"DEF-f_pinky.03.R": "RightHandPinky3",
# Legs
"DEF-thigh.L": "LeftUpLeg",
"DEF-shin.L": "LeftLeg",
"DEF-foot.L": "LeftFoot",
"DEF-toe.L": "LeftToeBase",
"DEF-thigh.R": "RightUpLeg",
"DEF-shin.R": "RightLeg",
"DEF-foot.R": "RightFoot",
"DEF-toe.R": "RightToeBase",
}
print("Bone mapping:", bone_map)
# Copy all animations
for action in bpy.data.actions:
print("Copying action:", action.name)
new_action = action.copy()
new_action.name = f"{action.name}_retarget"
for fcurve in new_action.fcurves:
path = fcurve.data_path
for src_bone, tgt_bone in bone_map.items():
if src_bone in path:
fcurve.data_path = path.replace(src_bone, tgt_bone)
if not arm_target.animation_data:
arm_target.animation_data_create()
arm_target.animation_data.action = new_action
# Remove source model
print("Deleting source...")
bpy.ops.object.select_all(action='DESELECT')
arm_source.select_set(True)
for child in arm_source.children:
child.select_set(True)
bpy.ops.object.delete()
# Exportuj only target
print("Exporting target with animations...")
bpy.ops.object.select_all(action='DESELECT')
arm_target.select_set(True)
for child in arm_target.children:
child.select_set(True)
bpy.context.view_layer.objects.active = arm_target
bpy.ops.export_scene.gltf(filepath=out_file, export_format='GLB', use_selection=True)
print("DONE. Exported to:", out_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment