Every time I import a skeleton from a new source, whether it's a UE5 Mannequin retarget, a client's custom rig, or a scan that came back with slightly different naming from last month's, I end up spending 20 minutes manually clicking through the HIK Character Definition panel. Every. Time. It's the most tedious part of any mocap cleanup session and I finally got sick of it.
So I wrote a script that scans the scene for skeleton nodes, matches them against configurable name patterns per HIK slot, and does the mapping automatically. It's not magic. Weird rigs still need manual fixup, but for the pipelines I work in regularly it handles about 85% of the slots without touching anything.
from pyfbsdk import (
FBSystem, FBCharacter, FBModelSkeleton,
FBCharacterNodeId, FBConnect
)
# HIK slot name -> patterns tried in order, first match wins (case-insensitive substring)
BONE_MAP = {
"Hips": ["hips", "pelvis", "cog", "root"],
"Spine": ["spine_01", "spine1", "spine_1"],
"Spine1": ["spine_02", "spine2"],
"Spine2": ["spine_03", "spine3", "chest"],
"Neck": ["neck_01", "neck"],
"Head": ["head"],
"LeftShoulder": ["clavicle_l", "l_clavicle", "left_clavicle"],
"LeftArm": ["upperarm_l", "l_upperarm", "left_upperarm"],
"LeftForeArm": ["lowerarm_l", "l_forearm", "left_forearm"],
"LeftHand": ["hand_l", "l_hand", "left_hand"],
"RightShoulder": ["clavicle_r", "r_clavicle", "right_clavicle"],
"RightArm": ["upperarm_r", "r_upperarm", "right_upperarm"],
"RightForeArm": ["lowerarm_r", "r_forearm", "right_forearm"],
"RightHand": ["hand_r", "r_hand", "right_hand"],
"LeftUpLeg": ["thigh_l", "l_thigh", "left_thigh"],
"LeftLeg": ["calf_l", "l_calf", "l_shin", "left_calf"],
"LeftFoot": ["foot_l", "l_foot", "left_foot"],
"LeftToeBase": ["ball_l", "l_toe", "left_toe"],
"RightUpLeg": ["thigh_r", "r_thigh", "right_thigh"],
"RightLeg": ["calf_r", "r_calf", "r_shin", "right_calf"],
"RightFoot": ["foot_r", "r_foot", "right_foot"],
"RightToeBase": ["ball_r", "r_toe", "right_toe"],
}
NODE_ID_PREFIX = "kFBCharacterNode"
def get_skeleton_nodes():
return [c for c in FBSystem().Scene.Components if isinstance(c, FBModelSkeleton)]
def auto_map_hik(character_name):
scene = FBSystem().Scene
character = next(
(c for c in scene.Components if isinstance(c, FBCharacter) and c.Name == character_name),
None
)
if character is None:
print(f"[HIK Mapper] Character '{character_name}' not found in scene.")
return
name_to_node = {n.Name: n for n in get_skeleton_nodes()}
mapped = set()
success, skipped = 0, 0
character.SetCharacterizeOn(False)
for slot, patterns in BONE_MAP.items():
try:
node_id = getattr(FBCharacterNodeId, NODE_ID_PREFIX + slot)
except AttributeError:
print(f" [skip] Unknown node ID for slot: {slot}")
skipped += 1
continue
match = None
for pattern in patterns:
for name, node in name_to_node.items():
if name not in mapped and pattern.lower() in name.lower():
match = node
break
if match:
break
if match:
prop = character.PropertyList.Find(slot)
if prop:
FBConnect(match, prop)
mapped.add(match.Name)
success += 1
print(f" OK {slot:20s} <- {match.Name}")
else:
print(f" !! {slot:20s} <- NO MATCH")
skipped += 1
print(f"\nMapped {success} / {len(BONE_MAP)} slots ({skipped} unmapped).")
print("Review in HIK Character Definition before calling SetCharacterizeOn(True).")
auto_map_hik("Character")
The BONE_MAP dict is where you'd add your studio's conventions. I've got patterns in there for UE4/UE5 Mannequin, Mixamo, and a couple of client pipelines. Takes maybe 5 minutes to add a new naming convention once you've seen it once.
A few known gaps: finger bones are intentionally left out because the HIK finger slot ordering (LeftHandIndex1, LeftHandIndex2, etc.) doesn't match how most rigs name them alphabetically, and I kept getting index and middle swapped. Also, if two bones match the same pattern it picks the first one. Symmetric rigs with consistent L/R suffixes are usually fine, but anything where left and right share a prefix root can get confused.
You do need to create the FBCharacter manually first before running this, or add it to the script. I have a version that handles creation too but it got messy fast.
Anyone tackled the finger slot problem in a clean way? That's the one part I haven't found a good pattern for that doesn't require hardcoding bone names completely.