MetaFusion/scripts/ui/models.py
2025-02-06 06:29:55 +08:00

340 lines
13 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.cmds as cmds
import maya.mel as mel
import sys
import os
from scripts.ui.widgets import (BaseWidget, LODGroup, IconButton, SearchLineEdit)
try:
from PySide2 import QtCore, QtGui, QtWidgets
from shiboken2 import wrapInstance
print("从PySide2加载Qt和shiboken2")
except ImportError:
try:
from PySide6 import QtCore, QtGui, QtWidgets
from shiboken6 import wrapInstance
print("从PySide6加载Qt和shiboken6")
except ImportError:
try:
from PySide import QtCore, QtGui, QtWidgets
from shiboken import wrapInstance
print("从PySide加载Qt和shiboken")
except ImportError as e:
print(f"Qt加载失败: {str(e)}")
QtCore = QtGui = QtWidgets = None
wrapInstance = None
#===================================== 2. Model Tab Class =====================================
class ModelTab(BaseWidget):
"""模型标签页"""
def __init__(self, parent=None):
# 在super()之前初始化类属性
self.lod_tabs = None
self.lod_buttons = []
self.source_combo = None
self.target_combo = None
# 调用父类初始化
super(ModelTab, self).__init__(parent)
def setup_ui(self):
"""初始化UI"""
layout = QtWidgets.QVBoxLayout(self)
layout.setSpacing(10)
layout.setContentsMargins(10, 10, 10, 10)
# 创建标签页控件
self.lod_tabs = QtWidgets.QTabWidget()
layout.addWidget(self.lod_tabs)
# 每个LOD级别的模型类型定义
lod_models = {
0: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "软骨", "身体"],
1: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "软骨", "身体"],
2: ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "身体"],
3: ["头部", "牙齿", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "身体"],
4: ["头部", "牙齿", "左眼", "右眼", "虹膜"],
5: ["头部", "牙齿", "左眼", "右眼"],
6: ["头部", "牙齿", "左眼", "右眼"],
7: ["头部", "牙齿", "左眼", "右眼"]
}
# 创建LOD0-7标签页
for i in range(8):
tab = QtWidgets.QWidget()
tab_layout = QtWidgets.QVBoxLayout(tab)
tab_layout.setSpacing(10)
# 创建网格布局
grid = QtWidgets.QGridLayout()
grid.setSpacing(10)
# 获取当前LOD级别的模型类型
models = lod_models[i]
# 为每个模型类型创建控件
for idx, model in enumerate(models):
# 添加标签
label_widget = QtWidgets.QLabel(f"{model}:")
label_widget.setMinimumWidth(50)
grid.addWidget(label_widget, idx, 0)
# 添加输入框
line_edit = QtWidgets.QLineEdit()
line_edit.setReadOnly(True)
line_edit.setMinimumWidth(200)
grid.addWidget(line_edit, idx, 1)
# 添加加载按钮
btn = IconButton("target.png", "加载...")
btn.setMinimumWidth(80)
if i > 0: # LOD0以外的按钮默认禁用
btn.setEnabled(False)
grid.addWidget(btn, idx, 2)
self.lod_buttons.append(btn)
# 连接按钮信号
callback_name = f"load_{model.lower()}"
if hasattr(self, callback_name):
btn.clicked.connect(getattr(self, callback_name))
# 添加删除按钮
delete_btn = IconButton("delete.png", "")
delete_btn.clicked.connect(lambda x=i: self.delete_lod(x))
tab_layout.addWidget(delete_btn, alignment=QtCore.Qt.AlignRight)
tab_layout.addLayout(grid)
tab_layout.addStretch()
self.lod_tabs.addTab(tab, f"LOD{i}")
# LOD功能组
lod_tools = self.create_lod_tools()
layout.addWidget(lod_tools)
# 模型工具组
model_tools = self.create_model_tools()
layout.addWidget(model_tools)
def create_lod_tools(self):
"""创建LOD功能组"""
group = QtWidgets.QGroupBox("LOD功能")
layout = QtWidgets.QHBoxLayout(group)
layout.setSpacing(5)
# 创建三个按钮并设置为等宽
buttons = [
("自定义加载模型", "custom_load.png", self.load_custom_models),
("标准化命名", "standardize.png", self.standardize_naming),
("自动分组", "auto_group.png", self.auto_group)
]
for text, icon, callback in buttons:
btn = IconButton(icon, text)
btn.clicked.connect(callback)
btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
layout.addWidget(btn)
return group
def create_model_tools(self):
"""创建模型工具组"""
group = QtWidgets.QGroupBox("模型工具")
layout = QtWidgets.QVBoxLayout(group)
layout.setSpacing(10)
# 第一行选择栏和创建LOD按钮
top_layout = QtWidgets.QHBoxLayout()
top_layout.setSpacing(10)
# 源模型选择
source_layout = QtWidgets.QHBoxLayout()
source_label = QtWidgets.QLabel("源模型:")
source_label.setMinimumWidth(60)
self.source_combo = QtWidgets.QComboBox()
self.source_combo.addItems(["选择源模型..."])
self.source_combo.setMinimumWidth(120)
source_layout.addWidget(source_label)
source_layout.addWidget(self.source_combo)
top_layout.addLayout(source_layout)
# 目标LOD选择
target_layout = QtWidgets.QHBoxLayout()
target_label = QtWidgets.QLabel("目标LOD:")
target_label.setMinimumWidth(60)
self.target_combo = QtWidgets.QComboBox()
self.target_combo.addItems([f"LOD{i}" for i in range(8)])
self.target_combo.setMinimumWidth(120)
target_layout.addWidget(target_label)
target_layout.addWidget(self.target_combo)
top_layout.addLayout(target_layout)
# 创建LOD按钮
create_lod_btn = IconButton("create_lod.png", "创建LOD")
create_lod_btn.clicked.connect(self.create_lod)
create_lod_btn.setMinimumWidth(100)
top_layout.addWidget(create_lod_btn)
layout.addLayout(top_layout)
# 其他工具按钮,每行两个
tool_buttons = [
("模型分离", "polySplitVertex.png", self.split_model),
("生成面部配件", "supplement_meshes.png", self.generate_facial_accessories),
("修复法线", "repair_normals.png", self.fix_normals),
("修复点序", "repair_vertex_order.png", self.fix_vertex_order),
("修复接缝", "polyChipOff.png", self.fix_seams)
]
# 创建网格布局
grid = QtWidgets.QGridLayout()
grid.setSpacing(10)
for idx, (text, icon, callback) in enumerate(tool_buttons):
btn = IconButton(icon, text)
btn.clicked.connect(callback)
btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
grid.addWidget(btn, idx//2, idx%2)
layout.addLayout(grid)
return group
def create_lod(self):
"""创建LOD"""
# 获取选中的源模型和目标LOD
source_model = self.source_combo.currentText()
target_lod = int(self.target_combo.currentText().replace("LOD", ""))
# 启用目标LOD的所有按钮
for btn in self.lod_buttons:
if btn.parent().parent() == self.lod_tabs.widget(target_lod):
btn.setEnabled(True)
# LOD功能回调
def load_custom_models(self):
"""自定加载模型"""
from scripts.utils import model_utils
model_utils.load_custom_models()
def standardize_naming(self):
"""标准化命名"""
from scripts.utils import model_utils
model_utils.standardize_naming()
def auto_group(self):
"""自动分组"""
from scripts.utils import model_utils
model_utils.auto_group()
# 模型工具回调
def split_model(self):
"""分离模型"""
from scripts.utils import model_utils
model_utils.split_model()
def generate_facial_accessories(self):
"""生成面部配件"""
from scripts.utils import model_utils
model_utils.generate_facial_accessories()
def fix_normals(self):
"""修复法线"""
from scripts.utils import model_utils
model_utils.fix_normals()
def fix_vertex_order(self):
"""修复点序"""
from scripts.utils import model_utils
model_utils.fix_vertex_order()
def fix_seams(self):
"""修复接缝"""
from scripts.utils import model_utils
model_utils.fix_seams()
def load_head(self, lod_index):
"""加载头部模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("头部模型")
if file_path:
model_utils.load_model(lod_index, "head", file_path)
self._update_input_text(lod_index, "头部", file_path)
def load_teeth(self, lod_index):
"""加载牙齿模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("牙齿模型")
if file_path:
model_utils.load_model(lod_index, "teeth", file_path)
self._update_input_text(lod_index, "牙齿", file_path)
def load_gums(self, lod_index):
"""加载牙龈模型"""
from scripts.utils import model_utils
file_path = self._get_file_path("牙龈模型")
if file_path:
model_utils.load_model(lod_index, "gums", file_path)
self._update_input_text(lod_index, "牙龈", file_path)
def delete_lod(self, lod_index):
"""删除指定LOD"""
from scripts.utils import model_utils
reply = QtWidgets.QMessageBox.question(
self,
"删除LOD",
f"确定要删除LOD{lod_index}吗?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
model_utils.delete_lod(lod_index)
# 清空输入框
self._clear_lod_inputs(lod_index)
# 禁用按钮
if lod_index > 0:
self._disable_lod_buttons(lod_index)
def _get_file_path(self, model_type):
"""获取文件路径"""
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
f"选择{model_type}",
"",
"模型文件 (*.obj *.fbx *.ma *.mb);;所有文件 (*.*)"
)
return file_path
def _update_input_text(self, lod_index, model_type, file_path):
"""更新输入框文本"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout() # 获取网格布局
for i in range(grid.rowCount()):
label = grid.itemAtPosition(i, 0).widget()
if label.text().startswith(model_type):
input_field = grid.itemAtPosition(i, 1).widget()
input_field.setText(os.path.basename(file_path))
break
def _clear_lod_inputs(self, lod_index):
"""清空LOD的所有输入框"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout()
for i in range(grid.rowCount()):
input_field = grid.itemAtPosition(i, 1).widget()
if input_field:
input_field.clear()
def _disable_lod_buttons(self, lod_index):
"""禁用LOD的所有按钮"""
tab = self.lod_tabs.widget(lod_index)
if tab:
grid = tab.layout().itemAt(1).layout()
for i in range(grid.rowCount()):
btn = grid.itemAtPosition(i, 2).widget()
if btn:
btn.setEnabled(False)