Files
blender-vrm/editor/body_mesh/panel.py
小煜 21222044c4 feat(body_mesh): Add edge UV data for facial and hair mesh components
- Add edge UV coordinate data for N00_000_00_FaceEyelash_00_FACE mesh instance with 112 edges
- Add edge UV coordinate data for N00_000_00_HairBack_00_HAIR mesh instance with 800 edges
- Update body_mesh module initialization and operations to support new mesh data
- Enhance panel UI and utility functions for mesh edge UV handling
- Add comprehensive UV matching test suite for validation
- Update registration module to include new mesh components
- Improve test utilities for edge UV data verification
2026-01-02 13:32:03 +08:00

134 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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")