From feb2f5ac85db08d83ca918bc621dba7051ed79d4 Mon Sep 17 00:00:00 2001 From: 989onan Date: Mon, 31 Mar 2025 18:28:04 -0400 Subject: [PATCH 1/2] 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: From eba18d72a68cc21da3fb6d25dd834fade7abd4ad Mon Sep 17 00:00:00 2001 From: 989onan Date: Mon, 31 Mar 2025 18:45:30 -0400 Subject: [PATCH 2/2] Fix poisoned name sets this is a simple patch because policing the standard is literally impossible --- core/dictionaries.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/dictionaries.py b/core/dictionaries.py index 5769260..c5525ec 100644 --- a/core/dictionaries.py +++ b/core/dictionaries.py @@ -4,6 +4,9 @@ # 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 + +from .common import simplify_bonename + bone_names = { # Right side bones "right_shoulder": [ @@ -354,11 +357,6 @@ 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 @@ -948,3 +946,15 @@ for category, mappings in non_standard_mappings.items(): bone_names[category].extend(mappings) else: bone_names[category] = mappings + + +# Since data set is very poisoned by bone names that aren't simplified (And as such will not map properly using the function) we will just force convert them to the proper format at the end here. - @989onan +for standard, mappings in bone_names: + for i in len(mappings): + bone_names[standard][i] = simplify_bonename(mappings[i]) + +# 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