Merge pull request #146 from 989onan/patch-1

Create centralized method for identifying bones
This commit is contained in:
Yusarina
2025-04-01 00:05:14 +01:00
committed by GitHub
3 changed files with 60 additions and 37 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 SceneMatClass(PropertyGroup): class SceneMatClass(PropertyGroup):
mat: PointerProperty(type=Material) mat: PointerProperty(type=Material)
@@ -386,6 +387,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] = []
+35 -18
View File
@@ -1,9 +1,12 @@
# 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
from .common import simplify_bonename
bone_names = { bone_names = {
# Right side bones # Right side bones
"right_shoulder": [ "right_shoulder": [
@@ -254,26 +257,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
@@ -355,6 +358,8 @@ resonite_translations = {
'thumb_3_r': "thumb3.R" 'thumb_3_r': "thumb3.R"
} }
standard_bones = { standard_bones = {
# Core Structure # Core Structure
'hips': 'Hips', 'hips': 'Hips',
@@ -941,3 +946,15 @@ for category, mappings in non_standard_mappings.items():
bone_names[category].extend(mappings) bone_names[category].extend(mappings)
else: else:
bone_names[category] = mappings 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
+10 -18
View File
@@ -3,15 +3,16 @@ 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, 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
from ..core.armature_validation import validate_armature from ..core.armature_validation import validate_armature
import re
from .resonite_loader import resonite_animx, resonite_types from .resonite_loader import resonite_animx, resonite_types
import os import os
@@ -65,30 +66,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) bone.name = re.compile(re.escape("<noik>"), 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: with ProgressTracker(context, total_bones, t("Tools.convert_resonite.operation")) as progress:
for bone in armature.data.bones: for key_simple,bone_name in identify_bones(arm_data,context).items():
# Remove any existing "<noik>" tags
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: if key_simple in resonite_translations:
new_name = resonite_translations[reverse_bone_lookup[simplified_name]] 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: