Fixes
- Fixed issue with plugin registration - Fixed bones symmetry not working - Fixed different modes not working - Some other small fixes.
This commit is contained in:
+63
-44
@@ -16,6 +16,8 @@ def validate_armature(armature: Object) -> Tuple[bool, List[str], bool]:
|
|||||||
"""
|
"""
|
||||||
validation_mode = bpy.context.scene.avatar_toolkit.validation_mode
|
validation_mode = bpy.context.scene.avatar_toolkit.validation_mode
|
||||||
messages: List[str] = []
|
messages: List[str] = []
|
||||||
|
hierarchy_messages: List[str] = []
|
||||||
|
non_standard_messages: List[str] = []
|
||||||
|
|
||||||
if validation_mode == 'NONE':
|
if validation_mode == 'NONE':
|
||||||
return True, [], False
|
return True, [], False
|
||||||
@@ -24,68 +26,76 @@ def validate_armature(armature: Object) -> Tuple[bool, List[str], bool]:
|
|||||||
return False, [t("Armature.validation.basic_check_failed")], False
|
return False, [t("Armature.validation.basic_check_failed")], False
|
||||||
|
|
||||||
found_bones: Dict[str, Bone] = {bone.name: bone for bone in armature.data.bones}
|
found_bones: Dict[str, Bone] = {bone.name: bone for bone in armature.data.bones}
|
||||||
|
|
||||||
# Check if armature matches acceptable standards
|
|
||||||
is_acceptable = check_acceptable_standards(found_bones)
|
is_acceptable = check_acceptable_standards(found_bones)
|
||||||
|
|
||||||
# List all bones in armature
|
# List all bones in armature
|
||||||
bone_list = "\n".join([f"- {bone}" for bone in found_bones.keys()])
|
bone_list = "\n".join([f"- {bone}" for bone in found_bones.keys()])
|
||||||
messages.append(t("Armature.validation.found_bones", bones=bone_list))
|
messages.append(t("Armature.validation.found_bones", bones=bone_list))
|
||||||
|
|
||||||
# Check each bone against our standard
|
# Basic validation for both STRICT and LIMITED modes
|
||||||
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
|
# Check for missing required bones
|
||||||
essential_bones = {standard_bones[key] for key in ['hips', 'spine', 'chest', 'neck', 'head']}
|
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]
|
missing_bones = [bone for bone in essential_bones if bone not in found_bones]
|
||||||
|
|
||||||
if missing_bones:
|
if missing_bones:
|
||||||
missing_list = "\n".join([f"- {bone}" for bone in missing_bones])
|
missing_list = "\n".join([f"- {bone}" for bone in missing_bones])
|
||||||
messages.append(t("Armature.validation.missing_bones", bones=missing_list))
|
hierarchy_messages.append(t("Armature.validation.missing_bones", bones=missing_list))
|
||||||
|
|
||||||
if validation_mode == 'STRICT':
|
if validation_mode == 'STRICT':
|
||||||
# Validate bone hierarchy
|
# Validate bone hierarchy
|
||||||
for parent, child in bone_hierarchy:
|
for parent, child in bone_hierarchy:
|
||||||
if parent in found_bones and child in found_bones:
|
if parent in found_bones and child in found_bones:
|
||||||
if not validate_bone_hierarchy(found_bones, parent, child):
|
if not validate_bone_hierarchy(found_bones, parent, child):
|
||||||
messages.append(t("Armature.validation.invalid_hierarchy",
|
hierarchy_messages.append(t("Armature.validation.invalid_hierarchy",
|
||||||
parent=parent, child=child))
|
parent=parent, child=child))
|
||||||
|
|
||||||
# Validate symmetry
|
# Validate symmetry
|
||||||
symmetry_pairs = [('arm', 'l', 'r'), ('leg', 'l', 'r')]
|
symmetry_pairs = [('arm', 'L', 'R'), ('leg', 'L', 'R')]
|
||||||
for base, left, right in symmetry_pairs:
|
for base, left, right in symmetry_pairs:
|
||||||
if not validate_symmetry(found_bones, base, left, right):
|
if not validate_symmetry(found_bones, base, left, right):
|
||||||
messages.append(t("Armature.validation.asymmetric_bones", bone=base))
|
hierarchy_messages.append(t("Armature.validation.asymmetric_bones", bone=base))
|
||||||
|
|
||||||
if (not validate_symmetry(found_bones, 'hand', 'l', 'r') and
|
if (not validate_symmetry(found_bones, 'hand', 'L', 'R') and
|
||||||
not validate_symmetry(found_bones, 'wrist', 'l', 'r')):
|
not validate_symmetry(found_bones, 'wrist', 'L', 'R')):
|
||||||
messages.append(t("Armature.validation.asymmetric_hand_wrist"))
|
hierarchy_messages.append(t("Armature.validation.asymmetric_hand_wrist"))
|
||||||
|
|
||||||
# Validate finger hierarchies
|
# Validate finger hierarchies
|
||||||
for side in ['left', 'right']:
|
for side in ['left', 'right']:
|
||||||
for finger_chain in finger_hierarchy[side]:
|
for finger_chain in finger_hierarchy[side]:
|
||||||
if all(bone in found_bones for bone in finger_chain):
|
if all(bone in found_bones for bone in finger_chain):
|
||||||
if not validate_finger_chain(found_bones, finger_chain):
|
if not validate_finger_chain(found_bones, finger_chain):
|
||||||
messages.append(t("Armature.validation.invalid_finger", finger=finger_chain[0]))
|
hierarchy_messages.append(t("Armature.validation.invalid_finger", finger=finger_chain[0]))
|
||||||
|
|
||||||
|
# Non-standard bones check
|
||||||
|
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):
|
||||||
|
is_standard = bone_name in standard_bones.values()
|
||||||
|
is_acceptable_bone = any(bone_name in names for names in acceptable_bone_names.values())
|
||||||
|
if not (is_standard or is_acceptable_bone):
|
||||||
|
non_standard_bones.append(bone_name)
|
||||||
|
|
||||||
|
if non_standard_bones:
|
||||||
|
non_standard_list = "\n".join([f"- {bone}" for bone in non_standard_bones])
|
||||||
|
non_standard_messages.append(t("Armature.validation.non_standard_bones", bones=non_standard_list))
|
||||||
|
|
||||||
is_valid = len(messages) == 0
|
# Combine messages in correct order
|
||||||
|
messages.extend(non_standard_messages)
|
||||||
|
messages.extend(hierarchy_messages)
|
||||||
|
|
||||||
|
is_valid = len(non_standard_messages) == 0 and len(hierarchy_messages) == 0
|
||||||
|
|
||||||
if not is_valid and is_acceptable:
|
if not is_valid and is_acceptable:
|
||||||
|
if non_standard_bones:
|
||||||
|
return False, messages, False
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
t("Armature.validation.acceptable_standard.success"),
|
t("Armature.validation.acceptable_standard.success"),
|
||||||
t("Armature.validation.acceptable_standard.note"),
|
t("Armature.validation.acceptable_standard.note"),
|
||||||
@@ -103,22 +113,31 @@ def validate_bone_hierarchy(bones: Dict[str, Bone], parent_name: str, child_name
|
|||||||
|
|
||||||
def validate_symmetry(bones: Dict[str, Bone], base: str, left: str, right: str) -> bool:
|
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"""
|
"""Validate if matching left and right bones exist for a given base bone name"""
|
||||||
left_patterns: List[str] = [
|
# Extract left and right bone names from both hierarchies
|
||||||
f"{base}.{left}",
|
left_bone_names = set()
|
||||||
f"{base}_{left}",
|
right_bone_names = set()
|
||||||
f"{left}_{base}"
|
|
||||||
]
|
|
||||||
|
|
||||||
right_patterns: List[str] = [
|
# Add standard bones
|
||||||
f"{base}.{right}",
|
for key, value in standard_bones.items():
|
||||||
f"{base}_{right}",
|
if base in key.lower():
|
||||||
f"{right}_{base}"
|
if '_l' in key.lower():
|
||||||
]
|
left_bone_names.add(value)
|
||||||
|
elif '_r' in key.lower():
|
||||||
|
right_bone_names.add(value)
|
||||||
|
|
||||||
|
# Add acceptable bones
|
||||||
|
for key, names in acceptable_bone_names.items():
|
||||||
|
if base in key.lower():
|
||||||
|
if '_l' in key.lower():
|
||||||
|
left_bone_names.update(names)
|
||||||
|
elif '_r' in key.lower():
|
||||||
|
right_bone_names.update(names)
|
||||||
|
|
||||||
left_exists: bool = any(pattern in bones for pattern in left_patterns)
|
# Check if at least one pair exists and matches
|
||||||
right_exists: bool = any(pattern in bones for pattern in right_patterns)
|
left_exists = any(name in bones for name in left_bone_names)
|
||||||
|
right_exists = any(name in bones for name in right_bone_names)
|
||||||
|
|
||||||
return left_exists and right_exists
|
return left_exists == right_exists
|
||||||
|
|
||||||
def validate_finger_chain(bones: Dict[str, Bone], chain: Tuple[str, ...]) -> bool:
|
def validate_finger_chain(bones: Dict[str, Bone], chain: Tuple[str, ...]) -> bool:
|
||||||
"""Validate if a finger bone chain has correct hierarchy"""
|
"""Validate if a finger bone chain has correct hierarchy"""
|
||||||
|
|||||||
+111
-109
@@ -44,6 +44,117 @@ def update_shape_intensity(self: PropertyGroup, context: Context) -> None:
|
|||||||
|
|
||||||
class AvatarToolkitSceneProperties(PropertyGroup):
|
class AvatarToolkitSceneProperties(PropertyGroup):
|
||||||
"""Property group containing Avatar Toolkit scene-level settings and properties"""
|
"""Property group containing Avatar Toolkit scene-level settings and properties"""
|
||||||
|
|
||||||
|
show_found_bones: BoolProperty(
|
||||||
|
name="Show Found Bones",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
show_non_standard: BoolProperty(
|
||||||
|
name="Show Non-Standard Bones",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
show_hierarchy: BoolProperty(
|
||||||
|
name="Show Hierarchy Issues",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
material_search_filter: StringProperty(
|
||||||
|
name=t("TextureAtlas.search_materials"),
|
||||||
|
description=t("TextureAtlas.search_materials_desc"),
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_texture_node_list(self: Material, context: Context) -> list[tuple]:
|
||||||
|
if self.use_nodes:
|
||||||
|
Object.Enum = [((i.image.name if i.image else i.name+"_image"),
|
||||||
|
(i.image.name if i.image else "node with no image..."),
|
||||||
|
(i.image.name if i.image else i.name),index+1)
|
||||||
|
for index,i in enumerate(self.node_tree.nodes)
|
||||||
|
if i.bl_idname == "ShaderNodeTexImage"]
|
||||||
|
if not len(Object.Enum):
|
||||||
|
Object.Enum = [(t("TextureAtlas.error.label"),
|
||||||
|
t("TextureAtlas.no_images_error.desc"),
|
||||||
|
t("TextureAtlas.error.label"), 0)]
|
||||||
|
else:
|
||||||
|
Object.Enum = [(t("TextureAtlas.error.label"),
|
||||||
|
t("TextureAtlas.no_nodes_error.desc"),
|
||||||
|
t("TextureAtlas.error.label"), 0)]
|
||||||
|
Object.Enum.append((t("TextureAtlas.none.label"),
|
||||||
|
t("TextureAtlas.none.label"),
|
||||||
|
t("TextureAtlas.none.label"), 0))
|
||||||
|
return Object.Enum
|
||||||
|
|
||||||
|
Material.texture_atlas_albedo = EnumProperty(
|
||||||
|
name=t("TextureAtlas.albedo"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.albedo").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.texture_atlas_normal = EnumProperty(
|
||||||
|
name=t("TextureAtlas.normal"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.normal").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.texture_atlas_emission = EnumProperty(
|
||||||
|
name=t("TextureAtlas.emission"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.emission").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.texture_atlas_ambient_occlusion = EnumProperty(
|
||||||
|
name=t("TextureAtlas.ambient_occlusion"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.ambient_occlusion").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.texture_atlas_height = EnumProperty(
|
||||||
|
name=t("TextureAtlas.height"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.texture_atlas_roughness = EnumProperty(
|
||||||
|
name=t("TextureAtlas.roughness"),
|
||||||
|
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.roughness").lower()),
|
||||||
|
default=0,
|
||||||
|
items=get_texture_node_list
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.include_in_atlas = BoolProperty(
|
||||||
|
name=t("TextureAtlas.include_in_atlas"),
|
||||||
|
description=t("TextureAtlas.include_in_atlas_desc"),
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
Material.material_expanded = BoolProperty(
|
||||||
|
name=t("TextureAtlas.material_expanded"),
|
||||||
|
description=t("TextureAtlas.material_expanded_desc"),
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
texture_atlas_Has_Mat_List_Shown: BoolProperty(
|
||||||
|
name=t("TextureAtlas.list_shown"),
|
||||||
|
description=t("TextureAtlas.list_shown_desc"),
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
texture_atlas_material_index: IntProperty(
|
||||||
|
default=-1,
|
||||||
|
get=lambda self: -1,
|
||||||
|
set=lambda self, context: None
|
||||||
|
)
|
||||||
|
|
||||||
|
materials: CollectionProperty(
|
||||||
|
type=SceneMatClass
|
||||||
|
)
|
||||||
|
|
||||||
avatar_toolkit_updater_version_list: EnumProperty(
|
avatar_toolkit_updater_version_list: EnumProperty(
|
||||||
items=get_version_list,
|
items=get_version_list,
|
||||||
@@ -407,121 +518,12 @@ class AvatarToolkitSceneProperties(PropertyGroup):
|
|||||||
description=t('MergeArmature.cleanup_shape_keys_desc'),
|
description=t('MergeArmature.cleanup_shape_keys_desc'),
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
material_search_filter: StringProperty(
|
|
||||||
name=t("TextureAtlas.search_materials"),
|
|
||||||
description=t("TextureAtlas.search_materials_desc"),
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_texture_node_list(self: Material, context: Context) -> list[tuple]:
|
|
||||||
if self.use_nodes:
|
|
||||||
Object.Enum = [((i.image.name if i.image else i.name+"_image"),
|
|
||||||
(i.image.name if i.image else "node with no image..."),
|
|
||||||
(i.image.name if i.image else i.name),index+1)
|
|
||||||
for index,i in enumerate(self.node_tree.nodes)
|
|
||||||
if i.bl_idname == "ShaderNodeTexImage"]
|
|
||||||
if not len(Object.Enum):
|
|
||||||
Object.Enum = [(t("TextureAtlas.error.label"),
|
|
||||||
t("TextureAtlas.no_images_error.desc"),
|
|
||||||
t("TextureAtlas.error.label"), 0)]
|
|
||||||
else:
|
|
||||||
Object.Enum = [(t("TextureAtlas.error.label"),
|
|
||||||
t("TextureAtlas.no_nodes_error.desc"),
|
|
||||||
t("TextureAtlas.error.label"), 0)]
|
|
||||||
Object.Enum.append((t("TextureAtlas.none.label"),
|
|
||||||
t("TextureAtlas.none.label"),
|
|
||||||
t("TextureAtlas.none.label"), 0))
|
|
||||||
return Object.Enum
|
|
||||||
|
|
||||||
Material.texture_atlas_albedo = EnumProperty(
|
|
||||||
name=t("TextureAtlas.albedo"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.albedo").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.texture_atlas_normal = EnumProperty(
|
|
||||||
name=t("TextureAtlas.normal"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.normal").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.texture_atlas_emission = EnumProperty(
|
|
||||||
name=t("TextureAtlas.emission"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.emission").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.texture_atlas_ambient_occlusion = EnumProperty(
|
|
||||||
name=t("TextureAtlas.ambient_occlusion"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.ambient_occlusion").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.texture_atlas_height = EnumProperty(
|
|
||||||
name=t("TextureAtlas.height"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.height").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.texture_atlas_roughness = EnumProperty(
|
|
||||||
name=t("TextureAtlas.roughness"),
|
|
||||||
description=t("TextureAtlas.texture_use_atlas.desc").format(name=t("TextureAtlas.roughness").lower()),
|
|
||||||
default=0,
|
|
||||||
items=get_texture_node_list
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.include_in_atlas = BoolProperty(
|
|
||||||
name=t("TextureAtlas.include_in_atlas"),
|
|
||||||
description=t("TextureAtlas.include_in_atlas_desc"),
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
Material.material_expanded = BoolProperty(
|
|
||||||
name=t("TextureAtlas.material_expanded"),
|
|
||||||
description=t("TextureAtlas.material_expanded_desc"),
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
texture_atlas_Has_Mat_List_Shown: BoolProperty(
|
|
||||||
name=t("TextureAtlas.list_shown"),
|
|
||||||
description=t("TextureAtlas.list_shown_desc"),
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
texture_atlas_material_index: IntProperty(
|
|
||||||
default=-1,
|
|
||||||
get=lambda self: -1,
|
|
||||||
set=lambda self, context: None
|
|
||||||
)
|
|
||||||
|
|
||||||
materials: CollectionProperty(
|
|
||||||
type=SceneMatClass
|
|
||||||
)
|
|
||||||
|
|
||||||
merge_twist_bones: BoolProperty(
|
merge_twist_bones: BoolProperty(
|
||||||
name=t("Tools.merge_twist_bones"),
|
name=t("Tools.merge_twist_bones"),
|
||||||
description=t("Tools.merge_twist_bones_desc"),
|
description=t("Tools.merge_twist_bones_desc"),
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
show_found_bones: BoolProperty(
|
|
||||||
name="Show Found Bones",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
show_non_standard: BoolProperty(
|
|
||||||
name="Show Non-Standard Bones",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
show_hierarchy: BoolProperty(
|
|
||||||
name="Show Hierarchy Issues",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
"""Register the Avatar Toolkit property group"""
|
"""Register the Avatar Toolkit property group"""
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class AvatarToolkit_OT_CombineMaterials(Operator):
|
|||||||
armature = get_active_armature(context)
|
armature = get_active_armature(context)
|
||||||
if not armature:
|
if not armature:
|
||||||
return False
|
return False
|
||||||
valid, _ = validate_armature(armature)
|
valid, _, _ = validate_armature(armature)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def execute(self, context: Context) -> Set[str]:
|
def execute(self, context: Context) -> Set[str]:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class AvatarToolkit_OT_JoinAllMeshes(Operator):
|
|||||||
if not armature:
|
if not armature:
|
||||||
return False
|
return False
|
||||||
valid: bool
|
valid: bool
|
||||||
valid, _ = validate_armature(armature)
|
valid, _, _ = validate_armature(armature)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def execute(self, context: Context) -> Set[str]:
|
def execute(self, context: Context) -> Set[str]:
|
||||||
@@ -69,7 +69,7 @@ class AvatarToolkit_OT_JoinSelectedMeshes(Operator):
|
|||||||
if not armature:
|
if not armature:
|
||||||
return False
|
return False
|
||||||
valid: bool
|
valid: bool
|
||||||
valid, _ = validate_armature(armature)
|
valid, _, _ = validate_armature(armature)
|
||||||
return (valid and
|
return (valid and
|
||||||
context.mode == 'OBJECT' and
|
context.mode == 'OBJECT' and
|
||||||
len([obj for obj in context.selected_objects if obj.type == 'MESH']) > 1)
|
len([obj for obj in context.selected_objects if obj.type == 'MESH']) > 1)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class AvatarToolkit_OT_RemoveDoublesAdvanced(Operator):
|
|||||||
armature = get_active_armature(context)
|
armature = get_active_armature(context)
|
||||||
if not armature:
|
if not armature:
|
||||||
return False
|
return False
|
||||||
valid, _ = validate_armature(armature)
|
valid, _, _ = validate_armature(armature)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def execute(self, context: Context) -> set[str]:
|
def execute(self, context: Context) -> set[str]:
|
||||||
@@ -111,7 +111,7 @@ class AvatarToolkit_OT_RemoveDoubles(Operator):
|
|||||||
armature = get_active_armature(context)
|
armature = get_active_armature(context)
|
||||||
if not armature:
|
if not armature:
|
||||||
return False
|
return False
|
||||||
valid, _ = validate_armature(armature)
|
valid, _, _ = validate_armature(armature)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def draw(self, context: Context) -> None:
|
def draw(self, context: Context) -> None:
|
||||||
|
|||||||
+70
-65
@@ -88,75 +88,81 @@ class AvatarToolKit_PT_QuickAccessPanel(Panel):
|
|||||||
|
|
||||||
info_box = col.box()
|
info_box = col.box()
|
||||||
|
|
||||||
if is_valid:
|
if not is_valid:
|
||||||
if is_acceptable:
|
# Display non-standard bones and hierarchy issues
|
||||||
# Show acceptable standard message
|
if len(messages) > 1:
|
||||||
|
# Found Bones section
|
||||||
|
validation_box = info_box.box()
|
||||||
|
row = validation_box.row()
|
||||||
|
row.prop(props, "show_found_bones", text=t("Validation.section.found_bones"), icon='TRIA_DOWN' if props.show_found_bones else 'TRIA_RIGHT', emboss=False)
|
||||||
|
if props.show_found_bones:
|
||||||
|
for line in messages[0].split('\n'):
|
||||||
|
validation_box.label(text=line)
|
||||||
|
|
||||||
|
# Main validation status
|
||||||
|
validation_box = info_box.box()
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.label(text=t("Validation.status.failed"))
|
||||||
|
|
||||||
|
# Detailed validation message
|
||||||
|
validation_box = info_box.box()
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.label(text=t("Validation.message.failed.line1"))
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.label(text=t("Validation.message.failed.line2"))
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.label(text=t("Validation.message.failed.line3"))
|
||||||
|
|
||||||
|
# Non-Standard Bones section
|
||||||
|
validation_box = info_box.box()
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.prop(props, "show_non_standard", text=t("Validation.section.non_standard"), icon='TRIA_DOWN' if props.show_non_standard else 'TRIA_RIGHT', emboss=False)
|
||||||
|
if props.show_non_standard:
|
||||||
|
for line in messages[1].split('\n'):
|
||||||
|
sub_row = validation_box.row()
|
||||||
|
sub_row.alert = True
|
||||||
|
sub_row.label(text=line)
|
||||||
|
|
||||||
|
# Hierarchy Issues section
|
||||||
|
validation_box = info_box.box()
|
||||||
|
row = validation_box.row()
|
||||||
|
row.alert = True
|
||||||
|
row.prop(props, "show_hierarchy", text=t("Validation.section.hierarchy"), icon='TRIA_DOWN' if props.show_hierarchy else 'TRIA_RIGHT', emboss=False)
|
||||||
|
if props.show_hierarchy:
|
||||||
|
for message in messages[2:]:
|
||||||
|
sub_row = validation_box.row()
|
||||||
|
sub_row.alert = True
|
||||||
|
sub_row.label(text=message)
|
||||||
|
else:
|
||||||
|
# If no specific issues, show acceptable message
|
||||||
info_box.label(text=messages[0], icon='INFO')
|
info_box.label(text=messages[0], icon='INFO')
|
||||||
info_box.label(text=messages[1])
|
info_box.label(text=messages[1])
|
||||||
info_box.label(text=messages[2])
|
info_box.label(text=messages[2])
|
||||||
|
elif is_valid and not is_acceptable:
|
||||||
# Add standardize button
|
row = info_box.row()
|
||||||
standardize_box = info_box.box()
|
split = row.split(factor=0.6)
|
||||||
standardize_box.operator("avatar_toolkit.standardize_armature",
|
split.label(text=t("QuickAccess.valid_armature"), icon='CHECKMARK')
|
||||||
text=t("QuickAccess.standardize_armature"),
|
stats = get_armature_stats(active_armature)
|
||||||
icon='MODIFIER')
|
split.label(text=t("QuickAccess.bones_count", count=stats['bone_count']))
|
||||||
else:
|
|
||||||
row = info_box.row()
|
|
||||||
split = row.split(factor=0.6)
|
|
||||||
split.label(text=t("QuickAccess.valid_armature"), icon='CHECKMARK')
|
|
||||||
stats = get_armature_stats(active_armature)
|
|
||||||
split.label(text=t("QuickAccess.bones_count", count=stats['bone_count']))
|
|
||||||
|
|
||||||
if stats['has_pose']:
|
|
||||||
info_box.label(text=t("QuickAccess.pose_bones_available"), icon='POSE_HLT')
|
|
||||||
else:
|
|
||||||
# Found Bones section
|
|
||||||
validation_box = info_box.box()
|
|
||||||
row = validation_box.row()
|
|
||||||
row.prop(props, "show_found_bones", text=t("Validation.section.found_bones"), icon='TRIA_DOWN' if props.show_found_bones else 'TRIA_RIGHT', emboss=False)
|
|
||||||
if props.show_found_bones:
|
|
||||||
for line in messages[0].split('\n'):
|
|
||||||
validation_box.label(text=line)
|
|
||||||
|
|
||||||
# Main validation status
|
if stats['has_pose']:
|
||||||
validation_box = info_box.box()
|
info_box.label(text=t("QuickAccess.pose_bones_available"), icon='POSE_HLT')
|
||||||
row = validation_box.row()
|
elif is_valid and is_acceptable:
|
||||||
row.alert = True
|
# Show acceptable standard message
|
||||||
row.label(text=t("Validation.status.failed"))
|
info_box.label(text=messages[0], icon='INFO')
|
||||||
|
info_box.label(text=messages[1])
|
||||||
|
info_box.label(text=messages[2])
|
||||||
|
|
||||||
# Detailed validation message
|
# Add standardize button
|
||||||
validation_box = info_box.box()
|
standardize_box = info_box.box()
|
||||||
row = validation_box.row()
|
standardize_box.operator("avatar_toolkit.standardize_armature",
|
||||||
row.alert = True
|
text=t("QuickAccess.standardize_armature"),
|
||||||
row.label(text=t("Validation.message.failed.line1"))
|
icon='MODIFIER')
|
||||||
row = validation_box.row()
|
|
||||||
row.alert = True
|
|
||||||
row.label(text=t("Validation.message.failed.line2"))
|
|
||||||
row = validation_box.row()
|
|
||||||
row.alert = True
|
|
||||||
row.label(text=t("Validation.message.failed.line3"))
|
|
||||||
|
|
||||||
# Non-Standard Bones section
|
|
||||||
validation_box = info_box.box()
|
|
||||||
row = validation_box.row()
|
|
||||||
row.alert = True
|
|
||||||
row.prop(props, "show_non_standard", text=t("Validation.section.non_standard"), icon='TRIA_DOWN' if props.show_non_standard else 'TRIA_RIGHT', emboss=False)
|
|
||||||
if props.show_non_standard:
|
|
||||||
for line in messages[1].split('\n'):
|
|
||||||
sub_row = validation_box.row()
|
|
||||||
sub_row.alert = True
|
|
||||||
sub_row.label(text=line)
|
|
||||||
|
|
||||||
# Hierarchy Issues section
|
|
||||||
validation_box = info_box.box()
|
|
||||||
row = validation_box.row()
|
|
||||||
row.alert = True
|
|
||||||
row.prop(props, "show_hierarchy", text=t("Validation.section.hierarchy"), icon='TRIA_DOWN' if props.show_hierarchy else 'TRIA_RIGHT', emboss=False)
|
|
||||||
if props.show_hierarchy:
|
|
||||||
for message in messages[2:]:
|
|
||||||
sub_row = validation_box.row()
|
|
||||||
sub_row.alert = True
|
|
||||||
sub_row.label(text=message)
|
|
||||||
|
|
||||||
# Validation Mode Warnings
|
# Validation Mode Warnings
|
||||||
validation_mode = context.scene.avatar_toolkit.validation_mode
|
validation_mode = context.scene.avatar_toolkit.validation_mode
|
||||||
@@ -196,4 +202,3 @@ class AvatarToolKit_PT_QuickAccessPanel(Panel):
|
|||||||
button_row.scale_y = 1.5
|
button_row.scale_y = 1.5
|
||||||
button_row.operator("avatar_toolkit.import", text=t("QuickAccess.import"), icon='IMPORT')
|
button_row.operator("avatar_toolkit.import", text=t("QuickAccess.import"), icon='IMPORT')
|
||||||
button_row.operator("avatar_toolkit.export", text=t("QuickAccess.export"), icon='EXPORT')
|
button_row.operator("avatar_toolkit.export", text=t("QuickAccess.export"), icon='EXPORT')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user