How am I supposed to be converting a UE5.8 skeletal mesh into a Rigify rig in Blender 5.2?

Hello! I’m new to using Blender. I’m more accustomed to Maya workflows, and would like some help with game rigging in Blender.

My task:

I have a skeletal mesh I’ve brought in from Unreal 5.8. I would like to convert this into a Rigify rig, to hand off to an animator who will create custom animations in Blender (I’m using 5.2).

My method:

I’m currently using the UE to Rigify add-on to do just that. I’m currently at the step where I’m manually mapping control rig FK nodes to source rig FK nodes. However, I’m finding doing this by hand for hundreds of bones very tedious and prone to human error.

Questions:

Is there a different way I’m supposed to be making the generated control rig drive the skinned UE5 rig? A script, an add-on, anything? I’m currently doing it in the “bone remapping nodes editor” and manually adding in each node one by one.

I also notice that the Rigify Human spine is a bit different from the Mannequin spine. Am I supposed to map the controls to the bones by name or by the location of the pivot point? (e.g. rigify “hips” → UE5 “pelvis”) or by the location of the pivot point (e.g. rigify “torso” → UE5 “pelvis”). What if two controls have the same pivot point? (I see that spine_fk.001 and spine_fk.002 both pivot at spine_03)

I’m also not sure how to correctly assign tweak controls and master controls (like torso, hips, chest) to the source rig. Sadly, there is only one tutorial series on this plugin, uploaded by Unreal over 6 years ago, and it skips right over the details of template creation.

Help and advice would be much appreciated, thank you!

Welcome! :vulcan_salute: I’m not an expert, and I have only used a self made control rig for UE animations. But does this video provide any answers?

https://youtu.be/DcTN3iybqcQ?is=CpV-on1okc-_OCQt

I’m launching a plugin exactly for your situation @YukiG
There will be a lite(free) and a full version. It’s literally 3 clicks and you’ll have the animation in the NLA.
I’ve posted a preview here: https://youtu.be/JRT9t8ZbeE0?si=ODGHY5yXurE-cgnS

Hello! This helped me get further than I was before, so I’m very grateful, thank you! I’m now stuck at running the “fix constraints” script part. I keep getting the “bone object has no attribute select” error. I could attempt to rewrite the script based on my (admittedly basic) knowledge of python, but I’m not familiar with blender scripting and if this would cause any problems.

I always get stuck at exactly this point: I have the generated rig, I have the unreal skeleton, I just want to find a way for the generated rig to drive the other.

It looks very cool! I’ll definitely get the lite version whenever you do end up releasing it. I need to make the Rigify controls drive the UE5 skeleton first so I can try using your addon whenever it’s available :slight_smile:

You’re welcome! :grin: The description of the video tells about minor patches to the rig after recording the video, did you also check those? :thinking: Also it seems to have a link to a drive folder with the project files.

Yes, I downloaded the newest files and even opened up a fresh file to use it. I keep running into this error in every single method I’ve tried… genuinely, what is the fix for this?

UPDATE: Your solution helped me a lot! Thank you for directing me to this video!! I ran into a minor snag with the now-deprecated “bone.bone.select” command, but I was able to contact the person who posted this video and get the control rig to drive my UE5 rig!

I’m sharing the updated “Fix Constraints” script I got from them here should anyone come across this post in the future with the same problem.

NOT MY SCRIPT, FULL CREDIT TO USER @/KLI ON YOUTUBE

UE5.8 → Blender 5.2

import bpy

Name of the armatures

rig_armature_name = “rig”
root_armature_name = “Armature”

Bone name mapping

ChatGPT is stupid, and calls all bones from the root armature root bone. READ THIS READ THIS READ THIS#

bone_name_map = {
“pelvis”: “DEF-spine”,
“thigh_r”: “ORG-thigh.R”,
“calf_r”: “ORG-shin.R”,
“foot_r”: “ORG-foot.R”,
“ball_r”: “ORG-toe.R”,
“thigh_l”: “ORG-thigh.L”,
“calf_l”: “ORG-shin.L”,
“foot_l”: “ORG-foot.L”,
“ball_l”: “ORG-toe.L”,
“spine_01”: “DEF-spine.001”,
“spine_02”: “DEF-spine.002”,
“spine_03”: “DEF-spine.003”,
“spine_04”: “DEF-spine.004”,
“spine_05”: “DEF-spine.005”,
“neck_01”: “DEF-spine.006”,
“neck_02”: “DEF-spine.007”,
“head”: “DEF-spine.008”,
“clavicle_r”: “ORG-shoulder.R”,
“upperarm_r”: “ORG-upper_arm.R”,
“lowerarm_r”: “ORG-forearm.R”,
“hand_r”: “ORG-hand.R”,
“pinky_metacarpal_r”: “ORG-palm.04.R”,
“pinky_01_r”: “ORG-f_pinky.01.R”,
“pinky_02_r”: “ORG-f_pinky.02.R”,
“pinky_03_r”: “ORG-f_pinky.03.R”,
“ring_metacarpal_r”: “ORG-palm.03.R”,
“ring_01_r”: “ORG-f_ring.01.R”,
“ring_02_r”: “ORG-f_ring.02.R”,
“ring_03_r”: “ORG-f_ring.03.R”,
“middle_metacarpal_r”: “ORG-palm.02.R”,
“middle_01_r”: “ORG-f_middle.01.R”,
“middle_02_r”: “ORG-f_middle.02.R”,
“middle_03_r”: “ORG-f_middle.03.R”,
“index_metacarpal_r”: “ORG-palm.01.R”,
“index_01_r”: “ORG-f_index.01.R”,
“index_02_r”: “ORG-f_index.02.R”,
“index_03_r”: “ORG-f_index.03.R”,
“thumb_01_r”: “ORG-thumb.01.R”,
“thumb_02_r”: “ORG-thumb.02.R”,
“thumb_03_r”: “ORG-thumb.03.R”,
“clavicle_l”: “ORG-shoulder.L”,
“upperarm_l”: “ORG-upper_arm.L”,
“lowerarm_l”: “ORG-forearm.L”,
“hand_l”: “ORG-hand.L”,
“pinky_metacarpal_l”: “ORG-palm.04.L”,
“pinky_01_l”: “ORG-f_pinky.01.L”,
“pinky_02_l”: “ORG-f_pinky.02.L”,
“pinky_03_l”: “ORG-f_pinky.03.L”,
“ring_metacarpal_l”: “ORG-palm.03.L”,
“ring_01_l”: “ORG-f_ring.01.L”,
“ring_02_l”: “ORG-f_ring.02.L”,
“ring_03_l”: “ORG-f_ring.03.L”,
“middle_metacarpal_l”: “ORG-palm.02.L”,
“middle_01_l”: “ORG-f_middle.01.L”,
“middle_02_l”: “ORG-f_middle.02.L”,
“middle_03_l”: “ORG-f_middle.03.L”,
“index_metacarpal_l”: “ORG-palm.01.L”,
“index_01_l”: “ORG-f_index.01.L”,
“index_02_l”: “ORG-f_index.02.L”,
“index_03_l”: “ORG-f_index.03.L”,
“thumb_01_l”: “ORG-thumb.01.L”,
“thumb_02_l”: “ORG-thumb.02.L”,
“thumb_03_l”: “ORG-thumb.03.L”
}

pelvis_name_map = {
“pelvis”: “ORG-spine”
}

Ensure we are in object mode

if bpy.context.object.mode != ‘OBJECT’:
bpy.ops.object.mode_set(mode=‘OBJECT’)

Deselect all objects

bpy.ops.object.select_all(action=‘DESELECT’)

Get the armatures

rig_armature = bpy.data.objects[rig_armature_name]
root_armature = bpy.data.objects[root_armature_name]

Enter pose mode for the root armature to add constraints

root_armature.select_set(True)
bpy.context.view_layer.objects.active = root_armature
bpy.ops.object.mode_set(mode=‘POSE’)

Parent bones from root armature to rig armature based on the name map

for root_bone_name, rig_bone_name in bone_name_map.items():
rig_bone = rig_armature.pose.bones.get(rig_bone_name)
root_bone = root_armature.pose.bones.get(root_bone_name)

if rig_bone and root_bone:
    # Select the root bone
    # Blender 5.x: selection state now lives on the pose bone itself
    bpy.ops.pose.select_all(action='DESELECT')
    root_bone.select = True
    root_armature.data.bones.active = root_bone.bone

    # Add Copy Rotation constraint to the root bone
    constraint = root_bone.constraints.new('COPY_ROTATION')
    constraint.target = rig_armature
    constraint.subtarget = rig_bone_name
    constraint.target_space = 'LOCAL_OWNER_ORIENT'
    constraint.owner_space = 'LOCAL'
    print(f"Parented {root_bone_name} to {rig_bone_name} with Copy Rotation constraint")
else:
    print(f"Bone {rig_bone_name} or {root_bone_name} not found")

for root_bone_name, rig_bone_name in pelvis_name_map.items():
rig_bone = rig_armature.pose.bones.get(rig_bone_name)
root_bone = root_armature.pose.bones.get(root_bone_name)

if rig_bone and root_bone:
    # Select the pelvis bone
    # Blender 5.x: selection state now lives on the pose bone itself
    bpy.ops.pose.select_all(action='DESELECT')
    root_bone.select = True
    root_armature.data.bones.active = root_bone.bone

    # Add Copy Location constraint to the pelvis bone
    constraint = root_bone.constraints.new('COPY_LOCATION')
    constraint.target = rig_armature
    constraint.subtarget = rig_bone_name
    constraint.target_space = 'WORLD'
    constraint.owner_space = 'WORLD'

    print(f"Parented {root_bone_name} to {rig_bone_name} with Copy Location constraint")
else:
    print(f"Bone {rig_bone_name} or {root_bone_name} not found")

@YukiG , a bit late, but the addon is out. DM me if you’re still interested in this. I can give you access: https://www.youtube.com/watch?v=phbU2ffFP7w

Hello! Yes please - I would like access to this tool!
edit: can’t figure out where/how to DM you here, let me know where I can reach you to request access. Thank you!

To direct message me, you can click on my avatar/user name, and “send message”.