MetaFusion/scripts/MetaFusion.py

473 lines
17 KiB
Python
Raw Normal View History

2025-01-11 16:53:48 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===================================== IMPORTS =====================================
# Standard library imports
import os
import sys
import webbrowser
import locale
# Qt imports
from PySide2 import QtWidgets, QtCore, QtGui
from shiboken2 import wrapInstance
# Maya imports
from maya import OpenMayaUI as omui
import maya.cmds as cmds
import maya.mel as mel
#===================================== IMPORT MODULES =====================================
# Standard library imports
import BodyPrep
import BatchImport
2025-01-11 17:38:05 +08:00
import dna_viewer
2025-01-11 16:53:48 +08:00
import DNA_Browser
from DNA_Browser import FlowLayout
#===================================== CONSTANTS =====================================
# Tool info
TOOL_NAME = "MetaFusion"
TOOL_VERSION = "Beta v1.0.0"
TOOL_AUTHOR = "Virtuos"
# UI Constants
TOOL_WSCL_NAME = "MetaFusionWorkSpaceControl"
TOOL_HELP_URL = f"http://10.72.61.59:3000/ArtGroup/{TOOL_NAME}/wiki"
2025-01-11 18:26:42 +08:00
DEFAULT_WINDOW_SIZE = (450, 800)
2025-01-11 16:53:48 +08:00
# Paths
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace("\\", "/")
SCRIPTS_PATH = os.path.join(TOOL_PATH, "scripts").replace("\\", "/")
ICONS_PATH = os.path.join(TOOL_PATH, "icons").replace("\\", "/")
TOOL_ICON = os.path.join(ICONS_PATH, "logo.png").replace("\\", "/")
# Metahuman paths
DATA_PATH = os.path.join(TOOL_PATH, "data").replace("\\", "/")
DNA_PATH = os.path.join(DATA_PATH, "dna").replace("\\", "/")
BODY_PATH = os.path.join(DATA_PATH, "body").replace("\\", "/")
IMG_PATH = os.path.join(DATA_PATH, "img").replace("\\", "/")
MAP_PATH = os.path.join(DATA_PATH, "map").replace("\\", "/")
MASKS_PATH = os.path.join(DATA_PATH, "masks").replace("\\", "/")
SHADERS_PATH = os.path.join(DATA_PATH, "shaders").replace("\\", "/")
MH4_PATH = os.path.join(DATA_PATH, "mh4").replace("\\", "/")
MH4_DNA_PATH = os.path.join(MH4_PATH, "dna").replace("\\", "/")
OUT_PATH = os.path.join(DATA_PATH, "out").replace("\\", "/")
SAVE_PATH = os.path.join(DATA_PATH, "save").replace("\\", "/")
MAYA_VERSION = cmds.about(version=True)
PLUGIN_PATH = os.path.join(TOOL_PATH, "plugins", f"{MAYA_VERSION}").replace("\\", "/")
if not os.path.exists(PLUGIN_PATH):
cmds.warning(f"Plugin path not found: {PLUGIN_PATH}")
# 打印上面的所有变量
print(f"TOOL_PATH: {TOOL_PATH}")
print(f"SCRIPTS_PATH: {SCRIPTS_PATH}")
print(f"ICONS_PATH: {ICONS_PATH}")
print(f"TOOL_ICON: {TOOL_ICON}")
print(f"DATA_PATH: {DATA_PATH}")
print(f"DNA_PATH: {DNA_PATH}")
print(f"BODY_PATH: {BODY_PATH}")
print(f"IMG_PATH: {IMG_PATH}")
print(f"MAP_PATH: {MAP_PATH}")
print(f"MASKS_PATH: {MASKS_PATH}")
print(f"SHADERS_PATH: {SHADERS_PATH}")
#===================================== LANGUAGE SETTINGS =====================================
TOOL_LANG = 'en_US'
SUPPORTED_LANGUAGES = ['en_US', 'zh_CN']
LANG = {
"en_US": {
"MetaFusion": "MetaFusion",
"Load DNA": "Load DNA",
"Prepare Body": "Prepare Body",
"Help": "Help",
"Switch Language": "Switch Language",
"EN": "EN",
"ZH": "ZH",
"English": "English",
"Chinese": "Chinese",
"Language switched": "Language switched",
"DNA Samples": "DNA Samples",
"Prepare": "Prepare",
"Import": "Import",
"Body Prepare": "Body Prepare",
"Batch Import": "Batch Import",
"DNA Edit": "DNA Edit",
"Open DNA Viewer": "Open DNA Viewer",
"Load DNA": "Load DNA",
"DNA File:": "DNA File:"
},
"zh_CN": {
"MetaFusion": "MetaFusion",
"DNA Samples": "DNA 样本",
"Load DNA": "加载 DNA",
"Prepare Body": "准备身体",
"Help": "帮助",
"Switch Language": "切换语言",
"EN": "英文",
"ZH": "中文",
"English": "英语",
"Chinese": "中文",
"Language switched": "语言已切换",
"DNA Samples": "DNA 样本",
"Prepare": "准备",
"Import": "导入",
"Body Prepare": "身体准备",
"Batch Import": "批量导入",
"DNA Edit": "DNA 编辑",
"Open DNA Viewer": "打开 DNA 查看器",
"Load DNA": "加载 DNA",
"DNA File:": "DNA 文件:"
}
}
#===================================== UTILITY FUNCTIONS =====================================
def get_system_encoding():
encoding = sys.getdefaultencoding()
if encoding.lower() == 'ascii':
encoding = locale.getpreferredencoding()
return encoding
def maya_main_window():
main_window_ptr = omui.MQtUtil.mainWindow()
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
#===================================== UI COMPONENTS =====================================
class MainButton(QtWidgets.QPushButton):
DEFAULT_COLORS = {
"normal": "#D0D0D0",
"hover": "#E0E0E0",
"pressed": "#C0C0C0"
}
def __init__(self, text="", icon=None, color=None, hover_color=None, pressed_color=None):
super().__init__(text)
if icon:
self.setIcon(icon)
self.setIconSize(QtCore.QSize(24, 24))
self.setMinimumHeight(30)
colors = {
"normal": color or self.DEFAULT_COLORS["normal"],
"hover": hover_color or self.DEFAULT_COLORS["hover"],
"pressed": pressed_color or self.DEFAULT_COLORS["pressed"]
}
self.setStyleSheet(self._generate_style_sheet(**colors))
@staticmethod
def _generate_style_sheet(normal, hover, pressed):
return f"""
QPushButton {{
background-color: {normal};
color: #303030;
border-radius: 10px;
padding: 5px;
font-weight: bold;
text-align: center;
}}
QPushButton:hover {{
background-color: {hover};
}}
QPushButton:pressed {{
background-color: {pressed};
}}
"""
class BottomButton(QtWidgets.QPushButton):
def __init__(self, text="", icon=None):
super().__init__(text)
self.setMinimumHeight(20)
self.setStyleSheet(self._generate_style_sheet())
self.setFont(QtGui.QFont("Microsoft YaHei", 10))
@staticmethod
def _generate_style_sheet():
return """
QPushButton {
background-color: transparent;
border: none;
color: gray;
font-weight: bold;
}
QPushButton:hover {
color: black;
}
"""
#===================================== MAIN WINDOW =====================================
class MainWindow(QtWidgets.QWidget):
instance = None
def __init__(self, parent=maya_main_window()):
super(MainWindow, self).__init__(parent)
self.setWindowTitle(f"{TOOL_NAME} - {TOOL_VERSION}")
self.setObjectName(TOOL_PATH)
self.setWindowFlags(QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
self.setMinimumSize(300, 800)
self.create_widgets()
self.create_layouts()
self.create_connections()
if os.path.exists(TOOL_ICON):
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
else:
print(f"WARNING: Icon file not found: {TOOL_ICON}")
@classmethod
def show_window(cls):
try:
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
cmds.deleteUI(TOOL_WSCL_NAME, control=True)
if cls.instance is not None:
try:
cls.instance.close()
cls.instance.deleteLater()
except Exception:
pass
cls.instance = cls()
cls.instance.dock_to_maya()
return cls.instance
except Exception as e:
print(f"Error showing {TOOL_NAME} window: {e}")
return None
def dock_to_maya(self):
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
cmds.deleteUI(TOOL_WSCL_NAME)
try:
workspace_control = cmds.workspaceControl(
TOOL_WSCL_NAME,
label=TOOL_NAME,
floating=True,
retain=True,
resizeWidth=True,
2025-01-11 18:26:42 +08:00
initialWidth=450,
minimumWidth=450
2025-01-11 16:53:48 +08:00
)
cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True)
cmds.control(self.objectName(), e=True, p=workspace_control)
cmds.evalDeferred(lambda: cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True))
except Exception as e:
print(f"Error creating workspace control: {e}")
#===================================== UI COMPONENTS =====================================
def create_widgets(self):
# DNA Samples group
self.dna_browser = DNA_Browser.create_browser(DNA_PATH, IMG_PATH)
self.dna_browser.dna_selected.connect(self.on_dna_selected)
# DNA File input
self.dna_file_layout = QtWidgets.QHBoxLayout()
self.dna_file_label = QtWidgets.QLabel("DNA File:")
self.dna_file_input = QtWidgets.QLineEdit()
self.dna_file_input.setStyleSheet("""
QLineEdit {
background-color: #303030;
color: #CCCCCC;
border: 1px solid #202020;
border-radius: 3px;
padding: 2px 5px;
font-size: 10px;
}
QLineEdit:hover {
border: 1px solid #404040;
}
QLineEdit:focus {
border: 1px solid #505050;
background-color: #353535;
}
""")
self.dna_file_input.textChanged.connect(self.on_dna_file_changed)
self.dna_file_layout.addWidget(self.dna_file_label)
self.dna_file_layout.addWidget(self.dna_file_input)
2025-01-11 18:26:42 +08:00
self.load_dna_btn = MainButton(LANG[TOOL_LANG]["Load DNA"], color="#E6B3B3", hover_color="#F2BFBF", pressed_color="#D99E9E")
2025-01-11 16:53:48 +08:00
# Create function buttons
# Prepare group
self.prepare_btn = MainButton(LANG[TOOL_LANG]["Prepare"])
self.body_prepare_btn = MainButton(LANG[TOOL_LANG]["Body Prepare"], color="#FFEBA1", hover_color="#FFF5B3", pressed_color="#FFE68A")
# Import group
self.import_btn = MainButton(LANG[TOOL_LANG]["Import"])
self.batch_import_btn = MainButton(LANG[TOOL_LANG]["Batch Import"], color="#A7C6ED", hover_color="#B2D3F0", pressed_color="#8BB8E0")
# DNA Edit group
self.dna_edit_btn = MainButton(LANG[TOOL_LANG]["DNA Edit"])
self.dna_viewer_btn = MainButton(LANG[TOOL_LANG]["Open DNA Viewer"], color="#B8E6B3", hover_color="#C4F2BF", pressed_color="#A3D99E")
# Bottom buttons (existing code)
self.help_btn = BottomButton(LANG[TOOL_LANG]["Help"])
self.help_btn.setToolTip(LANG[TOOL_LANG]["Help"])
self.help_btn.setFixedSize(100, 20)
self.lang_btn = BottomButton(LANG[TOOL_LANG]["ZH" if TOOL_LANG == 'en_US' else "EN"])
self.lang_btn.setToolTip(LANG[TOOL_LANG]["Switch Language"])
self.lang_btn.setFixedSize(30, 20)
for button in [self.help_btn, self.lang_btn]:
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
def create_layouts(self):
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.setContentsMargins(2, 2, 2, 2)
# Content layout
content_layout = QtWidgets.QVBoxLayout()
content_layout.setContentsMargins(5, 5, 5, 5)
# DNA Samples group
dna_samples_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Samples"])
dna_samples_layout = QtWidgets.QVBoxLayout(dna_samples_group)
dna_samples_layout.addWidget(self.dna_browser)
dna_samples_layout.addLayout(self.dna_file_layout)
dna_samples_layout.addWidget(self.load_dna_btn)
content_layout.addWidget(dna_samples_group)
# Prepare group
prepare_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["Prepare"])
prepare_layout = QtWidgets.QVBoxLayout(prepare_group)
prepare_layout.addWidget(self.body_prepare_btn)
content_layout.addWidget(prepare_group)
# Import group
import_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["Import"])
import_layout = QtWidgets.QVBoxLayout(import_group)
import_layout.addWidget(self.batch_import_btn)
content_layout.addWidget(import_group)
# DNA Edit group
dna_edit_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Edit"])
dna_edit_layout = QtWidgets.QVBoxLayout(dna_edit_group)
dna_edit_layout.addWidget(self.dna_viewer_btn)
content_layout.addWidget(dna_edit_group)
main_layout.addLayout(content_layout)
main_layout.addStretch()
# Bottom layout (existing code)
bottom_layout = QtWidgets.QHBoxLayout()
bottom_layout.setContentsMargins(5, 0, 5, 5)
icon_label = QtWidgets.QLabel()
if os.path.exists(TOOL_ICON):
icon = QtGui.QPixmap(TOOL_ICON).scaled(24, 24, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
icon_label.setPixmap(icon)
version_label = QtWidgets.QLabel(f"{TOOL_VERSION}")
version_label.setStyleSheet("color: gray; font-size: 12px;")
bottom_layout.addWidget(icon_label)
bottom_layout.addWidget(version_label)
bottom_layout.addStretch()
bottom_layout.addWidget(self.help_btn)
bottom_layout.addWidget(self.lang_btn)
main_layout.addLayout(bottom_layout)
def create_connections(self):
# Connect function buttons
self.body_prepare_btn.clicked.connect(self.run_body_prepare)
self.batch_import_btn.clicked.connect(self.run_batch_import)
self.dna_viewer_btn.clicked.connect(self.run_dna_viewer)
# Connect DNA Samples buttons
self.load_dna_btn.clicked.connect(self.run_load_dna)
# Existing connections
self.help_btn.clicked.connect(self.help)
self.lang_btn.clicked.connect(self.switch_language)
#===================================== FUNCTIONS =====================================
#===================================== MAIN FUNCTIONS =====================================
# Prepare group
def run_body_prepare(self):
import BodyPrep
BodyPrep.run()
# Import group
def run_batch_import(self):
import BatchImport
BatchImport.run()
# DNA Edit group
def run_dna_viewer(self):
2025-01-11 18:26:42 +08:00
import dna_viewer
dna_viewer.show()
2025-01-11 16:53:48 +08:00
# DNA Samples group
def run_load_dna(self):
"""加载选中的DNA文件"""
if hasattr(self, 'dna_list') and self.dna_list.currentItem():
2025-01-11 18:26:42 +08:00
import dna_viewer
dna_viewer.load_dna(DNA_File)
2025-01-11 16:53:48 +08:00
else:
cmds.warning("Please select a DNA file first")
2025-01-11 18:26:42 +08:00
def on_dna_selected(self, dna_path):
"""当DNA被选中时"""
global DNA_File
DNA_File = dna_path
self.dna_file_input.setText(DNA_File)
print(f"Selected DNA file: {DNA_File}")
def on_dna_file_changed(self):
"""当DNA文件输入框内容改变时"""
global DNA_File
DNA_File = self.dna_file_input.text()
print(f"DNA file path updated: {DNA_File}")
2025-01-11 16:53:48 +08:00
#===================================== BOTTOM LAYOUT =====================================
def help(self):
webbrowser.open(TOOL_HELP_URL)
def switch_language(self):
global TOOL_LANG
TOOL_LANG = 'en_US' if TOOL_LANG == 'zh_CN' else 'zh_CN'
self.lang_btn.setText("ZH" if TOOL_LANG == 'en_US' else "EN")
self.retranslate_ui()
QtWidgets.QToolTip.showText(
self.lang_btn.mapToGlobal(QtCore.QPoint(0, -30)),
"Language switched" if TOOL_LANG == 'en_US' else "语言已切换",
self.lang_btn
)
def retranslate_ui(self):
# Update function button translations
self.load_dna_btn.setText(LANG[TOOL_LANG]["Load DNA"])
self.body_prepare_btn.setText(LANG[TOOL_LANG]["Body Prepare"])
self.batch_import_btn.setText(LANG[TOOL_LANG]["Batch Import"])
self.dna_viewer_btn.setText(LANG[TOOL_LANG]["Open DNA Viewer"])
for button in [
self.body_prepare_btn,
self.batch_import_btn,
self.dna_viewer_btn
]:
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
# Update bottom button translations
self.help_btn.setText(LANG[TOOL_LANG]["Help"])
self.lang_btn.setText("ZH" if TOOL_LANG == 'en_US' else "EN")
for button in [
self.help_btn,
self.lang_btn
]:
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
self.dna_file_label.setText(LANG[TOOL_LANG]["DNA File:"])
#===================================== LAUNCH FUNCTIONS =====================================
def show():
return MainWindow.show_window()