2025-02-03 22:58:41 +08:00
|
|
|
|
#!/usr/bin/env python
|
2025-02-04 00:13:31 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
import os
|
2025-02-04 16:29:33 +08:00
|
|
|
|
import sys
|
2025-02-05 01:52:29 +08:00
|
|
|
|
import maya.cmds as cmds
|
|
|
|
|
import maya.OpenMayaUI as omui
|
|
|
|
|
from shiboken2 import wrapInstance
|
|
|
|
|
import traceback
|
2025-02-04 16:29:33 +08:00
|
|
|
|
|
2025-02-05 22:39:45 +08:00
|
|
|
|
|
|
|
|
|
from scripts.config import data
|
2025-02-04 00:13:31 +08:00
|
|
|
|
QtCore, QtGui, QtWidgets = data.Qt()
|
|
|
|
|
|
2025-02-04 16:29:33 +08:00
|
|
|
|
#===================================== 2. Global Variables =====================================
|
2025-02-05 22:39:45 +08:00
|
|
|
|
try:
|
|
|
|
|
ROOT_PATH = data.ROOT_PATH
|
|
|
|
|
except NameError:
|
|
|
|
|
ROOT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace("\\", "/")
|
2025-02-04 16:29:33 +08:00
|
|
|
|
TOOL_NAME = data.TOOL_NAME
|
|
|
|
|
TOOL_VERSION = data.TOOL_VERSION
|
|
|
|
|
TOOL_AUTHOR = data.TOOL_AUTHOR
|
|
|
|
|
TOOL_LANG = data.TOOL_LANG
|
|
|
|
|
TOOL_WSCL_NAME = data.TOOL_WSCL_NAME
|
|
|
|
|
TOOL_HELP_URL = data.TOOL_HELP_URL
|
|
|
|
|
SCRIPTS_PATH = data.SCRIPTS_PATH
|
|
|
|
|
ICONS_PATH = data.ICONS_PATH
|
|
|
|
|
TOOL_MAIN_SCRIPT = data.TOOL_MAIN_SCRIPT
|
|
|
|
|
TOOL_MOD_FILENAME = data.TOOL_MOD_FILENAME
|
|
|
|
|
TOOL_ICON = data.TOOL_ICON
|
|
|
|
|
TOOL_COMMAND_ICON = data.TOOL_COMMAND_ICON
|
2025-02-04 22:09:11 +08:00
|
|
|
|
DNA_PATH = data.DNA_PATH
|
|
|
|
|
DNA_IMG_PATH = data.DNA_IMG_PATH
|
2025-02-05 01:52:29 +08:00
|
|
|
|
DNALIB_PATH = data.DNALIB_PATH
|
|
|
|
|
BUILDER_PATH = data.BUILDER_PATH
|
2025-02-05 22:39:45 +08:00
|
|
|
|
UI_PATH = data.UI_PATH
|
|
|
|
|
UTILS_PATH = data.UTILS_PATH
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
main_window = None
|
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
class ModelTab(QtWidgets.QWidget):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.init_ui()
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
def init_ui(self):
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
# LOD导航栏
|
|
|
|
|
self.lod_tabs = QtWidgets.QTabWidget()
|
|
|
|
|
for i in range(1, 7):
|
|
|
|
|
tab = QtWidgets.QWidget()
|
|
|
|
|
self.lod_tabs.addTab(tab, f"LOD{i-1}")
|
|
|
|
|
layout.addWidget(self.lod_tabs)
|
|
|
|
|
|
|
|
|
|
# 模型操作工具栏
|
|
|
|
|
tool_bar = QtWidgets.QHBoxLayout()
|
|
|
|
|
self.add_button(tool_bar, "导入模型", "import.png", self.import_model)
|
|
|
|
|
self.add_button(tool_bar, "导出模型", "export.png", self.export_model)
|
|
|
|
|
layout.addLayout(tool_bar)
|
|
|
|
|
|
|
|
|
|
def add_button(self, layout, text, icon, callback):
|
|
|
|
|
btn = QtWidgets.QPushButton(QtGui.QIcon(os.path.join(data.ICONS_PATH, icon)), text)
|
|
|
|
|
btn.clicked.connect(callback)
|
|
|
|
|
layout.addWidget(btn)
|
|
|
|
|
|
|
|
|
|
def import_model(self): pass
|
|
|
|
|
def export_model(self): pass
|
|
|
|
|
|
|
|
|
|
class RigTab(QtWidgets.QWidget):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.init_ui()
|
|
|
|
|
|
|
|
|
|
def init_ui(self):
|
|
|
|
|
splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
|
|
|
|
|
|
|
|
|
|
# 左侧控制器列表
|
|
|
|
|
left_panel = QtWidgets.QWidget()
|
|
|
|
|
left_layout = QtWidgets.QVBoxLayout(left_panel)
|
|
|
|
|
self.controller_list = QtWidgets.QListWidget()
|
|
|
|
|
left_layout.addWidget(self.controller_list)
|
|
|
|
|
|
|
|
|
|
# 右侧视口区域
|
|
|
|
|
right_panel = QtWidgets.QWidget()
|
|
|
|
|
right_layout = QtWidgets.QVBoxLayout(right_panel)
|
|
|
|
|
self.viewport_label = QtWidgets.QLabel("3D视口区域")
|
|
|
|
|
right_layout.addWidget(self.viewport_label)
|
|
|
|
|
|
|
|
|
|
splitter.addWidget(left_panel)
|
|
|
|
|
splitter.addWidget(right_panel)
|
|
|
|
|
|
|
|
|
|
main_layout = QtWidgets.QHBoxLayout(self)
|
|
|
|
|
main_layout.addWidget(splitter)
|
|
|
|
|
|
|
|
|
|
class AdjustTab(QtWidgets.QWidget):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.init_ui()
|
|
|
|
|
|
|
|
|
|
def init_ui(self):
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self)
|
|
|
|
|
layout.addWidget(QtWidgets.QLabel("调整功能开发中..."))
|
|
|
|
|
|
|
|
|
|
class DefineTab(QtWidgets.QWidget):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.init_ui()
|
|
|
|
|
|
|
|
|
|
def init_ui(self):
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self)
|
|
|
|
|
layout.addWidget(QtWidgets.QLabel("定义功能开发中..."))
|
|
|
|
|
|
|
|
|
|
class MainWindow(QtWidgets.QMainWindow):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super(MainWindow, self).__init__(parent)
|
|
|
|
|
self.setup_core_components()
|
|
|
|
|
|
|
|
|
|
def setup_core_components(self):
|
|
|
|
|
"""初始化核心组件"""
|
|
|
|
|
self.setup_paths()
|
|
|
|
|
self.init_ui()
|
|
|
|
|
self.setup_connections()
|
|
|
|
|
|
|
|
|
|
def setup_paths(self):
|
|
|
|
|
"""配置系统路径"""
|
|
|
|
|
# 添加插件路径
|
|
|
|
|
if data.PLUGIN_PATH not in sys.path:
|
|
|
|
|
sys.path.insert(0, data.PLUGIN_PATH)
|
|
|
|
|
|
|
|
|
|
# 添加PyDNA二进制路径到环境变量
|
|
|
|
|
pydna_bin = os.path.join(data.PYDNA_PATH, "bin")
|
|
|
|
|
os.environ["PATH"] = f"{pydna_bin}{os.pathsep}{os.environ['PATH']}"
|
|
|
|
|
|
|
|
|
|
def init_ui(self):
|
|
|
|
|
"""初始化UI框架"""
|
|
|
|
|
self.setWindowTitle(f"{data.TOOL_NAME} {data.TOOL_VERSION}")
|
|
|
|
|
self.setMinimumSize(1200, 800)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
# 主窗口布局
|
|
|
|
|
main_widget = QtWidgets.QWidget()
|
|
|
|
|
self.setCentralWidget(main_widget)
|
|
|
|
|
main_layout = QtWidgets.QVBoxLayout(main_widget)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
# 创建核心组件
|
|
|
|
|
self.create_menu_bar()
|
|
|
|
|
self.create_toolbar()
|
|
|
|
|
self.create_tab_widget()
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
# 加载样式
|
|
|
|
|
self.load_styles()
|
|
|
|
|
|
|
|
|
|
def load_styles(self):
|
|
|
|
|
"""加载样式表"""
|
|
|
|
|
style_path = os.path.join(data.STYLES_PATH, "style.qss")
|
|
|
|
|
if os.path.exists(style_path):
|
|
|
|
|
with open(style_path, "r", encoding="utf-8") as f:
|
|
|
|
|
self.setStyleSheet(f.read())
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
def create_menu_bar(self):
|
|
|
|
|
"""创建菜单栏"""
|
|
|
|
|
menubar = self.menuBar()
|
|
|
|
|
|
|
|
|
|
# 文件菜单
|
|
|
|
|
file_menu = menubar.addMenu("文件")
|
2025-02-05 02:38:32 +08:00
|
|
|
|
self.add_menu_action(file_menu, "打开DNA", "open.png", lambda: self.load_dna())
|
|
|
|
|
self.add_menu_action(file_menu, "保存DNA", "save.png", lambda: self.save_dna())
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(file_menu, "加载当前项目的DNA", "open.png", self.on_load_project_dna)
|
|
|
|
|
self.add_menu_action(file_menu, "修改混合目标名称", "rename.png", self.on_rename_blend_target)
|
|
|
|
|
self.add_menu_action(file_menu, "重置混合目标名称", "resetname.png", self.on_reset_blend_target)
|
|
|
|
|
self.add_menu_action(file_menu, "导出FBX", "export.png", self.on_export_fbx)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
file_menu.addSeparator()
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(file_menu, "退出", "exit.png", self.close)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
|
|
|
|
# 编辑菜单
|
|
|
|
|
edit_menu = menubar.addMenu("编辑")
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(edit_menu, "创建RL4节点", "connect.png", self.create_rl4_node)
|
|
|
|
|
self.add_menu_action(edit_menu, "删除RL4节点", "disconnect.png", self.delete_rl4_node)
|
|
|
|
|
self.add_menu_action(edit_menu, "镜像左至右", "mirrorL.png", self.mirror_left_to_right)
|
|
|
|
|
self.add_menu_action(edit_menu, "镜像右至左", "mirrorR.png", self.mirror_right_to_left)
|
|
|
|
|
self.add_menu_action(edit_menu, "姿势由A型转T型", "pose_A_To_T.png", self.pose_A_to_T)
|
|
|
|
|
self.add_menu_action(edit_menu, "姿势由T型转A型", "pose_T_To_A.png", self.pose_T_to_A)
|
|
|
|
|
self.add_menu_action(edit_menu, "传输LOD贴图", "locator.png", self.transfer_lod_texture)
|
|
|
|
|
self.add_menu_action(edit_menu, "设置关节颜色", "color.png", self.set_joint_color)
|
|
|
|
|
self.add_menu_action(edit_menu, "取消全部标记", "unmark_all.png", self.unmark_all)
|
|
|
|
|
self.add_menu_action(edit_menu, "重建所有目标", "rebuildTargets.png", self.rebuild_targets)
|
|
|
|
|
self.add_menu_action(edit_menu, "为所有表情设置关键帧", "bakeAnimation.png", self.bake_all_animations)
|
|
|
|
|
self.add_menu_action(edit_menu, "烘焙所有表情的关键帧", "centerCurrentTime.png", self.bake_all_keyframes)
|
2025-02-04 22:09:11 +08:00
|
|
|
|
|
|
|
|
|
# 工具菜单
|
|
|
|
|
tool_menu = menubar.addMenu("工具")
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(tool_menu, "导出蒙皮", "export_skin.png", self.export_skin)
|
|
|
|
|
self.add_menu_action(tool_menu, "导入蒙皮", "import_skin.png", self.import_skin)
|
|
|
|
|
self.add_menu_action(tool_menu, "拷贝装皮", "copy_skin.png", self.copy_skin)
|
|
|
|
|
self.add_menu_action(tool_menu, "RBF变形器", "blendShape.png", self.create_blend_shape)
|
|
|
|
|
self.add_menu_action(tool_menu, "快速绑定服装", "clothing_weight.png", self.quick_bind_clothing)
|
|
|
|
|
self.add_menu_action(tool_menu, "克隆混合变形", "blendShape.png", self.clone_blend_shape)
|
|
|
|
|
self.add_menu_action(tool_menu, "UV传递点序", "repair_vertex_order.png", self.repair_vertex_order)
|
|
|
|
|
self.add_menu_action(tool_menu, "面部生成控制器", "controller.png", self.create_face_controller)
|
|
|
|
|
self.add_menu_action(tool_menu, "提取52BS", "ARKit52.png", self.extract_52BS)
|
|
|
|
|
self.add_menu_action(tool_menu, "关节轴向修复", "joint.png", self.repair_joint_axis)
|
|
|
|
|
self.add_menu_action(tool_menu, "生成身体控制器", "create_body_ctrl.png", self.create_body_controller)
|
|
|
|
|
self.add_menu_action(tool_menu, "导入面部动画", "import_face_anim.png", self.import_face_animation)
|
|
|
|
|
self.add_menu_action(tool_menu, "导入身体动画", "import_body_anim.png", self.import_body_animation)
|
2025-02-04 22:09:11 +08:00
|
|
|
|
|
|
|
|
|
# 语言菜单
|
|
|
|
|
lang_menu = menubar.addMenu("语言")
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(lang_menu, "中文", "chinese.png", self.set_chinese)
|
|
|
|
|
self.add_menu_action(lang_menu, "English", "english.png", self.set_english)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
|
|
|
|
# 帮助菜单
|
|
|
|
|
help_menu = menubar.addMenu("帮助")
|
2025-02-05 01:52:29 +08:00
|
|
|
|
self.add_menu_action(help_menu, "帮助文档", "help.png", self.show_help_document)
|
|
|
|
|
self.add_menu_action(help_menu, "关于", "warning.png", self.show_about_dialog)
|
|
|
|
|
|
2025-02-05 02:38:32 +08:00
|
|
|
|
def add_menu_action(self, menu, text, icon, callback):
|
2025-02-05 01:52:29 +08:00
|
|
|
|
"""通用菜单项添加方法"""
|
2025-02-05 02:10:47 +08:00
|
|
|
|
action = QtWidgets.QAction(text, self)
|
|
|
|
|
if icon:
|
2025-02-05 02:38:32 +08:00
|
|
|
|
# 优先使用Maya内置图标
|
|
|
|
|
maya_icon = self.get_maya_icon(icon)
|
|
|
|
|
if maya_icon:
|
|
|
|
|
action.setIcon(maya_icon)
|
|
|
|
|
else:
|
|
|
|
|
# 使用自定义图标
|
|
|
|
|
icon_path = os.path.join(data.ICONS_PATH, icon)
|
|
|
|
|
if os.path.exists(icon_path):
|
|
|
|
|
action.setIcon(QtGui.QIcon(icon_path))
|
|
|
|
|
else:
|
|
|
|
|
cmds.warning(f"图标文件不存在: {icon_path}")
|
2025-02-05 01:52:29 +08:00
|
|
|
|
action.triggered.connect(callback)
|
|
|
|
|
menu.addAction(action)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:38:32 +08:00
|
|
|
|
def get_maya_icon(self, icon_name):
|
|
|
|
|
"""获取Maya内置图标"""
|
|
|
|
|
maya_icons = {
|
|
|
|
|
"bakeAnimation.png": "BakeSimulation.png",
|
|
|
|
|
"centerCurrentTime.png": "timeCurrentFrame.png",
|
|
|
|
|
"export_skin.png": "kinReroot.png",
|
|
|
|
|
# 添加更多图标映射...
|
|
|
|
|
}
|
|
|
|
|
if icon_name in maya_icons:
|
|
|
|
|
return QtGui.QIcon(f":/{maya_icons[icon_name]}")
|
|
|
|
|
return None
|
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
def create_toolbar(self):
|
2025-02-04 00:13:31 +08:00
|
|
|
|
"""创建工具栏"""
|
|
|
|
|
toolbar = self.addToolBar("主工具栏")
|
|
|
|
|
toolbar.setMovable(False)
|
|
|
|
|
|
|
|
|
|
# 添加工具栏按钮
|
2025-02-04 22:09:11 +08:00
|
|
|
|
toolbar.addAction(QtGui.QIcon(os.path.join(ICONS_PATH, "save.png")), "保存DNA")
|
|
|
|
|
toolbar.addAction(QtGui.QIcon(os.path.join(ICONS_PATH, "open.png")), "加载当前项目DNA")
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
def create_tab_widget(self):
|
|
|
|
|
"""创建主标签页"""
|
2025-02-04 00:13:31 +08:00
|
|
|
|
self.tab_widget = QtWidgets.QTabWidget()
|
|
|
|
|
self.centralWidget().layout().addWidget(self.tab_widget)
|
2025-02-05 02:10:47 +08:00
|
|
|
|
|
|
|
|
|
# 初始化各标签页
|
|
|
|
|
self.model_tab = ModelTab()
|
|
|
|
|
self.rig_tab = RigTab()
|
|
|
|
|
self.adjust_tab = AdjustTab()
|
|
|
|
|
self.define_tab = DefineTab()
|
|
|
|
|
|
|
|
|
|
# 添加标签页
|
2025-02-04 00:13:31 +08:00
|
|
|
|
self.tab_widget.addTab(self.model_tab, "模型")
|
|
|
|
|
self.tab_widget.addTab(self.rig_tab, "绑定")
|
|
|
|
|
self.tab_widget.addTab(self.adjust_tab, "调整")
|
|
|
|
|
self.tab_widget.addTab(self.define_tab, "定义")
|
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
def setup_connections(self):
|
|
|
|
|
"""建立信号连接"""
|
|
|
|
|
# 示例:连接标签页切换信号
|
|
|
|
|
self.tab_widget.currentChanged.connect(self.on_tab_changed)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-05 02:10:47 +08:00
|
|
|
|
def on_tab_changed(self, index):
|
|
|
|
|
"""标签页切换回调"""
|
|
|
|
|
current_tab = self.tab_widget.widget(index)
|
|
|
|
|
print(f"切换到标签页: {current_tab.objectName()}")
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
def setup_model_tab(self):
|
|
|
|
|
"""设置模型标签页内容"""
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self.model_tab)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
2025-02-04 22:09:11 +08:00
|
|
|
|
# 创建 LOD 分栏
|
|
|
|
|
self.lod_tabs = QtWidgets.QTabWidget()
|
|
|
|
|
layout.addWidget(self.lod_tabs)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
2025-02-04 22:09:11 +08:00
|
|
|
|
# 添加 LOD 分页
|
|
|
|
|
for i in range(8):
|
|
|
|
|
lod_tab = QtWidgets.QWidget()
|
|
|
|
|
lod_layout = QtWidgets.QVBoxLayout(lod_tab)
|
|
|
|
|
|
|
|
|
|
# 添加删除按钮
|
|
|
|
|
delete_btn = QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "delete.png")), "删除")
|
|
|
|
|
lod_layout.addWidget(delete_btn)
|
|
|
|
|
|
|
|
|
|
# 添加模型输入框和加载按钮
|
|
|
|
|
for part in ["头部", "牙齿", "牙龈", "左眼", "右眼", "虹膜", "睫毛", "眼睑", "软骨", "身体"]:
|
|
|
|
|
part_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
part_input = QtWidgets.QLineEdit()
|
|
|
|
|
load_btn = QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "target.png")), "加载")
|
|
|
|
|
part_layout.addWidget(part_input)
|
|
|
|
|
part_layout.addWidget(load_btn)
|
|
|
|
|
lod_layout.addLayout(part_layout)
|
|
|
|
|
|
|
|
|
|
self.lod_tabs.addTab(lod_tab, f"LOD{i}")
|
|
|
|
|
|
|
|
|
|
# 添加 LOD 功能按钮
|
|
|
|
|
lod_func_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
lod_func_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "load_meshes.png")), "自定加载模型"))
|
|
|
|
|
lod_func_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "standardized_naming.png")), "标准化命名"))
|
|
|
|
|
lod_func_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "automatic_groupingg.png")), "自动分组"))
|
|
|
|
|
layout.addLayout(lod_func_layout)
|
|
|
|
|
|
|
|
|
|
# 添加模型工具
|
|
|
|
|
model_tool_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QComboBox()) # 拓扑结构下拉菜单
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QComboBox()) # 选择LOD下拉菜单
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "polySplitVertex.png")), "模型分离"))
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "supplement_meshes.png")), "生成面部配件"))
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "repair_normals.png")), "修复法线"))
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "repair_vertex_order.png")), "修复点序"))
|
|
|
|
|
model_tool_layout.addWidget(QtWidgets.QPushButton(QtGui.QIcon(os.path.join(ICONS_PATH, "polyChipOff.png")), "修复接缝"))
|
|
|
|
|
layout.addLayout(model_tool_layout)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
|
|
|
|
def setup_rig_tab(self):
|
|
|
|
|
"""设置绑定标签页内容"""
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self.rig_tab)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
2025-02-04 22:09:11 +08:00
|
|
|
|
# 添加 DNA 浏览器
|
|
|
|
|
self.dna_browser = QtWidgets.QListWidget()
|
|
|
|
|
self.dna_browser.setIconSize(QtCore.QSize(64, 64))
|
|
|
|
|
layout.addWidget(self.dna_browser)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
2025-02-04 22:09:11 +08:00
|
|
|
|
# 添加 DNA 图标缩放滑块
|
|
|
|
|
self.dna_scale_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
|
|
|
|
|
self.dna_scale_slider.setMinimum(50)
|
|
|
|
|
self.dna_scale_slider.setMaximum(200)
|
|
|
|
|
self.dna_scale_slider.setValue(100)
|
|
|
|
|
self.dna_scale_slider.valueChanged.connect(self.on_dna_scale_changed)
|
|
|
|
|
layout.addWidget(self.dna_scale_slider)
|
|
|
|
|
|
|
|
|
|
# 添加导入/导出按钮
|
2025-02-04 16:58:34 +08:00
|
|
|
|
button_layout = QtWidgets.QHBoxLayout()
|
2025-02-04 22:09:11 +08:00
|
|
|
|
import_btn = QtWidgets.QPushButton("导入设置")
|
|
|
|
|
export_btn = QtWidgets.QPushButton("导出设置")
|
|
|
|
|
import_btn.clicked.connect(self.on_import_settings)
|
|
|
|
|
export_btn.clicked.connect(self.on_export_settings)
|
|
|
|
|
button_layout.addWidget(import_btn)
|
|
|
|
|
button_layout.addWidget(export_btn)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
layout.addLayout(button_layout)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
2025-02-04 22:09:11 +08:00
|
|
|
|
# 初始化 DNA 浏览器
|
|
|
|
|
self.init_dna_browser()
|
|
|
|
|
|
|
|
|
|
def init_dna_browser(self):
|
|
|
|
|
"""初始化 DNA 浏览器"""
|
|
|
|
|
# 清空现有内容
|
|
|
|
|
self.dna_browser.clear()
|
|
|
|
|
|
|
|
|
|
# 获取 DNA 文件列表
|
|
|
|
|
dna_files = os.listdir(DNA_PATH)
|
|
|
|
|
img_files = os.listdir(DNA_IMG_PATH)
|
|
|
|
|
|
|
|
|
|
# 添加 DNA 项目
|
|
|
|
|
for dna_file in dna_files:
|
|
|
|
|
item = QtWidgets.QListWidgetItem(dna_file)
|
|
|
|
|
# 查找对应的图片
|
|
|
|
|
img_name = os.path.splitext(dna_file)[0] + ".png"
|
|
|
|
|
if img_name in img_files:
|
|
|
|
|
item.setIcon(QtGui.QIcon(os.path.join(data.DNA_IMG_PATH, img_name)))
|
|
|
|
|
self.dna_browser.addItem(item)
|
|
|
|
|
|
|
|
|
|
def on_dna_scale_changed(self, value):
|
|
|
|
|
"""处理 DNA 图标缩放"""
|
|
|
|
|
size = int(64 * (value / 100))
|
|
|
|
|
self.dna_browser.setIconSize(QtCore.QSize(size, size))
|
|
|
|
|
|
|
|
|
|
def on_import_settings(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def on_export_settings(self):
|
|
|
|
|
"""导出设置"""
|
|
|
|
|
# TODO: 实现导出设置功能
|
|
|
|
|
pass
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
def setup_adjust_tab(self):
|
|
|
|
|
"""设置调整标签页内容"""
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self.adjust_tab)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
|
|
|
|
# 添加BlendShape列表
|
|
|
|
|
blend_list = QtWidgets.QListWidget()
|
|
|
|
|
layout.addWidget(blend_list)
|
|
|
|
|
|
|
|
|
|
# 添加滑块组
|
|
|
|
|
slider_layout = QtWidgets.QVBoxLayout()
|
|
|
|
|
for i in range(5):
|
|
|
|
|
slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
|
|
|
|
|
slider_layout.addWidget(slider)
|
|
|
|
|
layout.addLayout(slider_layout)
|
|
|
|
|
|
|
|
|
|
# 添加按钮组
|
|
|
|
|
button_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
edit_btn = QtWidgets.QPushButton("编辑BlendShape")
|
|
|
|
|
save_btn = QtWidgets.QPushButton("保存设置")
|
|
|
|
|
button_layout.addWidget(edit_btn)
|
|
|
|
|
button_layout.addWidget(save_btn)
|
|
|
|
|
layout.addLayout(button_layout)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
|
|
|
|
def setup_define_tab(self):
|
|
|
|
|
"""设置定义标签页内容"""
|
|
|
|
|
layout = QtWidgets.QVBoxLayout(self.define_tab)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
|
|
|
|
# 添加DNA编辑区域
|
|
|
|
|
self.dna_edit = QtWidgets.QTextEdit()
|
|
|
|
|
layout.addWidget(self.dna_edit)
|
|
|
|
|
|
|
|
|
|
# 添加按钮组
|
|
|
|
|
button_layout = QtWidgets.QHBoxLayout()
|
|
|
|
|
self.load_btn = QtWidgets.QPushButton("载入DNA")
|
|
|
|
|
self.save_btn = QtWidgets.QPushButton("保存DNA")
|
|
|
|
|
self.export_btn = QtWidgets.QPushButton("导出FBX")
|
|
|
|
|
|
|
|
|
|
# 连接信号槽
|
|
|
|
|
self.load_btn.clicked.connect(self.on_load_dna)
|
|
|
|
|
self.save_btn.clicked.connect(self.on_save_dna)
|
|
|
|
|
self.export_btn.clicked.connect(self.on_export_fbx)
|
|
|
|
|
|
|
|
|
|
button_layout.addWidget(self.load_btn)
|
|
|
|
|
button_layout.addWidget(self.save_btn)
|
|
|
|
|
button_layout.addWidget(self.export_btn)
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
# 创建DNA管理器
|
|
|
|
|
self.dna_manager = DNAManager()
|
|
|
|
|
|
2025-02-05 02:38:32 +08:00
|
|
|
|
def on_load_dna(self): pass
|
|
|
|
|
def on_save_dna(self): pass
|
|
|
|
|
def on_export_fbx(self): pass
|
|
|
|
|
def on_open_dna(self): pass
|
|
|
|
|
def on_load_project_dna(self): pass
|
|
|
|
|
def on_rename_blend_target(self): pass
|
|
|
|
|
def on_reset_blend_target(self): pass
|
|
|
|
|
def create_rl4_node(self): pass
|
|
|
|
|
def delete_rl4_node(self): pass
|
|
|
|
|
def mirror_left_to_right(self): pass
|
|
|
|
|
def mirror_right_to_left(self): pass
|
|
|
|
|
def pose_A_to_T(self): pass
|
|
|
|
|
def pose_T_to_A(self): pass
|
|
|
|
|
def transfer_lod_texture(self): pass
|
|
|
|
|
def set_joint_color(self): pass
|
|
|
|
|
def unmark_all(self): pass
|
|
|
|
|
def rebuild_targets(self): pass
|
|
|
|
|
def bake_all_animations(self): pass
|
|
|
|
|
def bake_all_keyframes(self): pass
|
|
|
|
|
def safe_shutdown(self): pass
|
|
|
|
|
def export_skin(self): pass
|
|
|
|
|
def import_skin(self): pass
|
|
|
|
|
def copy_skin(self): pass
|
|
|
|
|
def create_blend_shape(self): pass
|
|
|
|
|
def quick_bind_clothing(self): pass
|
|
|
|
|
def clone_blend_shape(self): pass
|
|
|
|
|
def repair_vertex_order(self): pass
|
|
|
|
|
def create_face_controller(self): pass
|
|
|
|
|
def extract_52BS(self): pass
|
|
|
|
|
def repair_joint_axis(self): pass
|
|
|
|
|
def create_body_controller(self): pass
|
|
|
|
|
def import_face_animation(self): pass
|
|
|
|
|
def import_body_animation(self): pass
|
|
|
|
|
|
|
|
|
|
# 添加语言支持方法
|
|
|
|
|
def set_chinese(self):
|
|
|
|
|
"""设置中文界面"""
|
|
|
|
|
print("切换中文界面(待实现)")
|
|
|
|
|
# TODO: 实现国际化切换
|
|
|
|
|
|
|
|
|
|
def set_english(self):
|
|
|
|
|
"""设置英文界面"""
|
|
|
|
|
print("切换英文界面(待实现)")
|
|
|
|
|
# TODO: 实现国际化切换
|
|
|
|
|
|
|
|
|
|
# 添加帮助相关方法
|
|
|
|
|
def show_help_document(self):
|
|
|
|
|
"""显示帮助文档"""
|
|
|
|
|
print("打开帮助文档(待实现)")
|
|
|
|
|
# TODO: 实现帮助文档打开逻辑
|
|
|
|
|
|
|
|
|
|
def show_about_dialog(self):
|
|
|
|
|
"""显示关于对话框"""
|
|
|
|
|
print("显示关于信息(待实现)")
|
|
|
|
|
# TODO: 实现关于对话框
|
2025-02-05 01:52:29 +08:00
|
|
|
|
|
|
|
|
|
# ===================================== 显示主窗口 =====================================
|
|
|
|
|
def get_maya_window():
|
|
|
|
|
"""获取 Maya 主窗口"""
|
|
|
|
|
import maya.OpenMayaUI as omui
|
|
|
|
|
from shiboken2 import wrapInstance
|
|
|
|
|
|
|
|
|
|
maya_main_window_ptr = omui.MQtUtil.mainWindow()
|
|
|
|
|
return wrapInstance(int(maya_main_window_ptr), QtWidgets.QWidget)
|
|
|
|
|
|
|
|
|
|
def dock_to_maya():
|
|
|
|
|
"""将窗口嵌入到 Maya 的 Dock 面板"""
|
|
|
|
|
try:
|
|
|
|
|
# 先清理可能存在的旧控件
|
2025-02-05 02:10:47 +08:00
|
|
|
|
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
|
|
|
|
|
cmds.deleteUI(TOOL_WSCL_NAME)
|
2025-02-04 16:58:34 +08:00
|
|
|
|
|
2025-02-05 01:52:29 +08:00
|
|
|
|
# 创建新的Dock控件
|
|
|
|
|
dock_control = cmds.workspaceControl(
|
2025-02-05 02:10:47 +08:00
|
|
|
|
TOOL_WSCL_NAME,
|
2025-02-05 01:52:29 +08:00
|
|
|
|
label=TOOL_NAME,
|
|
|
|
|
tabToControl=["AttributeEditor", -1],
|
|
|
|
|
initialWidth=1400,
|
|
|
|
|
minimumWidth=1000,
|
|
|
|
|
minimumHeight=800,
|
|
|
|
|
widthProperty="free",
|
|
|
|
|
heightProperty="free"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 获取Dock控件指针
|
|
|
|
|
maya_dock_ptr = omui.MQtUtil.findControl(dock_control)
|
|
|
|
|
if not maya_dock_ptr:
|
|
|
|
|
raise RuntimeError("无法获取Dock控件指针")
|
|
|
|
|
|
|
|
|
|
# 获取Maya主窗口并创建实例
|
|
|
|
|
maya_main_window = get_maya_window()
|
2025-02-05 02:10:47 +08:00
|
|
|
|
main_window = MainWindow(parent=maya_main_window)
|
2025-02-05 01:52:29 +08:00
|
|
|
|
|
|
|
|
|
# 嵌入到Dock
|
|
|
|
|
maya_dock_widget = wrapInstance(int(maya_dock_ptr), QtWidgets.QWidget)
|
|
|
|
|
maya_dock_widget.layout().addWidget(main_window)
|
|
|
|
|
|
|
|
|
|
return main_window
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error_msg = f"Dock嵌入失败: {str(e)}\n{''.join(traceback.format_exc())}"
|
|
|
|
|
cmds.warning(error_msg)
|
|
|
|
|
return None
|
2025-02-04 00:13:31 +08:00
|
|
|
|
|
|
|
|
|
def show():
|
|
|
|
|
"""显示主窗口"""
|
|
|
|
|
global main_window
|
|
|
|
|
|
|
|
|
|
try:
|
2025-02-05 01:52:29 +08:00
|
|
|
|
# 清理旧实例
|
|
|
|
|
if main_window:
|
|
|
|
|
main_window.close()
|
2025-02-05 02:10:47 +08:00
|
|
|
|
cmds.deleteUI(TOOL_WSCL_NAME)
|
2025-02-04 00:13:31 +08:00
|
|
|
|
except:
|
|
|
|
|
pass
|
2025-02-05 01:52:29 +08:00
|
|
|
|
|
|
|
|
|
# 尝试嵌入 Dock
|
|
|
|
|
main_window = dock_to_maya()
|
|
|
|
|
|
|
|
|
|
# 备用方案:独立窗口模式
|
|
|
|
|
if not main_window:
|
|
|
|
|
cmds.warning("Dock 模式失败,使用独立窗口模式")
|
2025-02-05 02:10:47 +08:00
|
|
|
|
main_window = MainWindow()
|
2025-02-05 01:52:29 +08:00
|
|
|
|
main_window.show()
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
return main_window
|
|
|
|
|
|
2025-02-05 01:52:29 +08:00
|
|
|
|
# ===================================== 主函数 =====================================
|
|
|
|
|
|
2025-02-04 00:13:31 +08:00
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
app = QtWidgets.QApplication([])
|
|
|
|
|
window = show()
|
|
|
|
|
app.exec_()
|
|
|
|
|
|