# SPDX-License-Identifier: MIT OR GPL-3.0-or-later from collections.abc import Set as AbstractSet from typing import TYPE_CHECKING, Optional from bpy.types import Context, Panel from .utils import ( MaterialInfo, find_body_mesh, get_material_face_count, is_skin_material, ) if TYPE_CHECKING: from bpy.types import Object class VRM_PT_body_mesh_separator(Panel): """Body网格分离面板""" bl_idname = "VRM_PT_body_mesh_separator" bl_label = "Body Mesh Separator" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "VRM" bl_options: AbstractSet[str] = set() @classmethod def poll(cls, context: Context) -> bool: """检查是否应该显示面板 - 仅在选中包含Body网格的Armature时显示""" armature = cls._get_armature(context) if armature is None: return False body_mesh = find_body_mesh(armature) return body_mesh is not None @classmethod def _get_armature(cls, context: Context) -> Optional["Object"]: """获取当前选中的Armature对象""" active_object = context.active_object if active_object is None: return None if active_object.type == "ARMATURE": return active_object # 如果选中的是网格,检查其父对象是否为Armature if active_object.parent is not None and active_object.parent.type == "ARMATURE": return active_object.parent return None def draw(self, context: Context) -> None: """绘制面板UI""" layout = self.layout armature = self._get_armature(context) if armature is None: layout.label(text="未找到Armature对象", icon="ERROR") return body_mesh = find_body_mesh(armature) if body_mesh is None: layout.label(text="未找到Body网格对象", icon="ERROR") return mesh_data = body_mesh.data if mesh_data is None: layout.label(text="Body网格数据无效", icon="ERROR") return # 显示材质统计信息 box = layout.box() box.label(text="材质列表", icon="MATERIAL") # 收集材质信息 materials_info: list[MaterialInfo] = [] for i, material in enumerate(mesh_data.materials): if material is not None: face_count = get_material_face_count(mesh_data, i) is_skin = is_skin_material(material.name) materials_info.append( MaterialInfo( name=material.name, index=i, face_count=face_count, is_skin=is_skin, ) ) # 显示材质列表 has_non_skin = False for mat_info in materials_info: row = box.row() if mat_info.is_skin: row.label(text=f"[皮肤] {mat_info.name}", icon="CHECKMARK") else: row.label(text=f"[非皮肤] {mat_info.name}", icon="MESH_DATA") has_non_skin = True row.label(text=f"{mat_info.face_count} 面") # 显示分离操作按钮 layout.separator() col = layout.column() col.enabled = has_non_skin col.operator( "vrm.separate_non_skin_faces", text="分离非皮肤面", icon="MESH_DATA", ) if not has_non_skin: layout.label(text="没有非皮肤材质可分离", icon="INFO")