Create centralized method for identifying bones

- also fixes an issue where VRM bones would never be identified due to the names having "_" in them.
This commit is contained in:
989onan
2025-03-31 18:28:04 -04:00
parent 08f37d3202
commit feb2f5ac85
3 changed files with 52 additions and 36 deletions
+14
View File
@@ -18,6 +18,7 @@ from bpy.utils import register_class
from ..core.logging_setup import logger from ..core.logging_setup import logger
from ..core.translations import t from ..core.translations import t
from ..core.dictionaries import bone_names from ..core.dictionaries import bone_names
from .dictionaries import reverse_bone_lookup, bone_names
class ProgressTracker: class ProgressTracker:
"""Universal progress tracking for Avatar Toolkit operations""" """Universal progress tracking for Avatar Toolkit operations"""
@@ -412,6 +413,19 @@ def simplify_bonename(name: str) -> str:
"""Simplify bone name by removing spaces, underscores, dots and converting to lowercase""" """Simplify bone name by removing spaces, underscores, dots and converting to lowercase"""
return name.lower().translate(dict.fromkeys(map(ord, u" _."))) return name.lower().translate(dict.fromkeys(map(ord, u" _.")))
def identify_bones(arm_data: bpy.types.Armature, context: bpy.types.Context) -> Dict[str,str]:
"""Identify bone names in an armature based on our reverse dictionary, so there is no confusion to what a bone is.
Essentially makes a dictionary of keys from dictionaries.bone_names like "hips", and the corosponding value is the bone that can be mapped to that key."""
returned: Dict[str,str] = {}
for bone in arm_data.bones:
simplified_name = simplify_bonename(bone.name)
if simplified_name in reverse_bone_lookup:
returned[reverse_bone_lookup[simplified_name]] = bone.name
return returned
def duplicate_bone_chain(bones: List[EditBone]) -> List[EditBone]: def duplicate_bone_chain(bones: List[EditBone]) -> List[EditBone]:
"""Duplicate a chain of bones while preserving hierarchy""" """Duplicate a chain of bones while preserving hierarchy"""
new_bones: List[EditBone] = [] new_bones: List[EditBone] = []
+28 -18
View File
@@ -1,8 +1,8 @@
# GPL Licence # GPL Licence
# Bone names from https://github.com/triazo/immersive_scaler/ # Bone names from https://github.com/triazo/immersive_scaler/
# Note from @989onan: Please make sure to make your names are lowercase in this array. I banged my head metaphorically till I figured that out... # Note from @989onan: Please make sure to make your names are lowercase in this array, or it will never find a match. I banged my head metaphorically till I figured that out...
# Note2: Remove all "_", ".", and " " (space) from your values array or it will also not ever find a match!!!!
# Taken from Tuxedo/Cats # Taken from Tuxedo/Cats
bone_names = { bone_names = {
# Right side bones # Right side bones
@@ -254,26 +254,26 @@ bone_names = {
# Add VRM bone name variations # Add VRM bone name variations
bone_names.update({ bone_names.update({
'hips': bone_names['hips'] + ['j_bip_c_hips', 'j_hips', 'vrm_hips'], 'hips': bone_names['hips'] + ['jbipchips', 'jhips', 'vrmhips'],
'spine': bone_names['spine'] + ['j_bip_c_spine', 'j_spine', 'vrm_spine'], 'spine': bone_names['spine'] + ['jbipcspine', 'jspine', 'vrmspine'],
'chest': bone_names['chest'] + ['j_bip_c_chest', 'j_chest', 'vrm_chest'], 'chest': bone_names['chest'] + ['jbipcchest', 'jchest', 'vrmchest'],
'upper_chest': bone_names['upper_chest'] + ['j_bip_c_upper_chest', 'j_upper_chest', 'vrm_upperchest'], 'upper_chest': bone_names['upperchest'] + ['jbipcupperchest', 'jupperchest', 'vrmupperchest'],
'neck': bone_names['neck'] + ['j_bip_c_neck', 'j_neck', 'vrm_neck'], 'neck': bone_names['neck'] + ['jbipcneck', 'jneck', 'vrmneck'],
'head': bone_names['head'] + ['j_bip_c_head', 'j_head', 'vrm_head'], 'head': bone_names['head'] + ['jbipchead', 'jhead', 'vrmhead'],
# VRM specific finger naming # VRM specific finger naming
'thumb_0_l': bone_names['thumb_0_l'] + ['thumb_metacarpal_l', 'j_thumb1_l'], 'thumb_0_l': bone_names['thumb_0_l'] + ['thumbmetacarpall', 'jthumb1l'],
'index_0_l': bone_names['index_0_l'] + ['index_metacarpal_l', 'j_index1_l'], 'index_0_l': bone_names['index_0_l'] + ['indexmetacarpall', 'jindex1l'],
'middle_0_l': bone_names['middle_0_l'] + ['middle_metacarpal_l', 'j_middle1_l'], 'middle_0_l': bone_names['middle_0_l'] + ['middlemetacarpall', 'jmiddle1l'],
'ring_0_l': bone_names['ring_0_l'] + ['ring_metacarpal_l', 'j_ring1_l'], 'ring_0_l': bone_names['ring_0_l'] + ['ringmetacarpall', 'jring1l'],
'pinkie_0_l': bone_names['pinkie_0_l'] + ['little_metacarpal_l', 'j_little1_l'], 'pinkie_0_l': bone_names['pinkie_0_l'] + ['littlemetacarpall', 'jlittle1l'],
# Mirror for right side # Mirror for right side
'thumb_0_r': bone_names['thumb_0_r'] + ['thumb_metacarpal_r', 'j_thumb1_r'], 'thumb_0_r': bone_names['thumb_0_r'] + ['thumbmetacarpalr', 'jthumb1r'],
'index_0_r': bone_names['index_0_r'] + ['index_metacarpal_r', 'j_index1_r'], 'index_0_r': bone_names['index_0_r'] + ['indexmetacarpalr', 'jindex1r'],
'middle_0_r': bone_names['middle_0_r'] + ['middle_metacarpal_r', 'j_middle1_r'], 'middle_0_r': bone_names['middle_0_r'] + ['middlemetacarpalr', 'jmiddle1r'],
'ring_0_r': bone_names['ring_0_r'] + ['ring_metacarpal_r', 'j_ring1_r'], 'ring_0_r': bone_names['ring_0_r'] + ['ringmetacarpalr', 'jring1r'],
'pinkie_0_r': bone_names['pinkie_0_r'] + ['little_metacarpal_r', 'j_little1_r'] 'pinkie_0_r': bone_names['pinkie_0_r'] + ['littlemetacarpalr', 'jlittle1r']
}) })
# array taken from cats # array taken from cats
@@ -354,3 +354,13 @@ resonite_translations = {
'thumb_2_r': "thumb2.R", 'thumb_2_r': "thumb2.R",
'thumb_3_r': "thumb3.R" 'thumb_3_r': "thumb3.R"
} }
# Create reverse lookup dictionary (conversion/translation)
reverse_bone_lookup = {}
for preferred_name, name_list in bone_names.items():
for name in name_list:
reverse_bone_lookup[name] = preferred_name
+11 -19
View File
@@ -3,14 +3,15 @@ import bpy
import bpy_extras import bpy_extras
from numpy import double from numpy import double
from typing import Set, Dict from typing import Set, Dict
import re
from .common import get_active_armature, simplify_bonename, validate_armature, ProgressTracker from .common import get_active_armature, simplify_bonename, validate_armature, ProgressTracker, identify_bones
from bpy.types import Context, Operator from bpy.types import Context, Operator
from ..core.translations import t from ..core.translations import t
from ..core.dictionaries import bone_names, resonite_translations from ..core.dictionaries import bone_names, resonite_translations
from ..core.logging_setup import logger from ..core.logging_setup import logger
import re
from .resonite_loader import resonite_animx, resonite_types from .resonite_loader import resonite_animx, resonite_types
import os import os
@@ -64,30 +65,21 @@ class AvatarToolkit_OT_ConvertResonite(Operator):
untranslated_bones: Set[str] = set() untranslated_bones: Set[str] = set()
simplified_names: Dict[str, str] = {} simplified_names: Dict[str, str] = {}
# Create reverse lookup dictionary
reverse_bone_lookup = {}
for preferred_name, name_list in bone_names.items():
for name in name_list:
reverse_bone_lookup[name] = preferred_name
try: try:
context.view_layer.objects.active = armature context.view_layer.objects.active = armature
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
arm_data: bpy.types.Armature = armature.data
# Cache simplified bone names # Cache simplified bone names
for bone in armature.data.bones: for bone in arm_data.bones:
simplified_names[bone.name] = simplify_bonename(bone.name)
total_bones = len(armature.data.bones)
with ProgressTracker(context, total_bones, t("Tools.convert_resonite.operation")) as progress:
for bone in armature.data.bones:
# Remove any existing "<noik>" tags
bone.name = re.compile(re.escape("<noik>"), re.IGNORECASE).sub("", bone.name) bone.name = re.compile(re.escape("<noik>"), re.IGNORECASE).sub("", bone.name)
simplified_name = simplified_names[bone.name]
if simplified_name in reverse_bone_lookup and reverse_bone_lookup[simplified_name] in resonite_translations: total_bones = len(arm_data.bones)
new_name = resonite_translations[reverse_bone_lookup[simplified_name]] with ProgressTracker(context, total_bones, t("Tools.convert_resonite.operation")) as progress:
for key_simple,bone_name in identify_bones(arm_data,context).items():
if key_simple in resonite_translations:
new_name = resonite_translations[key_simple]
logger.debug(f"Translating bone: {bone.name} -> {new_name}") logger.debug(f"Translating bone: {bone.name} -> {new_name}")
bone.name = new_name bone.name = new_name
else: else: