From feb2f5ac85db08d83ca918bc621dba7051ed79d4 Mon Sep 17 00:00:00 2001 From: 989onan Date: Mon, 31 Mar 2025 18:28:04 -0400 Subject: [PATCH] Create centralized method for identifying bones - also fixes an issue where VRM bones would never be identified due to the names having "_" in them. --- core/common.py | 14 +++++++++++++ core/dictionaries.py | 46 +++++++++++++++++++++++++----------------- core/resonite_utils.py | 28 +++++++++---------------- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/core/common.py b/core/common.py index c232856..83a8ff3 100644 --- a/core/common.py +++ b/core/common.py @@ -18,6 +18,7 @@ from bpy.utils import register_class from ..core.logging_setup import logger from ..core.translations import t from ..core.dictionaries import bone_names +from .dictionaries import reverse_bone_lookup, bone_names class ProgressTracker: """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""" 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]: """Duplicate a chain of bones while preserving hierarchy""" new_bones: List[EditBone] = [] diff --git a/core/dictionaries.py b/core/dictionaries.py index 9a54b05..ec594a9 100644 --- a/core/dictionaries.py +++ b/core/dictionaries.py @@ -1,8 +1,8 @@ # GPL Licence - # 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 bone_names = { # Right side bones @@ -254,26 +254,26 @@ bone_names = { # Add VRM bone name variations bone_names.update({ - 'hips': bone_names['hips'] + ['j_bip_c_hips', 'j_hips', 'vrm_hips'], - 'spine': bone_names['spine'] + ['j_bip_c_spine', 'j_spine', 'vrm_spine'], - 'chest': bone_names['chest'] + ['j_bip_c_chest', 'j_chest', 'vrm_chest'], - 'upper_chest': bone_names['upper_chest'] + ['j_bip_c_upper_chest', 'j_upper_chest', 'vrm_upperchest'], - 'neck': bone_names['neck'] + ['j_bip_c_neck', 'j_neck', 'vrm_neck'], - 'head': bone_names['head'] + ['j_bip_c_head', 'j_head', 'vrm_head'], + 'hips': bone_names['hips'] + ['jbipchips', 'jhips', 'vrmhips'], + 'spine': bone_names['spine'] + ['jbipcspine', 'jspine', 'vrmspine'], + 'chest': bone_names['chest'] + ['jbipcchest', 'jchest', 'vrmchest'], + 'upper_chest': bone_names['upperchest'] + ['jbipcupperchest', 'jupperchest', 'vrmupperchest'], + 'neck': bone_names['neck'] + ['jbipcneck', 'jneck', 'vrmneck'], + 'head': bone_names['head'] + ['jbipchead', 'jhead', 'vrmhead'], # VRM specific finger naming - 'thumb_0_l': bone_names['thumb_0_l'] + ['thumb_metacarpal_l', 'j_thumb1_l'], - 'index_0_l': bone_names['index_0_l'] + ['index_metacarpal_l', 'j_index1_l'], - 'middle_0_l': bone_names['middle_0_l'] + ['middle_metacarpal_l', 'j_middle1_l'], - 'ring_0_l': bone_names['ring_0_l'] + ['ring_metacarpal_l', 'j_ring1_l'], - 'pinkie_0_l': bone_names['pinkie_0_l'] + ['little_metacarpal_l', 'j_little1_l'], + 'thumb_0_l': bone_names['thumb_0_l'] + ['thumbmetacarpall', 'jthumb1l'], + 'index_0_l': bone_names['index_0_l'] + ['indexmetacarpall', 'jindex1l'], + 'middle_0_l': bone_names['middle_0_l'] + ['middlemetacarpall', 'jmiddle1l'], + 'ring_0_l': bone_names['ring_0_l'] + ['ringmetacarpall', 'jring1l'], + 'pinkie_0_l': bone_names['pinkie_0_l'] + ['littlemetacarpall', 'jlittle1l'], # Mirror for right side - 'thumb_0_r': bone_names['thumb_0_r'] + ['thumb_metacarpal_r', 'j_thumb1_r'], - 'index_0_r': bone_names['index_0_r'] + ['index_metacarpal_r', 'j_index1_r'], - 'middle_0_r': bone_names['middle_0_r'] + ['middle_metacarpal_r', 'j_middle1_r'], - 'ring_0_r': bone_names['ring_0_r'] + ['ring_metacarpal_r', 'j_ring1_r'], - 'pinkie_0_r': bone_names['pinkie_0_r'] + ['little_metacarpal_r', 'j_little1_r'] + 'thumb_0_r': bone_names['thumb_0_r'] + ['thumbmetacarpalr', 'jthumb1r'], + 'index_0_r': bone_names['index_0_r'] + ['indexmetacarpalr', 'jindex1r'], + 'middle_0_r': bone_names['middle_0_r'] + ['middlemetacarpalr', 'jmiddle1r'], + 'ring_0_r': bone_names['ring_0_r'] + ['ringmetacarpalr', 'jring1r'], + 'pinkie_0_r': bone_names['pinkie_0_r'] + ['littlemetacarpalr', 'jlittle1r'] }) # array taken from cats @@ -354,3 +354,13 @@ resonite_translations = { 'thumb_2_r': "thumb2.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 + + + diff --git a/core/resonite_utils.py b/core/resonite_utils.py index f6938d2..c1e459c 100644 --- a/core/resonite_utils.py +++ b/core/resonite_utils.py @@ -3,14 +3,15 @@ import bpy import bpy_extras from numpy import double 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 ..core.translations import t from ..core.dictionaries import bone_names, resonite_translations from ..core.logging_setup import logger -import re + from .resonite_loader import resonite_animx, resonite_types import os @@ -64,30 +65,21 @@ class AvatarToolkit_OT_ConvertResonite(Operator): untranslated_bones: Set[str] = set() 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: context.view_layer.objects.active = armature bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='OBJECT') - + arm_data: bpy.types.Armature = armature.data # Cache simplified bone names - for bone in armature.data.bones: - simplified_names[bone.name] = simplify_bonename(bone.name) + for bone in arm_data.bones: + bone.name = re.compile(re.escape(""), re.IGNORECASE).sub("", bone.name) - total_bones = len(armature.data.bones) + total_bones = len(arm_data.bones) with ProgressTracker(context, total_bones, t("Tools.convert_resonite.operation")) as progress: - for bone in armature.data.bones: - # Remove any existing "" tags - bone.name = re.compile(re.escape(""), re.IGNORECASE).sub("", bone.name) - simplified_name = simplified_names[bone.name] + for key_simple,bone_name in identify_bones(arm_data,context).items(): - if simplified_name in reverse_bone_lookup and reverse_bone_lookup[simplified_name] in resonite_translations: - new_name = resonite_translations[reverse_bone_lookup[simplified_name]] + if key_simple in resonite_translations: + new_name = resonite_translations[key_simple] logger.debug(f"Translating bone: {bone.name} -> {new_name}") bone.name = new_name else: