MetaFusion/scripts/ui/meshes.py
2025-02-11 02:11:53 +08:00

591 lines
21 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.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===================================== 1. Module Imports =====================================
import maya.OpenMayaUI as omui
from scripts import config
import maya.cmds as cmds
import maya.mel as mel
import webbrowser
import sys
import os
from .. import config
QtCore, QtGui, QtWidgets, wrapInstance = config.Qt()
if QtCore is None or QtGui is None or QtWidgets is None or wrapInstance is None:
print(f"Qt加载失败: {QtCore}, {QtGui}, {QtWidgets}, {wrapInstance}")
class MeshItem(QtWidgets.QWidget):
"""单个网格项控件"""
def __init__(self, mesh_name, parent=None):
super(MeshItem, self).__init__(parent)
self._setup_ui(mesh_name)
self._create_connections()
def _setup_ui(self, mesh_name):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QHBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(4)
# === Widgets ===
# 网格名称标签
self.name_label = QtWidgets.QLabel(mesh_name)
self.name_label.setMinimumWidth(100)
# 网格路径输入框
self.path_edit = QtWidgets.QLineEdit()
self.path_edit.setReadOnly(True)
# 添加按钮
self.add_btn = QtWidgets.QPushButton("添加...")
self.add_btn.setFixedWidth(60)
# === Layout ===
self.main_layout.addWidget(self.name_label)
self.main_layout.addWidget(self.path_edit)
self.main_layout.addWidget(self.add_btn)
# === Style ===
self._setup_style()
def _setup_style(self):
"""设置样式"""
self.setStyleSheet("""
QWidget {
background: #2D2D2D;
}
QLabel {
color: #CCCCCC;
font-size: 12px;
}
QLineEdit {
background: #3D3D3D;
border: 1px solid #555555;
border-radius: 2px;
padding: 4px 8px;
color: #CCCCCC;
}
QPushButton {
background: #3D3D3D;
border: 1px solid #555555;
border-radius: 2px;
padding: 4px 12px;
color: #CCCCCC;
min-width: 60px;
}
QPushButton:hover {
background: #454545;
}
QPushButton:pressed {
background: #2A2A2A;
}
""")
def _create_connections(self):
"""创建信号连接"""
self.add_btn.clicked.connect(self._on_add_clicked)
def _on_add_clicked(self):
"""添加按钮点击"""
# TODO: 实现添加逻辑
pass
class MeshesTab(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MeshesTab, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
# === 顶部工具栏 ===
self.toolbar = QtWidgets.QWidget()
toolbar_layout = QtWidgets.QHBoxLayout(self.toolbar)
toolbar_layout.setContentsMargins(4, 4, 4, 4)
toolbar_layout.setSpacing(4)
# Meta-Human下拉框
self.preset_combo = QtWidgets.QComboBox()
self.preset_combo.addItem("Meta-Human")
self.preset_combo.setFixedWidth(120)
# 删除按钮移到LOD页面的功能按钮区域
toolbar_layout.addStretch()
# === 中间内容区 ===
self.content = QtWidgets.QWidget()
content_layout = QtWidgets.QVBoxLayout(self.content)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(0)
# LOD标签页
self.lod_tabs = QtWidgets.QTabWidget()
for i in range(8):
tab = self._create_lod_page(i)
self.lod_tabs.addTab(tab, f"LOD{i}")
content_layout.addWidget(self.lod_tabs)
# === 底部工具栏 ===
self.bottom_toolbar = self._create_bottom_toolbar()
# === 添加到主布局 ===
self.main_layout.addWidget(self.toolbar)
self.main_layout.addWidget(self.content)
self.main_layout.addWidget(self.bottom_toolbar)
# === 设置样式 ===
self._setup_style()
def _create_lod_page(self, lod_index):
"""创建单个LOD页面"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Main Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(8, 8, 8, 8)
layout.setSpacing(12) # 增加间距
# === 网格列表区域 ===
scroll = QtWidgets.QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameShape(QtWidgets.QFrame.NoFrame)
content = QtWidgets.QWidget()
content_layout = QtWidgets.QVBoxLayout(content)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(0) # 将项目间距改为0
# 创建网格项
meshes = config.LOD_MESHES[f"LOD{lod_index}"]
for mesh in meshes:
item = self._create_mesh_item(mesh)
content_layout.addWidget(item)
content_layout.addStretch()
scroll.setWidget(content)
# === 底部功能按钮 ===
buttons = QtWidgets.QWidget()
button_layout = QtWidgets.QHBoxLayout(buttons)
button_layout.setContentsMargins(0, 8, 0, 8)
# 创建功能按钮
load_btn = self._create_tool_button("自动加载模型", "load_meshes.png", "fileOpen.png")
standardize_btn = self._create_tool_button("标准化命名", "standardized_naming.png", "setEdNormalize.png")
group_btn = self._create_tool_button("自动分组", "automatic_grouping.png", "menuIconEdit.png")
delete_btn = self._create_tool_button("清理", "delete.png", "delete.png")
# 统一按钮高度
for btn in [load_btn, standardize_btn, group_btn, delete_btn]:
btn.setFixedHeight(32) # 设置为与模型分离按钮相同的高度
# 创建按钮容器
button_container = QtWidgets.QWidget()
container_layout = QtWidgets.QHBoxLayout(button_container)
container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.setSpacing(4) # 减小按钮间距为4
# 添加按钮到容器,使用弹性空间使按钮居中
container_layout.addStretch(1)
for btn in [load_btn, standardize_btn, group_btn, delete_btn]:
container_layout.addWidget(btn)
if btn != delete_btn: # 最后一个按钮后不添加间距
container_layout.addSpacing(4) # 添加固定间距
container_layout.addStretch(1)
button_layout.addWidget(button_container)
# === 添加到主布局 ===
layout.addWidget(scroll)
layout.addWidget(buttons)
# 添加窗口大小变化事件处理
def on_resize(event):
width = event.size().width()
available_width = width - 28 # 28是左右边距(16)和按钮间距(4*3)的总和
button_width = (available_width) // 4
for btn in [load_btn, standardize_btn, group_btn, delete_btn]:
btn.setFixedWidth(button_width)
super(type(widget), widget).resizeEvent(event)
widget.resizeEvent = on_resize
return widget
def _create_mesh_item(self, mesh_name):
"""创建网格项"""
item = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(item)
layout.setContentsMargins(4, 0, 4, 0) # 移除上下边距
layout.setSpacing(4)
# 设置item的最小高度确保内容完整显示
item.setMinimumHeight(40)
# 左侧容器(标签和输入框)
left_container = QtWidgets.QWidget()
left_layout = QtWidgets.QHBoxLayout(left_container)
left_layout.setContentsMargins(0, 0, 0, 0)
left_layout.setSpacing(1)
# 网格名称标签
name_label = QtWidgets.QLabel(mesh_name + ":") # 添加冒号
name_label.setFixedWidth(50) # 减小标签宽度
name_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
name_label.setStyleSheet("""
QLabel {
color: #CCCCCC;
padding-right: 0px;
}
""")
# 网格路径输入框
path_edit = QtWidgets.QLineEdit()
path_edit.setPlaceholderText("输入或选择模型路径...")
path_edit.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
path_edit.setFixedHeight(24)
path_edit.setStyleSheet("""
QLineEdit {
background: #1d1d1d;
border: 2px solid #444444; /* 调整边框颜色 */
border-radius: 5px; /* 减小圆角 */
padding: 2px 4px;
color: #CCCCCC;
}
QLineEdit:hover {
border: 1px solid #555555;
}
QLineEdit:focus {
border: 1px solid #666666;
background: #2A2A2A;
}
""")
left_layout.addWidget(name_label)
left_layout.addWidget(path_edit)
# 加载按钮
add_btn = QtWidgets.QPushButton("加载...")
add_btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/target.png"))
add_btn.setToolTip("浏览...")
add_btn.setFixedSize(100, 24)
add_btn.setStyleSheet("""
QPushButton {
background: #232323;
border: 2px solid #444444; /* 调整边框颜色 */
border-radius: 5px; /* 减小圆角 */
padding: 2px 4px;
color: #CCCCCC;
text-align: center; /* 居中对齐 */
}
QPushButton:hover {
background: #454545;
border: 1px solid #555555;
}
QPushButton:pressed {
background: #2A2A2A;
border: 1px solid #666666;
}
""")
# 添加到主布局
layout.addWidget(left_container, stretch=1)
layout.addWidget(add_btn)
return item
def _create_tool_button(self, text, icon_name, fallback_icon=None):
"""创建工具按钮"""
btn = QtWidgets.QPushButton(text)
# 尝试使用自定义图标如果失败则使用Maya内置图标
icon_path = f"{config.ICONS_PATH}/{icon_name}"
print(f"尝试加载图标: {icon_path}") # 添加调试输出
if os.path.exists(icon_path):
btn.setIcon(QtGui.QIcon(icon_path))
print(f"成功加载自定义图标: {icon_path}")
elif fallback_icon:
btn.setIcon(QtGui.QIcon(f":{fallback_icon}"))
print(f"使用Maya内置图标: {fallback_icon}")
else:
print(f"未能找到图标: {icon_path}")
btn.setStyleSheet("""
QPushButton {
background: #3D3D3D;
border: 1px solid #555555;
border-radius: 2px;
padding: 4px 8px;
color: #CCCCCC;
text-align: left;
icon-size: 16px;
min-height: 32px; # 统一按钮高度
}
QPushButton:hover {
background: #454545;
}
QPushButton:pressed {
background: #2A2A2A;
}
""")
return btn
def _create_bottom_toolbar(self):
"""创建底部工具栏"""
# === Widget ===
widget = QtWidgets.QWidget()
widget.setFixedHeight(180) # 增加高度
# === Main Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(8, 8, 8, 8) # 增加边距
layout.setSpacing(8)
# === 第一行工具栏 - 预设和LOD ===
first_row = QtWidgets.QWidget()
first_layout = QtWidgets.QHBoxLayout(first_row)
first_layout.setContentsMargins(4, 0, 4, 0)
first_layout.setSpacing(4)
# 预设名称
self.preset_label = QtWidgets.QLabel("预设名称:")
self.preset_name_combo = QtWidgets.QComboBox()
self.preset_name_combo.addItem("Meta-Human")
self.preset_name_combo.setFixedWidth(150)
self.preset_name_combo.setFixedHeight(32) # 设置为与模型分离按钮相同的高度
# 选择LOD
self.lod_label = QtWidgets.QLabel("选择LOD:")
self.lod_combo = QtWidgets.QComboBox()
self.lod_combo.addItems(["全部"] + [f"LOD{i}" for i in range(8)])
self.lod_combo.setFixedWidth(100)
self.lod_combo.setFixedHeight(32) # 设置为与模型分离按钮相同的高度
# 创建LOD按钮
self.create_lod_btn = QtWidgets.QPushButton()
self.create_lod_btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/create_lod.png"))
self.create_lod_btn.setText("创建LOD")
self.create_lod_btn.setFixedHeight(32) # 设置为与模型分离按钮相同的高度
first_layout.addWidget(self.preset_label)
first_layout.addWidget(self.preset_name_combo)
first_layout.addStretch()
first_layout.addWidget(self.lod_label)
first_layout.addWidget(self.lod_combo)
first_layout.addWidget(self.create_lod_btn)
# === 功能按钮区域 ===
buttons_widget = QtWidgets.QWidget()
buttons_layout = QtWidgets.QVBoxLayout(buttons_widget)
buttons_layout.setContentsMargins(4, 4, 4, 4)
buttons_layout.setSpacing(8)
# 创建按钮行
row1 = QtWidgets.QWidget()
row1_layout = QtWidgets.QHBoxLayout(row1)
row1_layout.setContentsMargins(0, 0, 0, 0)
row1_layout.setSpacing(8)
row2 = QtWidgets.QWidget()
row2_layout = QtWidgets.QHBoxLayout(row2)
row2_layout.setContentsMargins(0, 0, 0, 0)
row2_layout.setSpacing(8)
row3 = QtWidgets.QWidget()
row3_layout = QtWidgets.QHBoxLayout(row3)
row3_layout.setContentsMargins(0, 0, 0, 0)
row3_layout.setSpacing(8)
# 创建按钮
self.separate_btn = self._create_tool_button("模型分离", "separate.png", "polySplitVertex.png")
self.face_accessory_btn = self._create_tool_button("生成面部配件", "supplement_meshes.png", "polyCreateFacet.png")
self.fix_normal_btn = self._create_tool_button("修复法线", "repair_normals.png", "polyNormal.png")
self.fix_vertex_order_btn = self._create_tool_button("修复点序", "repair_vertex_order.png", "polyNormalPerVertex.png")
self.fix_seam_btn = self._create_tool_button("修复接缝", "fix_seam.png", "polyChipOff.png")
# 设置按钮固定宽度
button_width = (widget.width() - 24) // 2 # 24是左右边距和按钮间距(8)的总和
for btn in [self.separate_btn, self.face_accessory_btn,
self.fix_normal_btn, self.fix_vertex_order_btn,
self.fix_seam_btn]:
btn.setFixedWidth(button_width)
# 添加按钮到行
row1_layout.addStretch()
row1_layout.addWidget(self.separate_btn)
row1_layout.addWidget(self.face_accessory_btn)
row1_layout.addStretch()
row2_layout.addStretch()
row2_layout.addWidget(self.fix_normal_btn)
row2_layout.addWidget(self.fix_vertex_order_btn)
row2_layout.addStretch()
# 修复接缝按钮不居中
row3_layout.addWidget(self.fix_seam_btn)
row3_layout.addStretch()
# 添加行到布局
buttons_layout.addWidget(row1)
buttons_layout.addWidget(row2)
buttons_layout.addWidget(row3)
# === 添加到主布局 ===
layout.addWidget(first_row)
layout.addWidget(buttons_widget)
# 添加窗口大小变化事件处理
widget.resizeEvent = lambda event: self._adjust_button_widths(event, [
self.separate_btn, self.face_accessory_btn,
self.fix_normal_btn, self.fix_vertex_order_btn,
self.fix_seam_btn
])
return widget
def _adjust_button_widths(self, event, buttons):
"""调整按钮宽度以适应窗口大小"""
width = event.size().width()
button_width = (width - 24) // 2 # 24是左右边距和按钮间距(8)的总和
for btn in buttons:
btn.setFixedWidth(button_width)
def _setup_style(self):
"""设置样式"""
# 工具栏样式
self.toolbar.setStyleSheet("""
QWidget {
background: #2D2D2D;
}
QComboBox {
background: #3D3D3D;
border: 1px solid #555555;
border-radius: 2px;
padding: 4px;
color: #CCCCCC;
}
""")
# 标签页样式
self.lod_tabs.setStyleSheet("""
QTabWidget::pane {
border: none;
background: #2D2D2D;
}
QTabBar::tab {
background: #3D3D3D;
border: none;
min-width: 40px; # 减小最小宽度
padding: 4px 8px; # 减小内边距
color: #CCCCCC;
margin-right: 1px;
}
QTabBar::tab:selected {
background: #4D4D4D;
color: #FFFFFF;
}
QTabBar::tab:hover:!selected {
background: #454545;
}
QTabBar::tab:first {
margin-left: 0px;
}
""")
# 应用样式到下拉框
for combo in [self.preset_combo, self.preset_name_combo, self.lod_combo]:
combo.setStyleSheet("""
QComboBox {
background: #3D3D3D;
border: 1px solid #555555;
border-radius: 2px;
padding: 4px;
color: #CCCCCC;
}
""")
# 应用样式到标签
for label in [self.preset_label, self.lod_label]:
label.setStyleSheet("color: #CCCCCC;")
def _create_connections(self):
"""创建信号连接"""
# 顶部工具栏
self.preset_combo.currentIndexChanged.connect(self._on_preset_changed)
# 底部工具栏
self.create_lod_btn.clicked.connect(self._on_create_lod)
self.separate_btn.clicked.connect(self._on_separate)
self.face_accessory_btn.clicked.connect(self._on_face_accessory)
self.fix_normal_btn.clicked.connect(self._on_fix_normal)
self.fix_vertex_order_btn.clicked.connect(self._on_fix_vertex_order)
self.fix_seam_btn.clicked.connect(self._on_fix_seam)
# === Event Handlers ===
def _on_preset_changed(self, index):
"""预设改变"""
# TODO: 实现预设切换
pass
def _on_create_lod(self):
"""生成当前LOD"""
# TODO: 实现生成当前LOD
pass
def _on_separate(self):
"""模型分离"""
# TODO: 实现模型分离
pass
def _on_face_accessory(self):
"""生成面部配件"""
# TODO: 实现生成面部配件
pass
def _on_fix_normal(self):
"""修复法线"""
# TODO: 实现修复法线
pass
def _on_fix_vertex_order(self):
"""修复点序"""
# TODO: 实现修复点序
pass
def _on_fix_seam(self):
"""修复接缝"""
# TODO: 实现修复接缝
pass
def _on_load_meshes(self, lod_index):
"""自动加载模型"""
# TODO: 实现自动加载模型
print(f"加载 LOD{lod_index} 的模型")
pass
def _on_standardize_names(self, lod_index):
"""标准化命名"""
# TODO: 实现标准化命名
print(f"标准化 LOD{lod_index} 的命名")
pass
def _on_auto_group(self, lod_index):
"""自动分组"""
# TODO: 实现自动分组
print(f"自动分组 LOD{lod_index}")
pass
if __name__ == "__main__":
pass