# 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, find_face_mesh, get_material_face_count, is_retained_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") # 收集材质信息,使用is_retained_material和is_separable_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_retained = is_retained_material(material.name) materials_info.append( MaterialInfo( name=material.name, index=i, face_count=face_count, is_skin=is_retained, # 使用is_skin字段存储保留状态 ) ) # 显示材质列表,区分保留材质(SKIN/HAIR)和可分离材质(CLOTH和其他) has_separable = False for mat_info in materials_info: row = box.row() if mat_info.is_skin: # is_skin字段现在表示是否为保留材质 # 保留材质(SKIN/HAIR)标记为"[保留]" row.label(text=f"[保留] {mat_info.name}", icon="CHECKMARK") else: # 可分离材质(CLOTH和其他)标记为"[分离]" row.label(text=f"[分离] {mat_info.name}", icon="MESH_DATA") has_separable = True row.label(text=f"{mat_info.face_count} 面") # 显示分离操作按钮 layout.separator() col = layout.column() col.enabled = has_separable col.operator( "vrm.separate_non_skin_faces", text="分离非皮肤面", icon="MESH_DATA", ) if not has_separable: layout.label(text="没有可分离材质", icon="INFO") # 显示UV边融并按钮 (Requirements 1.2, 1.3) layout.separator() armature = self._get_armature(context) face_mesh = find_face_mesh(armature) if armature else None has_vrm_structure = body_mesh is not None and face_mesh is not None col = layout.column() col.enabled = has_vrm_structure col.operator( "vrm.merge_uv_edges", text="根据UV转四边面", icon="MOD_EDGESPLIT", ) if not has_vrm_structure: layout.label(text="需要Body和Face网格", icon="INFO")