6412b6f619
- Added standard list. - Added bone_hierarchy list - Added bone_hierarchy - Better checks. - Better UI. This is the first part, still needs alot of work, but this is better then before. Need to add some more standards and then we will be golden.
112 lines
4.5 KiB
Python
112 lines
4.5 KiB
Python
import bpy
|
|
from typing import Tuple, List, Dict, Set, Optional
|
|
from bpy.types import Object, Bone
|
|
from ..core.translations import t
|
|
from ..core.dictionaries import (
|
|
standard_bones,
|
|
bone_hierarchy,
|
|
finger_hierarchy
|
|
)
|
|
|
|
def validate_armature(armature: Object) -> Tuple[bool, List[str]]:
|
|
validation_mode = bpy.context.scene.avatar_toolkit.validation_mode
|
|
messages: List[str] = []
|
|
|
|
if validation_mode == 'NONE':
|
|
return True, []
|
|
|
|
if not armature or armature.type != 'ARMATURE' or not armature.data.bones:
|
|
return False, [t("Armature.validation.basic_check_failed")]
|
|
|
|
found_bones: Dict[str, Bone] = {bone.name: bone for bone in armature.data.bones}
|
|
|
|
# List all bones in armature
|
|
bone_list = "\n".join([f"- {bone}" for bone in found_bones.keys()])
|
|
messages.append(t("Armature.validation.found_bones", bones=bone_list))
|
|
|
|
# Check each bone against our standard
|
|
non_standard_bones = []
|
|
required_patterns = [
|
|
'Hips', 'Spine', 'Chest', 'Neck', 'Head',
|
|
'Upper', 'Lower', 'Hand', 'Foot', 'Toe',
|
|
'Thumb', 'Index', 'Middle', 'Ring', 'Pinky',
|
|
'Eye'
|
|
]
|
|
|
|
for bone_name in found_bones:
|
|
if any(pattern in bone_name for pattern in required_patterns):
|
|
if bone_name not in standard_bones.values():
|
|
non_standard_bones.append(bone_name)
|
|
|
|
if non_standard_bones:
|
|
non_standard_list = "\n".join([f"- {bone}" for bone in non_standard_bones])
|
|
messages.append(t("Armature.validation.non_standard_bones", bones=non_standard_list))
|
|
|
|
# Check for missing required bones
|
|
essential_bones = {standard_bones[key] for key in ['hips', 'spine', 'chest', 'neck', 'head']}
|
|
missing_bones = [bone for bone in essential_bones if bone not in found_bones]
|
|
|
|
if missing_bones:
|
|
missing_list = "\n".join([f"- {bone}" for bone in missing_bones])
|
|
messages.append(t("Armature.validation.missing_bones", bones=missing_list))
|
|
|
|
if validation_mode == 'STRICT':
|
|
# Validate bone hierarchy
|
|
for parent, child in bone_hierarchy:
|
|
if parent in found_bones and child in found_bones:
|
|
if not validate_bone_hierarchy(found_bones, parent, child):
|
|
messages.append(t("Armature.validation.invalid_hierarchy",
|
|
parent=parent, child=child))
|
|
|
|
# Validate symmetry
|
|
symmetry_pairs = [('arm', 'l', 'r'), ('leg', 'l', 'r')]
|
|
for base, left, right in symmetry_pairs:
|
|
if not validate_symmetry(found_bones, base, left, right):
|
|
messages.append(t("Armature.validation.asymmetric_bones", bone=base))
|
|
|
|
if (not validate_symmetry(found_bones, 'hand', 'l', 'r') and
|
|
not validate_symmetry(found_bones, 'wrist', 'l', 'r')):
|
|
messages.append(t("Armature.validation.asymmetric_hand_wrist"))
|
|
|
|
# Validate finger hierarchies
|
|
for side in ['left', 'right']:
|
|
for finger_chain in finger_hierarchy[side]:
|
|
if all(bone in found_bones for bone in finger_chain):
|
|
if not validate_finger_chain(found_bones, finger_chain):
|
|
messages.append(t("Armature.validation.invalid_finger", finger=finger_chain[0]))
|
|
|
|
is_valid: bool = len(messages) == 0
|
|
return is_valid, messages
|
|
|
|
def validate_bone_hierarchy(bones: Dict[str, Bone], parent_name: str, child_name: str) -> bool:
|
|
"""Validate if there is a valid parent-child relationship between bones"""
|
|
if parent_name not in bones or child_name not in bones:
|
|
return False
|
|
return bones[child_name].parent == bones[parent_name]
|
|
|
|
def validate_symmetry(bones: Dict[str, Bone], base: str, left: str, right: str) -> bool:
|
|
"""Validate if matching left and right bones exist for a given base bone name"""
|
|
left_patterns: List[str] = [
|
|
f"{base}.{left}",
|
|
f"{base}_{left}",
|
|
f"{left}_{base}"
|
|
]
|
|
|
|
right_patterns: List[str] = [
|
|
f"{base}.{right}",
|
|
f"{base}_{right}",
|
|
f"{right}_{base}"
|
|
]
|
|
|
|
left_exists: bool = any(pattern in bones for pattern in left_patterns)
|
|
right_exists: bool = any(pattern in bones for pattern in right_patterns)
|
|
|
|
return left_exists and right_exists
|
|
|
|
def validate_finger_chain(bones: Dict[str, Bone], chain: Tuple[str, ...]) -> bool:
|
|
"""Validate if a finger bone chain has correct hierarchy"""
|
|
for i in range(len(chain) - 1):
|
|
if not validate_bone_hierarchy(bones, chain[i], chain[i + 1]):
|
|
return False
|
|
return True
|