Base
This commit is contained in:
24
.gitattributes
vendored
Normal file
24
.gitattributes
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Unified configuration of binary files
|
||||
*.png filter=lfs diff=lfs merge=lfs binary
|
||||
*.mb filter=lfs diff=lfs merge=lfs binary
|
||||
*.ma filter=lfs diff=lfs merge=lfs binary
|
||||
*.fbx filter=lfs diff=lfs merge=lfs binary
|
||||
*.dna filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Specify directory configuration (if special processing is required)
|
||||
assets/dna/** filter=lfs diff=lfs merge=lfs
|
||||
assets/img/** filter=lfs diff=lfs merge=lfs
|
||||
|
||||
# Text file configuration
|
||||
*.py text eol=lf
|
||||
*.json text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.txt text eol=lf
|
||||
|
||||
# Ensure script files use correct line endings
|
||||
*.sh text eol=lf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Other common configurations
|
||||
.gitattributes text eol=lf
|
||||
.gitignore text eol=lf
|
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.dylib
|
||||
|
||||
# Fortran module files
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Local build
|
||||
/build*
|
||||
|
||||
# Work Environment Artifacts
|
||||
/.vs
|
||||
/.vscode
|
||||
|
||||
*.pyc
|
||||
|
||||
/.idea
|
||||
/output
|
||||
|
||||
/Reference
|
||||
/assets/dna
|
||||
/assets/img
|
100
.windsurfrules
Normal file
100
.windsurfrules
Normal file
@@ -0,0 +1,100 @@
|
||||
## Global rules:
|
||||
- Comprehensive search: All questions must first retrieve all relevant documents in the project folder to obtain context.
|
||||
- Web search: After retrieving local files, a web search must be conducted to obtain more information and the latest data.
|
||||
- Use context7 as much as possible to reduce tokens
|
||||
|
||||
## MCP usage:
|
||||
Use the following MCP model context protocol to enhance responses:
|
||||
1. markitdown mcp:
|
||||
- Purpose: Convert PDF files to Markdown format.
|
||||
- Trigger conditions: Used when the user query contains information in the PDF file, or needs to convert the PDF content to a more readable format.
|
||||
2. sequential-thinking mcp;
|
||||
- Purpose: Break down complex problems into smaller, more manageable parts, and reason step by step to ensure the logic and coherence of the answer.
|
||||
- Trigger: Used when the user asks a question that requires multiple steps or relies on logical reasoning. This helps provide a clearer and more organized response.
|
||||
|
||||
## Dialogue rules:
|
||||
- Always respond in 中文 in Cascade chat an composer dialogue bar
|
||||
|
||||
## Code rules:
|
||||
- Code are always in English
|
||||
- Comments are always in English
|
||||
- Check code errors and correct them after each code update
|
||||
- Check plugin integrity and optimize update development plans and next suggestions for each update
|
||||
- The Reference path does not participate in the implementation of the reference function, but is only used as a reference. The Reference path is only used as a reference, and the necessary files can be copied from it to the current project
|
||||
- The code must be compatible with Maya2022, Maya2023, Maya2024, Maya2025
|
||||
- The code must be compatible with Python3.10, Python3.11, Python3.12
|
||||
- All codes must be encoded in UTF-8
|
||||
- All modules must be preceded by:
|
||||
```.
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
```
|
||||
|
||||
## Code write steps rules:
|
||||
- Read https://github.com/EpicGames/MetaHuman-DNA-Calibration, to understand the code examples and basic rules of Metahuman DNA calibration, use context7;
|
||||
- Read https://epicgames.github.io/MetaHuman-DNA-Calibration/index.html, to understand the code examples and basic rules of Metahuman DNA calibration, use context7;
|
||||
- Read Readme.md to understand the plugin goals
|
||||
- Read the code and examples in the Reference path
|
||||
- The UI style must be implemented in a unified style file
|
||||
- All UIs must be implemented using Qt(scripts\Qt.py)
|
||||
- After each function is implemented, the document must be updated
|
||||
|
||||
## Project rules:
|
||||
### UI Styles:
|
||||
- Flat design style,
|
||||
- It should have a sense of luxury and sci-fi futurism
|
||||
- Rounded corner design
|
||||
- Chinese should be clear and elegant
|
||||
- English should be concise and clear
|
||||
- Colors should be uniform
|
||||
- Fonts should be uniform
|
||||
- Font size should be uniform
|
||||
- Buttons should be uniform
|
||||
- Input boxes should be uniform
|
||||
- Labels should be uniform
|
||||
|
||||
### Project Goal:
|
||||
|
||||
Make a Metahuman custom plugin for Maya, language: Python-based, Maya version: 2022, 2023, 2024, 2025
|
||||
|
||||
### Project Description:
|
||||
- This project is a Maya plugin, the main function is to provide a model with the same topology as MetaHuman or a custom 3D model to complete custom binding, edit DNA, calibrate bone position, save DNA, load DNA, export fbx, save DNA file, edit BlendShape, and other functions.
|
||||
|
||||
### Project Functions:
|
||||
- Provide a model with the same topology as MetaHuman or a custom 3D model to complete custom binding, edit DNA, calibrate bone position, save DNA, load DNA, export fbx, save DNA file, edit BlendShape, and other functions.
|
||||
|
||||
### Important Variables:
|
||||
- Config File: `config.py`
|
||||
- Main File: `scripts\Main.py`
|
||||
- UI Modules: `scripts\ui\`
|
||||
- Ot Module: `scripts\ui\Qt.py`
|
||||
- Style File: `scripts\ui\style.qss`
|
||||
- Utilities Path: `scripts\utils\`
|
||||
- Reload Module: `scripts\ReloadModules.py`
|
||||
|
||||
### Doc reference (Do not modify)
|
||||
- [DNA_Calibration在线文档](https://epicgames.github.io/MetaHuman-DNA-Calibration), use context7;
|
||||
- [MetaHuman DNA Calibration White Paper](https://cdn2.unrealengine.com/rig-logic-whitepaper-v2-zhcn-5860d80f8357.pdf), use context7;
|
||||
- [MetaHuman DNA Calibration Code](https://github.com/EpicGames/MetaHuman-DNA-Calibration), use context7;
|
||||
- [MetaHuman DNA Calibration Documents](https://github.com/EpicGames/MetaHuman-DNA-Calibration/tree/main/docs), use context7;
|
||||
- [MetaHuman DNA Calibration Tools](https://dev.epicgames.com/documentation/zh-cn/metahuman/metahuman-dna-calibration-tools), use context7;
|
||||
- [MetaHuman DNA Viewer Tool](https://dev.epicgames.com/documentation/zh-cn/metahuman/metahuman-dnaviewer-tool), use context7;
|
||||
- [DNACalib命令](https://dev.epicgames.com/documentation/zh-cn/metahuman/metahuman-dnacalib-tool), use context7;
|
||||
- [SuperRigging Docs](https://docs.pointart.net/), use context7;
|
||||
- [Metapipe Originsdocs](https://www.artsandspells.com/originsdocs), use context7;
|
||||
- [Metapipe Nitrous](https://www.artsandspells.com/nitrous), use context7;
|
||||
- [Metapipe Genetics](https://www.artsandspells.com/genetics), use context7;
|
||||
- [Metapipe Synapses](https://www.artsandspells.com/synapses), use context7;
|
||||
- [FACS面部表情编码系统](https://www.artsandspells.com/synapses), use context7;
|
||||
- [MetaHuman 实时环境面部装配逻辑](https://zhuanlan.zhihu.com/p/511001493), use context7;
|
||||
- [MetaHuman Principle](https://zhuanlan.zhihu.com/p/673471863), use context7;
|
||||
- [Metahuman 蒙皮、骨骼、驱动](https://blog.csdn.net/qq_28976599/article/details/130849821), use context7;
|
||||
- [MetaHuman DNA Calibration Deep Dive](https://dev.epicgames.com/community/learning/tutorials/EoPj/metahuman-dna-calibration-deep-dive), use context7;
|
||||
|
||||
### Code reference (do not modify)
|
||||
- `Reference\DNA_Calibration`
|
||||
- `Reference\MSLiveLink`
|
||||
- `Reference\SuperRiggingEditor`
|
||||
- `Reference\Utils`
|
||||
### UI reference images:
|
||||
- `Reference\UI`
|
5
CleanPycache.bat
Normal file
5
CleanPycache.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
REM Delete all __pycache__ folders
|
||||
for /d /r %%d in (__pycache__) do @if exist "%%d" rd /s /q "%%d"
|
||||
|
||||
echo Cleaned up!
|
18
Install.mel
Normal file
18
Install.mel
Normal file
@@ -0,0 +1,18 @@
|
||||
global proc install()
|
||||
{
|
||||
string $scriptPath = `whatIs install`;
|
||||
string $dirPath = `substring $scriptPath 25 (size($scriptPath))`;
|
||||
$dirPath = `dirname $dirPath`;
|
||||
string $pythonPath = $dirPath + "/Install.py";
|
||||
$pythonPath = substituteAllString($pythonPath, "\\", "/");
|
||||
|
||||
string $pythonCmd = "import os, sys\n";
|
||||
$pythonCmd += "INSTALL_PATH = r'" + $pythonPath + "'\n";
|
||||
$pythonCmd += "sys.path.append(os.path.dirname(INSTALL_PATH))\n";
|
||||
$pythonCmd += "import Install\n";
|
||||
$pythonCmd += "Install.main()\n";
|
||||
|
||||
python($pythonCmd);
|
||||
}
|
||||
|
||||
install();
|
765
Install.py
Normal file
765
Install.py
Normal file
@@ -0,0 +1,765 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#===================================== 1. Module Imports =====================================
|
||||
import maya.OpenMayaUI as omui
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import webbrowser
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Import configuration module
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
import config
|
||||
|
||||
try:
|
||||
from PySide2 import QtCore, QtGui, QtWidgets
|
||||
from shiboken2 import wrapInstance
|
||||
print("Loading Qt and shiboken2 from PySide2")
|
||||
except ImportError:
|
||||
try:
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
from shiboken6 import wrapInstance
|
||||
print("Loading Qt and shiboken6 from PySide6")
|
||||
except ImportError:
|
||||
try:
|
||||
from PySide import QtCore, QtGui, QtWidgets
|
||||
from shiboken import wrapInstance
|
||||
print("Loading Qt and shiboken from PySide")
|
||||
except ImportError as error:
|
||||
print(f"Qt loading failed: {str(error)}")
|
||||
QtCore = QtGui = QtWidgets = None
|
||||
wrapInstance = None
|
||||
|
||||
# Use config directly instead of duplicating variables
|
||||
# This ensures we always use the latest values from config
|
||||
# and reduces maintenance overhead
|
||||
TOOL_NAME = config.TOOL_NAME
|
||||
TOOL_VERSION = config.TOOL_VERSION
|
||||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
UI_PATH = config.UI_PATH
|
||||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||||
ASSETS_PATH = config.ASSETS_PATH
|
||||
ICONS_PATH = config.ICONS_PATH
|
||||
PLUGINS_PATH = config.PLUGINS_PATH
|
||||
PYDNA_PATH = config.PYDNA_PATH
|
||||
SYSTEM_OS = config.SYSTEM_OS
|
||||
MAYA_VERSION = config.MAYA_VERSION
|
||||
PYVERSION_DIR = config.PYVERSION_DIR
|
||||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||||
DNA_IMG_PATH = config.DNA_IMG_PATH
|
||||
STYLE_FILE = config.STYLE_FILE
|
||||
TOOL_ICON = config.TOOL_ICON
|
||||
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
|
||||
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
|
||||
TOOL_YEAR = config.TOOL_YEAR
|
||||
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
|
||||
TOOL_LANG = config.TOOL_LANG
|
||||
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
|
||||
TOOL_HELP_URL = config.TOOL_HELP_URL
|
||||
|
||||
print("=" * 50)
|
||||
print(f"TOOL_NAME: {TOOL_NAME}")
|
||||
print(f"TOOL_VERSION: {TOOL_VERSION}")
|
||||
print(f"TOOL_YEAR: {TOOL_YEAR}")
|
||||
print(f"SYSTEM_OS: {SYSTEM_OS}")
|
||||
print(f"MAYA_VERSION: {MAYA_VERSION}")
|
||||
print(f"TOOL_AUTHOR: {TOOL_AUTHOR}")
|
||||
print(f"TOOL_LANG: {TOOL_LANG}")
|
||||
print(f"PYVERSION_DIR: {PYVERSION_DIR}")
|
||||
print(f"TOOL_WSCL_NAME: {TOOL_WSCL_NAME}")
|
||||
print(f"TOOL_MOD_FILENAME: {TOOL_MOD_FILENAME}")
|
||||
print(f"TOOL_HELP_URL: {TOOL_HELP_URL}")
|
||||
print(f"TOOL_PATH: {TOOL_PATH}")
|
||||
print(f"PLUGINS_PATH: {PLUGINS_PATH}")
|
||||
print(f"PYDNA_PATH: {PYDNA_PATH}")
|
||||
print(f"SCRIPTS_PATH: {SCRIPTS_PATH}")
|
||||
print(f"TOOL_MAIN_SCRIPT: {TOOL_MAIN_SCRIPT}")
|
||||
print(f"UI_PATH: {UI_PATH}")
|
||||
print(f"STYLE_FILE: {STYLE_FILE}")
|
||||
print(f"ICONS_PATH: {ICONS_PATH}")
|
||||
print(f"TOOL_ICON: {TOOL_ICON}")
|
||||
print(f"TOOL_COMMAND_ICON: {TOOL_COMMAND_ICON}")
|
||||
print(f"ASSETS_PATH: {ASSETS_PATH}")
|
||||
print(f"DNA_FILE_PATH: {DNA_FILE_PATH}")
|
||||
print(f"DNA_IMG_PATH: {DNA_IMG_PATH}")
|
||||
print("=" * 50)
|
||||
|
||||
#===================================== 3. Utility Functions =====================================
|
||||
def maya_main_window():
|
||||
"""Get Maya main window"""
|
||||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||||
if main_window_ptr:
|
||||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||||
return None
|
||||
|
||||
def ensure_directory(directory_path):
|
||||
"""Ensure directory exists"""
|
||||
if directory_path and isinstance(directory_path, str):
|
||||
if not os.path.exists(directory_path):
|
||||
os.makedirs(directory_path)
|
||||
print(f"Created directory: {directory_path}")
|
||||
return directory_path
|
||||
|
||||
def get_maya_modules_dir():
|
||||
"""Get Maya modules directory"""
|
||||
maya_app_dir = cmds.internalVar(userAppDir=True)
|
||||
if maya_app_dir and isinstance(maya_app_dir, str):
|
||||
return ensure_directory(os.path.join(maya_app_dir, "modules"))
|
||||
return None
|
||||
|
||||
#===================================== 4. UI Component Classes =====================================
|
||||
class SetButton(QtWidgets.QPushButton):
|
||||
"""Custom styled button for installation interface"""
|
||||
def __init__(self, text):
|
||||
super(SetButton, self).__init__(text)
|
||||
|
||||
#===================================== 5. Main Window Class =====================================
|
||||
class InstallDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=maya_main_window()):
|
||||
super(InstallDialog, self).__init__(parent)
|
||||
self.load_stylesheet()
|
||||
self.setup_ui()
|
||||
|
||||
def load_stylesheet(self):
|
||||
try:
|
||||
with open(STYLE_FILE, 'r', encoding='utf-8') as f:
|
||||
style = f.read()
|
||||
self.setStyleSheet(style)
|
||||
except Exception as error:
|
||||
print(f"Error loading stylesheet: {error}")
|
||||
# Use a default style if the file can't be loaded
|
||||
self.setStyleSheet("QDialog { background-color: #333333; color: #CCCCCC; }")
|
||||
|
||||
def setup_ui(self):
|
||||
"""Initialize and setup UI components"""
|
||||
self.setWindowTitle(f"{TOOL_NAME} Installation")
|
||||
self.setFixedSize(220, 120)
|
||||
self.setup_window_icon()
|
||||
self.create_widgets()
|
||||
self.create_layouts()
|
||||
self.create_connections()
|
||||
|
||||
def setup_window_icon(self):
|
||||
"""Setup window icon if available"""
|
||||
if os.path.exists(TOOL_ICON):
|
||||
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
else:
|
||||
print(f"Warning: Icon file not found: {TOOL_ICON}")
|
||||
|
||||
#----------------- 5.1 UI Methods -----------------
|
||||
def create_widgets(self):
|
||||
self.new_shelf_toggle = QtWidgets.QCheckBox(f"{TOOL_NAME} Installation")
|
||||
self.install_button = SetButton("Install " + TOOL_NAME)
|
||||
self.uninstall_button = SetButton("Uninstall " + TOOL_NAME)
|
||||
|
||||
def create_layouts(self):
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(10, 2, 10, 5)
|
||||
main_layout.setSpacing(5)
|
||||
|
||||
header_layout = QtWidgets.QHBoxLayout()
|
||||
header_layout.setSpacing(5)
|
||||
|
||||
welcome_label = QtWidgets.QLabel("Welcome to " + TOOL_NAME + "!")
|
||||
welcome_label.setStyleSheet("font-size: 11px; padding: 0px; margin: 0px;")
|
||||
header_layout.addWidget(welcome_label)
|
||||
header_layout.addStretch()
|
||||
|
||||
main_layout.addLayout(header_layout)
|
||||
main_layout.addWidget(self.install_button)
|
||||
main_layout.addWidget(self.uninstall_button)
|
||||
|
||||
self.install_button.setFixedHeight(30)
|
||||
self.uninstall_button.setFixedHeight(30)
|
||||
|
||||
def create_connections(self):
|
||||
self.install_button.clicked.connect(self.install)
|
||||
self.uninstall_button.clicked.connect(self.uninstall)
|
||||
|
||||
def create_styled_message_box(self, title, text):
|
||||
msg_box = QtWidgets.QMessageBox(self)
|
||||
msg_box.setWindowTitle(title)
|
||||
msg_box.setText(text)
|
||||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
|
||||
# 自定义样式,去掉提示文字的边框
|
||||
custom_style = self.styleSheet() + """
|
||||
QMessageBox { background-color: #333333; color: #CCCCCC; }
|
||||
QLabel { border: none; background-color: transparent; color: #CCCCCC; }
|
||||
QPushButton { min-width: 80px; min-height: 24px; }
|
||||
"""
|
||||
msg_box.setStyleSheet(custom_style)
|
||||
|
||||
return msg_box
|
||||
|
||||
#----------------- 5.2 Event Handler Methods -----------------
|
||||
def event(self, event):
|
||||
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
|
||||
QtWidgets.QWhatsThis.leaveWhatsThisMode()
|
||||
self.open_help_url()
|
||||
return True
|
||||
return QtWidgets.QDialog.event(self, event)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Handle window close event"""
|
||||
try:
|
||||
super(InstallDialog, self).closeEvent(event)
|
||||
except Exception as error:
|
||||
print(f"Error closing window: {error}")
|
||||
event.accept()
|
||||
|
||||
def helpEvent(self, event):
|
||||
self.open_help_url()
|
||||
event.accept()
|
||||
|
||||
#----------------- 5.3 Utility Methods -----------------
|
||||
def open_help_url(self):
|
||||
webbrowser.open(TOOL_HELP_URL)
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def get_script_path():
|
||||
maya_script = mel.eval('getenv("MAYA_SCRIPT_NAME")')
|
||||
if maya_script and os.path.exists(maya_script):
|
||||
return os.path.dirname(maya_script)
|
||||
|
||||
for sys_path in sys.path:
|
||||
install_path = os.path.join(sys_path, "install.py")
|
||||
if os.path.exists(install_path):
|
||||
return os.path.dirname(install_path)
|
||||
|
||||
return os.getcwd()
|
||||
|
||||
def get_maya_modules_dir(self):
|
||||
"""Get Maya modules directory"""
|
||||
# Try to get module directory from Maya environment variable
|
||||
try:
|
||||
maya_app_dir = mel.eval('getenv("MAYA_APP_DIR")')
|
||||
if maya_app_dir:
|
||||
modules_dir = os.path.join(maya_app_dir, "modules")
|
||||
if not os.path.exists(modules_dir):
|
||||
os.makedirs(modules_dir)
|
||||
return modules_dir
|
||||
except Exception as error:
|
||||
print(f"Error getting Maya modules directory from environment: {error}")
|
||||
|
||||
# If environment variable is not available, use default path
|
||||
user_home = os.path.expanduser("~")
|
||||
maya_version = MAYA_VERSION
|
||||
|
||||
# Windows path
|
||||
if SYSTEM_OS == "Windows":
|
||||
modules_dir = os.path.join(user_home, "Documents", "maya", maya_version, "modules")
|
||||
# Linux path
|
||||
else:
|
||||
modules_dir = os.path.join(user_home, "maya", maya_version, "modules")
|
||||
|
||||
# Make sure the path exists
|
||||
if not os.path.exists(modules_dir):
|
||||
try:
|
||||
os.makedirs(modules_dir)
|
||||
except Exception as error:
|
||||
print(f"Error creating modules directory: {error}")
|
||||
|
||||
return modules_dir
|
||||
|
||||
#----------------- 5.4 Installation Methods -----------------
|
||||
def install(self):
|
||||
"""Handle install request with error handling"""
|
||||
if not self._validate_paths():
|
||||
return
|
||||
|
||||
msg_box = self.create_styled_message_box(
|
||||
"Confirm Installation",
|
||||
f"Are you sure you want to install {TOOL_NAME}?"
|
||||
)
|
||||
if msg_box.exec_() == QtWidgets.QMessageBox.Yes:
|
||||
try:
|
||||
self.install_tool()
|
||||
self.close()
|
||||
except Exception as error:
|
||||
error_msg = f"Error during installation: {error}"
|
||||
print(error_msg)
|
||||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||||
|
||||
def uninstall(self, *args):
|
||||
"""Handle uninstall request"""
|
||||
msg_box = self.create_styled_message_box(
|
||||
"Confirm Uninstallation",
|
||||
f"Are you sure you want to uninstall {TOOL_NAME}?"
|
||||
)
|
||||
|
||||
if msg_box.exec_() == QtWidgets.QMessageBox.Yes:
|
||||
try:
|
||||
self.uninstall_tool()
|
||||
self.close()
|
||||
except Exception as error:
|
||||
error_msg = f"Error during uninstallation: {error}"
|
||||
print(error_msg)
|
||||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||||
else:
|
||||
print("Uninstallation cancelled")
|
||||
|
||||
def clean_pycache(self):
|
||||
"""Clean __pycache__ directories and .pyc files"""
|
||||
count = 0
|
||||
try:
|
||||
for root, dirs, files in os.walk(TOOL_PATH):
|
||||
# Remove __pycache__ directories
|
||||
if "__pycache__" in dirs:
|
||||
pycache_path = os.path.join(root, "__pycache__")
|
||||
try:
|
||||
import shutil
|
||||
shutil.rmtree(pycache_path)
|
||||
print(f"Removed __pycache__ directory: {pycache_path}")
|
||||
count += 1
|
||||
except Exception as error:
|
||||
print(f"Failed to remove {pycache_path}: {error}")
|
||||
|
||||
# Remove .pyc files
|
||||
for file in files:
|
||||
if file.endswith(".pyc"):
|
||||
try:
|
||||
os.remove(os.path.join(root, file))
|
||||
print(f"Removed .pyc file: {os.path.join(root, file)}")
|
||||
count += 1
|
||||
except Exception as error:
|
||||
print(f"Failed to remove {os.path.join(root, file)}: {error}")
|
||||
except Exception as error:
|
||||
print(f"Error cleaning __pycache__ directories: {error}")
|
||||
|
||||
return count
|
||||
|
||||
def create_mod_file(self):
|
||||
"""Create or update Maya's .mod file"""
|
||||
modules_dir = self.get_maya_modules_dir()
|
||||
|
||||
# Get all Maya versions and their corresponding Python versions
|
||||
version_map = {
|
||||
"2022": "python3",
|
||||
"2023": "python397",
|
||||
"2024": "python3108",
|
||||
"2025": "python311"
|
||||
}
|
||||
|
||||
# System mapping
|
||||
os_paths = {
|
||||
"win64": "Windows",
|
||||
"linux": "Linux"
|
||||
}
|
||||
|
||||
# Create mod file content
|
||||
mod_content = f"""+ {TOOL_NAME} {TOOL_VERSION} {TOOL_PATH}
|
||||
"""
|
||||
|
||||
# Add each Maya version's configuration
|
||||
for maya_version, python_version in version_map.items():
|
||||
# Add each operating system's configuration
|
||||
for os_name, os_path in os_paths.items():
|
||||
mod_content += f"""
|
||||
if MAYA_VERSION == {maya_version} && PLATFORM == {os_name}
|
||||
scripts: {SCRIPTS_PATH}
|
||||
plug-ins: {os.path.join(PLUGINS_PATH, os_path)}
|
||||
XBMLANGPATH+:={ICONS_PATH}
|
||||
PATH+:={os.path.join(PLUGINS_PATH, os_path)}
|
||||
PATH+:={os.path.join(PLUGINS_PATH, os_path, "pydna", python_version)}
|
||||
PYTHONPATH+:={SCRIPTS_PATH}
|
||||
PYTHONPATH+:={os.path.join(PLUGINS_PATH, os_path, "pydna", python_version)}
|
||||
endif
|
||||
"""
|
||||
|
||||
# Write mod file
|
||||
mod_file_path = os.path.join(modules_dir, TOOL_MOD_FILENAME)
|
||||
try:
|
||||
with open(mod_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(mod_content)
|
||||
print(f"Created mod file: {mod_file_path}")
|
||||
except Exception as error:
|
||||
print(f"Error creating mod file {mod_file_path}: {error}")
|
||||
|
||||
def uninstall_mod_file(self):
|
||||
modules_dir = self.get_maya_modules_dir()
|
||||
mod_file_path = os.path.join(modules_dir, TOOL_MOD_FILENAME)
|
||||
if os.path.exists(mod_file_path):
|
||||
try:
|
||||
os.remove(mod_file_path)
|
||||
print(f"{TOOL_NAME}.mod file deleted")
|
||||
except Exception as error:
|
||||
print(f"Error deleting {TOOL_NAME}.mod file: {error}")
|
||||
|
||||
def clean_existing_buttons(self):
|
||||
if cmds.shelfLayout(TOOL_NAME, exists=True):
|
||||
buttons = cmds.shelfLayout(TOOL_NAME, query=True, childArray=True) or []
|
||||
for btn in buttons:
|
||||
if cmds.shelfButton(btn, query=True, exists=True):
|
||||
label = cmds.shelfButton(btn, query=True, label=True)
|
||||
if label == TOOL_NAME:
|
||||
cmds.deleteUI(btn)
|
||||
print(f"Deleted existing {TOOL_NAME} button: {btn}")
|
||||
|
||||
def install_tool(self):
|
||||
"""Install the tool to Maya"""
|
||||
if not os.path.exists(SCRIPTS_PATH):
|
||||
print(f"Error: Scripts path does not exist: {SCRIPTS_PATH}")
|
||||
return
|
||||
|
||||
if not os.path.exists(TOOL_MAIN_SCRIPT):
|
||||
print(f"Error: Main script file not found: {TOOL_MAIN_SCRIPT}")
|
||||
return
|
||||
|
||||
# Clean __pycache__ directories
|
||||
print("Cleaning __pycache__ directories...")
|
||||
pycache_count = self.clean_pycache()
|
||||
print(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||||
|
||||
# Add scripts path to Python path
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
|
||||
# Create shelf and button
|
||||
self._create_shelf_button()
|
||||
self.create_mod_file()
|
||||
|
||||
# 安装完成后不需要在这里切换到对应shelf,因为在_create_shelf_button函数中已经处理了
|
||||
print(f"Successfully created {TOOL_NAME} shelf and buttons")
|
||||
|
||||
self._show_install_success_message()
|
||||
|
||||
def _create_shelf_button(self):
|
||||
"""Create shelf button for the tool"""
|
||||
shelf_layout = mel.eval('$tmpVar=$gShelfTopLevel')
|
||||
|
||||
# Create shelf if not exists
|
||||
if not cmds.shelfLayout(TOOL_NAME, exists=True):
|
||||
cmds.shelfLayout(TOOL_NAME, parent=shelf_layout)
|
||||
|
||||
# Clean existing buttons
|
||||
self.clean_existing_buttons()
|
||||
|
||||
# Create main button
|
||||
icon_path = TOOL_ICON if os.path.exists(TOOL_ICON) else TOOL_COMMAND_ICON
|
||||
command = self._get_shelf_button_command()
|
||||
|
||||
cmds.shelfButton(
|
||||
parent=TOOL_NAME,
|
||||
image1=icon_path,
|
||||
label=TOOL_NAME,
|
||||
command=command,
|
||||
sourceType="python",
|
||||
annotation=f"{TOOL_NAME} {TOOL_VERSION}",
|
||||
noDefaultPopup=True,
|
||||
style="iconOnly"
|
||||
)
|
||||
|
||||
# Create reload modules button
|
||||
reload_icon_path = os.path.join(ICONS_PATH, "reload.png")
|
||||
if not os.path.exists(reload_icon_path):
|
||||
reload_icon_path = icon_path # 如果没有专用图标,使用主图标
|
||||
|
||||
reload_command = self._get_reload_modules_command()
|
||||
|
||||
cmds.shelfButton(
|
||||
parent=TOOL_NAME,
|
||||
image1=reload_icon_path,
|
||||
label="ReloadModules",
|
||||
command=reload_command,
|
||||
sourceType="python",
|
||||
annotation=f"重新加载 {TOOL_NAME} 模块",
|
||||
noDefaultPopup=True,
|
||||
style="iconOnly"
|
||||
)
|
||||
|
||||
# 自动切换到工具架
|
||||
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=TOOL_NAME)
|
||||
|
||||
def _get_reload_modules_command(self):
|
||||
"""Get the command string for the reload modules button"""
|
||||
return f"""
|
||||
import sys
|
||||
import os
|
||||
import importlib
|
||||
|
||||
# 首先导入主配置文件
|
||||
TOOL_PATH = r'{TOOL_PATH}'
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
|
||||
try:
|
||||
# 导入主配置文件
|
||||
import config
|
||||
importlib.reload(config)
|
||||
|
||||
# 使用主配置文件中的路径
|
||||
if config.SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, config.SCRIPTS_PATH)
|
||||
|
||||
# 导入并运行重载模块脚本
|
||||
import scripts.ReloadModules
|
||||
importlib.reload(scripts.ReloadModules)
|
||||
scripts.ReloadModules.main()
|
||||
except ImportError:
|
||||
# 备用方案:如果无法导入主配置,使用硬编码路径
|
||||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
|
||||
try:
|
||||
import scripts.ReloadModules
|
||||
importlib.reload(scripts.ReloadModules)
|
||||
scripts.ReloadModules.main()
|
||||
except Exception as error:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error reloading modules: {{str(error)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
except Exception as error:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error reloading modules: {{str(error)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
"""
|
||||
|
||||
def _get_shelf_button_command(self):
|
||||
"""Get the command string for the shelf button"""
|
||||
return f"""
|
||||
import sys
|
||||
import os
|
||||
import importlib
|
||||
|
||||
# 首先导入主配置文件
|
||||
TOOL_PATH = r'{TOOL_PATH}'
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
|
||||
try:
|
||||
# 导入主配置文件
|
||||
import config
|
||||
importlib.reload(config)
|
||||
|
||||
# 使用主配置文件中的路径
|
||||
if config.SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, config.SCRIPTS_PATH)
|
||||
|
||||
# 尝试导入并运行主模块
|
||||
try:
|
||||
# 从scripts包导入Main
|
||||
from scripts import Main
|
||||
importlib.reload(Main)
|
||||
Main.main()
|
||||
except ImportError:
|
||||
# 尝试直接导入Main
|
||||
import Main
|
||||
importlib.reload(Main)
|
||||
Main.main()
|
||||
except Exception as e:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
print(f"Scripts path: {{config.SCRIPTS_PATH}}")
|
||||
print("sys.path:", sys.path)
|
||||
print(f"Contents of Scripts folder: {{os.listdir(config.SCRIPTS_PATH)}}")
|
||||
except ImportError:
|
||||
# 备用方案:如果无法导入主配置,使用硬编码路径
|
||||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
|
||||
try:
|
||||
# 尝试从scripts包导入Main
|
||||
from scripts import Main
|
||||
importlib.reload(Main)
|
||||
Main.main()
|
||||
except ImportError:
|
||||
# 尝试直接导入Main
|
||||
try:
|
||||
import Main
|
||||
importlib.reload(Main)
|
||||
Main.main()
|
||||
except Exception as e:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
print(f"Scripts path: {{SCRIPTS_PATH}}")
|
||||
print("sys.path:", sys.path)
|
||||
print(f"Contents of Scripts folder: {{os.listdir(SCRIPTS_PATH)}}")
|
||||
except Exception as e:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
"""
|
||||
def _get_reload_modules_command(self):
|
||||
"""Get the command string for the reload modules button"""
|
||||
return f"""
|
||||
import sys
|
||||
import os
|
||||
import importlib
|
||||
|
||||
# Set tool path
|
||||
TOOL_PATH = r'{TOOL_PATH}'
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
|
||||
# Set scripts path
|
||||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
|
||||
# Try to import and reload modules
|
||||
try:
|
||||
# Import ReloadModules directly
|
||||
from scripts import ReloadModules
|
||||
importlib.reload(ReloadModules)
|
||||
ReloadModules.show_reload_ui()
|
||||
except Exception as e:
|
||||
import maya.cmds as cmds
|
||||
error_msg = f"Error loading ReloadModules: {{str(e)}}"
|
||||
print(error_msg)
|
||||
cmds.warning(error_msg)
|
||||
print(f"Scripts path: {{SCRIPTS_PATH}}")
|
||||
print("sys.path:", sys.path)
|
||||
"""
|
||||
|
||||
def uninstall_tool(self):
|
||||
"""Uninstall the tool from Maya"""
|
||||
window_name = f"{TOOL_NAME}Window"
|
||||
dock_name = f"{TOOL_NAME}WindowDock"
|
||||
shelf_file = f"shelf_{TOOL_NAME}.mel"
|
||||
|
||||
# Clean __pycache__ directories
|
||||
print("Cleaning __pycache__ directories...")
|
||||
pycache_count = self.clean_pycache()
|
||||
print(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||||
|
||||
if cmds.window(window_name, exists=True):
|
||||
try:
|
||||
cmds.deleteUI(window_name)
|
||||
except Exception as error:
|
||||
print(f"Error closing {TOOL_NAME} window: {error}")
|
||||
|
||||
if cmds.dockControl(dock_name, exists=True):
|
||||
try:
|
||||
cmds.deleteUI(dock_name)
|
||||
except Exception as error:
|
||||
print(f"Error closing docked {TOOL_NAME} window: {error}")
|
||||
|
||||
self.uninstall_mod_file()
|
||||
|
||||
# Get the current shelf before removing it
|
||||
current_shelf = cmds.shelfTabLayout("ShelfLayout", query=True, selectTab=True)
|
||||
|
||||
# Delete Shelves and Buttons
|
||||
if cmds.shelfLayout(TOOL_NAME, exists=True):
|
||||
try:
|
||||
cmds.deleteUI(TOOL_NAME, layout=True)
|
||||
except Exception as error:
|
||||
print(f"Error deleting {TOOL_NAME} shelf: {error}")
|
||||
|
||||
self._clean_all_shelf_buttons()
|
||||
|
||||
# Remove from Python path
|
||||
if SCRIPTS_PATH in sys.path:
|
||||
sys.path.remove(SCRIPTS_PATH)
|
||||
|
||||
# Deleting Shelf Files
|
||||
shelf_path = os.path.join(
|
||||
cmds.internalVar(userAppDir=True),
|
||||
cmds.about(version=True),
|
||||
"prefs",
|
||||
"shelves",
|
||||
f"shelf_{TOOL_NAME}.mel"
|
||||
)
|
||||
|
||||
if os.path.exists(shelf_path):
|
||||
try:
|
||||
os.remove(shelf_path)
|
||||
except Exception as error:
|
||||
print(f"Error deleting shelf file: {error}")
|
||||
|
||||
# If the current tool shelf is a deleted tool shelf, switch to another tool shelf
|
||||
if current_shelf == TOOL_NAME:
|
||||
shelves = cmds.shelfTabLayout("ShelfLayout", query=True, childArray=True)
|
||||
if shelves and len(shelves) > 0:
|
||||
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=shelves[0])
|
||||
|
||||
self._show_uninstall_success_message()
|
||||
|
||||
def _clean_all_shelf_buttons(self):
|
||||
"""Clean up all shelf buttons related to the tool"""
|
||||
all_shelves = cmds.shelfTabLayout("ShelfLayout", query=True, childArray=True) or []
|
||||
for shelf in all_shelves:
|
||||
shelf_buttons = cmds.shelfLayout(shelf, query=True, childArray=True) or []
|
||||
for btn in shelf_buttons:
|
||||
if cmds.shelfButton(btn, query=True, exists=True):
|
||||
if cmds.shelfButton(btn, query=True, label=True) == TOOL_NAME:
|
||||
cmds.deleteUI(btn)
|
||||
|
||||
def _show_uninstall_success_message(self):
|
||||
"""Show uninstallation success message"""
|
||||
msg_box = QtWidgets.QMessageBox(self)
|
||||
msg_box.setWindowTitle("Uninstallation Successful")
|
||||
msg_box.setText(f"{TOOL_NAME} has been successfully uninstalled!")
|
||||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
msg_box.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
msg_box.setStyleSheet(self.styleSheet())
|
||||
msg_box.exec_()
|
||||
|
||||
def _show_install_success_message(self):
|
||||
msg_box = QtWidgets.QMessageBox(self)
|
||||
msg_box.setWindowTitle("Installation Successful")
|
||||
msg_box.setText(f"{TOOL_NAME} has been successfully installed!")
|
||||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
msg_box.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
msg_box.setStyleSheet(self.styleSheet())
|
||||
msg_box.exec_()
|
||||
|
||||
def _validate_paths(self):
|
||||
"""Validate all required paths exist"""
|
||||
paths = {
|
||||
"Root": TOOL_PATH,
|
||||
"Scripts": SCRIPTS_PATH,
|
||||
"Icons": ICONS_PATH
|
||||
}
|
||||
|
||||
for name, path in paths.items():
|
||||
if not os.path.exists(path):
|
||||
error_msg = f"Error: {name} path does not exist: {path}"
|
||||
print(error_msg)
|
||||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _log(self, message, error=False):
|
||||
"""Log messages with timestamp"""
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_message = f"[{timestamp}] {message}"
|
||||
print(log_message)
|
||||
if error:
|
||||
QtWidgets.QMessageBox.critical(self, "Error", message)
|
||||
|
||||
def _load_mel_shelf(self):
|
||||
"""Load mel shelf file with error handling"""
|
||||
try:
|
||||
mel.eval(f'loadNewShelf "shelf_{TOOL_NAME}.mel"')
|
||||
except Exception as error:
|
||||
self._log(f"Error loading shelf file: {error}", error=True)
|
||||
|
||||
#===================================== 6. Main Function =====================================
|
||||
def main():
|
||||
"""Main entry point for the installer"""
|
||||
try:
|
||||
dialog = InstallDialog()
|
||||
dialog.show()
|
||||
except Exception as error:
|
||||
print(f"Error launching installer: {error}")
|
||||
return -1
|
||||
return dialog
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
102
Plan.md
Normal file
102
Plan.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Maya插件开发计划
|
||||
|
||||
## 1. 项目目标与功能梳理
|
||||
- 支持MetaHuman同拓扑或自定义3D模型的绑定、DNA编辑、骨骼校准、DNA保存/加载、FBX导出、BlendShape编辑等。
|
||||
- 兼容Maya 2022-2025,Python 3.10-3.12,UTF-8编码。
|
||||
- UI采用Qt(参考`scripts/Qt.py`),样式统一在`scripts/ui/style.qss`。
|
||||
- 参考Epic官方MetaHuman DNA Calibration工具及相关文档,吸收SuperRigging等插件的设计思路。
|
||||
|
||||
## 2. 主要模块划分
|
||||
1. **配置与主控**
|
||||
- `config.py`(配置文件)
|
||||
- `scripts/Main.py`(主入口)
|
||||
- `scripts/ReloadModules.py`(模块热重载)
|
||||
2. **UI层**
|
||||
- `scripts/ui/`(所有UI模块,含主界面、子工具面板)
|
||||
- `scripts/ui/Qt.py`(Qt封装)
|
||||
- `scripts/ui/style.qss`(统一样式)
|
||||
3. **功能实现**
|
||||
- DNA文件处理(加载/保存/编辑)
|
||||
- 绑定与骨骼校准
|
||||
- BlendShape与控制器映射
|
||||
- FBX导出
|
||||
- 兼容MetaHuman DNA标准
|
||||
4. **工具与通用模块**
|
||||
- `scripts/utils/`(通用工具函数)
|
||||
|
||||
## 3. 阶段性开发计划
|
||||
### 第一阶段:基础框架与UI
|
||||
- [x] 搭建插件基础结构,主入口、配置加载
|
||||
- [x] 主界面UI搭建(参考UI图与.qss风格)
|
||||
- [x] 完善主界面各功能区分栏(模型、绑定、调整、定义)
|
||||
- [x] 优化 `main_window.py`,确保Tab加载各Panel,统一风格和注释
|
||||
- [x] 完善 `model_editor_panel.py`(模型编辑面板)
|
||||
- [x] 完善 `dna_browser_panel.py`(DNA浏览面板)
|
||||
- [x] 创建 `joint_calibration_panel.py`(骨骼校准面板)
|
||||
- [x] 创建 `blendshape_edit_panel.py`(混合变形编辑面板)
|
||||
- [x] 完善 `animation_system_panel.py`(动画系统面板)
|
||||
|
||||
### 第二阶段:核心功能开发
|
||||
- [ ] DNA文件读写与MetaHuman兼容
|
||||
- [ ] 绑定流程实现(支持MetaHuman与自定义模型)
|
||||
- [ ] 骨骼校准与自动分组
|
||||
- [ ] BlendShape与控制器映射
|
||||
- [ ] FBX导出与DNA文件导出
|
||||
|
||||
### 第三阶段:高级功能与优化
|
||||
- [ ] 批量处理与自动化工具
|
||||
- [ ] 多LOD支持与切换
|
||||
- [ ] 配置与模板管理
|
||||
- [ ] 插件性能与兼容性优化
|
||||
|
||||
### 第四阶段:文档与测试
|
||||
- [ ] 全流程测试脚本
|
||||
- [ ] 用户操作手册与开发文档完善
|
||||
- [ ] 参考文档和代码持续同步
|
||||
|
||||
---
|
||||
|
||||
## 进度记录与后续安排
|
||||
|
||||
### 2025-04-30 更新
|
||||
- 完成了基础框架与UI阶段的全部任务(第一阶段 100%)
|
||||
- 优化了主窗口与各功能面板的UI结构,确保风格统一
|
||||
- 创建了所有必要的面板类,包括:
|
||||
- ModelEditorPanel(模型编辑面板)
|
||||
- DNABrowserPanel(DNA浏览面板)
|
||||
- JointCalibrationPanel(骨骼校准面板)
|
||||
- BlendshapeEditPanel(混合变形编辑面板)
|
||||
- AnimationSystemPanel(动画系统面板)
|
||||
|
||||
### 下一步开发计划(第二阶段)
|
||||
1. **DNA文件读写与MetaHuman兼容**
|
||||
- 实现DNA文件的读取、解析与保存功能
|
||||
- 确保与MetaHuman DNA标准兼容
|
||||
- 完善DNAManager类,提供DNA文件操作API
|
||||
|
||||
2. **绑定流程实现**
|
||||
- 支持MetaHuman同拓扑模型的自动绑定
|
||||
- 支持自定义模型的绑定流程
|
||||
- 实现骨骼映射与权重传递
|
||||
|
||||
3. **骨骼校准与自动分组**
|
||||
- 完善JointCalibrationPanel的功能实现
|
||||
- 实现骨骼自动分组算法
|
||||
- 支持骨骼位置、旋转的微调与校准
|
||||
|
||||
4. **BlendShape与控制器映射**
|
||||
- 完善BlendshapeEditPanel的功能实现
|
||||
- 实现BlendShape的创建、编辑与管理
|
||||
- 支持控制器与BlendShape的映射关系设置
|
||||
|
||||
5. **FBX导出与DNA文件导出**
|
||||
- 实现模型与骨骼的FBX导出
|
||||
- 支持DNA文件的导出与版本控制
|
||||
- 确保导出文件与MetaHuman兼容
|
||||
|
||||
- 每完成一个功能模块,及时更新开发计划文档与进度百分比。
|
||||
- 代码变更后,自动检查代码规范、插件完整性,并给出下一步开发建议。
|
||||
|
||||
---
|
||||
|
||||
> 如需调整开发优先级或补充特定功能,请随时告知!
|
126
Readme.md
Normal file
126
Readme.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# MetaHuman DNA 自定义绑定插件
|
||||
|
||||
## 项目介绍
|
||||
- Maya的Metahuman自定义绑定插件
|
||||
- 语言: 基于Python
|
||||
- Maya版本: 2022, 2023, 2024, 2025
|
||||
- 本项目是一个Maya插件,提供全面的MetaHuman自定义解决方案,包括DNA编辑、骨骼校准、BlendShape编辑、动画交换等功能
|
||||
|
||||
## 核心功能
|
||||
### DNA浏览器
|
||||
- 自动读取DNA文件,显示在项目浏览器中
|
||||
- 可以打开、保存DNA文件
|
||||
- 可以加载DNA文件以供编辑
|
||||
- 可编辑DNA文件基本信息
|
||||
- DNA文件保存和写入
|
||||
### 模型编辑,DNA编辑与校准
|
||||
- 自定义造型模型快速指认和绑定(Metahuman拓扑)
|
||||
- 自动生成其他身体的配件(睫毛,泪腺,舌头等)
|
||||
- 校准骨骼位置以匹配自定义模型
|
||||
- 自动修复接缝和权重问题
|
||||
- 根据DNA生成身体并支持自定义绑定
|
||||
- 头和身体接缝修复
|
||||
- 支持自定义拓扑结构的模型绑定
|
||||
- 赋予材质,以及顶点色
|
||||
- FBX模型导入和导出
|
||||
### 面部表情系统
|
||||
- 编辑和优化BlendShape
|
||||
- 批量导出和导入BlendShape,支持表情克隆
|
||||
- 支持Blendshape修改并更新DNA
|
||||
- 表情范围调整和克隆
|
||||
### 动画系统
|
||||
- 坐标系统自动转换
|
||||
- 支持面部和身体动画
|
||||
- RBF变形器,用于不同体型间的衣物配饰转移
|
||||
- 支持动画导入和导出
|
||||
- 动画实时预览系统
|
||||
|
||||
## 主要模块
|
||||
|
||||
### 工具栏 Toolbar
|
||||
- 加载预设
|
||||
- 保存预设
|
||||
- 导入DNA
|
||||
- 导出DNA
|
||||
- 创建RL4节点(用于切换DNA编辑的状态)
|
||||
- 删除RL4节点(用于切换DNA编辑的状态)
|
||||
|
||||
### 几何体 Geomery
|
||||
- 模型拾取以及加载
|
||||
- LOD模型分级过滤
|
||||
- LOD模型创建
|
||||
- 自动加载模型
|
||||
- 标准化命名
|
||||
- 自动分组
|
||||
- 生成面部配件(睫毛,舌头,泪腺 等)
|
||||
- 修复接缝(修复法线)
|
||||
- 修复点序
|
||||
|
||||
### 绑定 Rigging
|
||||
- DNA浏览器
|
||||
- 根据DNA导入骨骼
|
||||
- 根据DNA生成身体
|
||||
- DNA校准
|
||||
- 骨骼位置校准
|
||||
- 创建绑定
|
||||
- 复制蒙皮
|
||||
|
||||
### 行为 Behaviour
|
||||
- Blendshape自动加载,刷新,筛选
|
||||
- 次级Blendshape自动加载,刷新,筛选
|
||||
- Blendshape批量导出和导入
|
||||
- Blendshape范围编辑
|
||||
- Blendshape镜像
|
||||
- Blendshape查找翻转目标
|
||||
- Blendshape重建
|
||||
- 表情控制器还原默认表情
|
||||
- 查找选择表情
|
||||
- 控制面板查找
|
||||
- 选择关联关节
|
||||
- 写入当前表情
|
||||
- 写入镜像表情
|
||||
|
||||
### 定义 Definition
|
||||
- LOD, Meshes, Joints, Blendshape, AnimatedMap 加载和刷新
|
||||
- 写入: 写入关节默认位置,写入几何体,写入蒙皮,写入混合变形目标
|
||||
- 创建:创建混合变形,绑定蒙皮,取消蒙皮
|
||||
- 工具:重新定位头部关节,重新定位身体关节,重新定位全身关节,快速创建预设
|
||||
|
||||
|
||||
## 技术特点
|
||||
- 基于Epic Games的MetaHuman-DNA-Calibration库
|
||||
- 集成PoseWrangler系统实现RBF变形
|
||||
- 优化算法提升性能
|
||||
- 多语言界面支持(中文简体、繁体、英语、日语、韩语、法语)
|
||||
|
||||
## 开发目标
|
||||
1. 实现完整的DNA校准和编辑功能
|
||||
2. 提供直观的用户界面,简化复杂操作
|
||||
3. 支持多种拓扑结构的自定义模型
|
||||
4. 优化性能,提高大型模型的处理效率
|
||||
5. 实现UE与Maya之间的无缝动画交换
|
||||
6. 提供全面的文档和教程
|
||||
|
||||
## 开发路线图
|
||||
- [x] 基础DNA读取和修改功能
|
||||
- [x] 骨骼校准系统
|
||||
- [x] BlendShape编辑工具
|
||||
- [x] 面部表情系统
|
||||
- [x] 动画导入导出
|
||||
- [x] 多拓扑支持
|
||||
- [x] RBF变形器
|
||||
- [x] 一键创建LOD
|
||||
- [x] 自动修复工具
|
||||
- [x] 赋予材质,以及顶点色
|
||||
- [x] 动画实时预览系统
|
||||
|
||||
## 安装与使用
|
||||
1. 将插件文件复制到Maya插件目录
|
||||
2. 在Maya中加载插件
|
||||
3. 使用插件界面进行操作
|
||||
|
||||
## 依赖项
|
||||
- Maya 2022或更高版本
|
||||
- Python 3.7+
|
||||
- MetaHuman-DNA-Calibration库
|
||||
- PoseWrangler系统
|
88
config.py
Normal file
88
config.py
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
# Basic Information
|
||||
TOOL_NAME = "MetaFusion"
|
||||
TOOL_VERSION = "Alpha v1.0.0"
|
||||
TOOL_AUTHOR = "CGNICO"
|
||||
|
||||
TOOL_YEAR = "2025"
|
||||
TOOL_MOD_FILENAME = f"{TOOL_NAME.lower()}.mod"
|
||||
TOOL_LANG = "en_US"
|
||||
TOOL_WSCL_NAME = f"{TOOL_NAME}WorkspaceControl"
|
||||
TOOL_HELP_URL = f"http://10.72.61.59:3000/ArtGroup/{TOOL_NAME}/wiki"
|
||||
|
||||
# Path Configuration
|
||||
TOOL_PATH = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/")
|
||||
SCRIPTS_PATH = os.path.join(TOOL_PATH, "scripts").replace("\\", "/")
|
||||
TOOL_MAIN_SCRIPT = os.path.join(TOOL_PATH, "scripts", "Main.py").replace("\\", "/")
|
||||
UI_PATH = os.path.join(TOOL_PATH, "scripts", "ui").replace("\\", "/")
|
||||
STYLE_FILE = os.path.join(TOOL_PATH, "scripts", "ui", "style.qss").replace("\\", "/")
|
||||
ICONS_PATH = os.path.join(TOOL_PATH, "icons").replace("\\", "/")
|
||||
TOOL_ICON = os.path.join(TOOL_PATH, "icons", "logo.png").replace("\\", "/")
|
||||
ASSETS_PATH = os.path.join(TOOL_PATH, "assets").replace("\\", "/")
|
||||
DNA_FILE_PATH = os.path.join(TOOL_PATH, "assets", "dna").replace("\\", "/")
|
||||
DNA_IMG_PATH = os.path.join(TOOL_PATH, "assets", "img").replace("\\", "/")
|
||||
TOOL_COMMAND_ICON = TOOL_ICON
|
||||
|
||||
# DNA Config
|
||||
# GUI相关常量
|
||||
GUI_HOLDER = "GUI_Holder"
|
||||
ANALOG_GUI_HOLDER = "Analog_GUI_Holder"
|
||||
RIG_LOGIC_PREFIX = "rl4Embedded_"
|
||||
# 用于打印蒙皮权重的范围
|
||||
SKIN_WEIGHT_PRINT_RANGE = 2000
|
||||
|
||||
# System Information
|
||||
import maya.cmds as cmds
|
||||
SYSTEM_OS = "Windows" if cmds.about(os=True).lower().startswith("win") else "Linux"
|
||||
# Define Maya version as integer for easier version comparison
|
||||
MAYA_VERSION = str(int(cmds.about(version=True).split('.')[0]))
|
||||
|
||||
# Plugins Path
|
||||
def get_pydna_dir():
|
||||
"""Get Python version directory name"""
|
||||
version = sys.version_info
|
||||
if version.major == 3:
|
||||
if version.minor == 9 and version.micro == 7:
|
||||
return "python397"
|
||||
elif version.minor == 10 and version.micro == 8:
|
||||
return "python3108"
|
||||
elif version.minor == 11:
|
||||
return "python311"
|
||||
return "python3"
|
||||
PYVERSION_DIR = get_pydna_dir()
|
||||
PLUGINS_PATH = os.path.join(TOOL_PATH, "plugins", SYSTEM_OS, MAYA_VERSION).replace("\\", "/")
|
||||
PYDNA_PATH = os.path.join(TOOL_PATH, "plugins", SYSTEM_OS, "pydna", PYVERSION_DIR).replace("\\", "/")
|
||||
|
||||
# Initialize Paths
|
||||
def init_paths():
|
||||
"""Initialize paths"""
|
||||
paths = [
|
||||
TOOL_PATH,
|
||||
SCRIPTS_PATH,
|
||||
ICONS_PATH,
|
||||
ASSETS_PATH,
|
||||
DNA_FILE_PATH,
|
||||
DNA_IMG_PATH,
|
||||
UI_PATH,
|
||||
PLUGINS_PATH,
|
||||
PYDNA_PATH,
|
||||
STYLE_FILE
|
||||
]
|
||||
|
||||
for path in paths:
|
||||
if not os.path.exists(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
print(f"Created directory: {path}")
|
||||
except Exception as e:
|
||||
print(f"Failed to create directory: {path}, error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Initialize paths
|
||||
init_paths()
|
BIN
plugins/Linux/2022/MayaUE4RBFPlugin2022.mll
Normal file
BIN
plugins/Linux/2022/MayaUE4RBFPlugin2022.mll
Normal file
Binary file not shown.
BIN
plugins/Linux/2022/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Linux/2022/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Linux/2022/_py3dnacalib.so
Normal file
BIN
plugins/Linux/2022/_py3dnacalib.so
Normal file
Binary file not shown.
1172
plugins/Linux/2022/dnacalib.py
Normal file
1172
plugins/Linux/2022/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/2022/libdnacalib.so.6
Normal file
BIN
plugins/Linux/2022/libdnacalib.so.6
Normal file
Binary file not shown.
BIN
plugins/Linux/2022/libembeddedRL4.so
Normal file
BIN
plugins/Linux/2022/libembeddedRL4.so
Normal file
Binary file not shown.
BIN
plugins/Linux/2022/libembeddedRL4.so.8
Normal file
BIN
plugins/Linux/2022/libembeddedRL4.so.8
Normal file
Binary file not shown.
BIN
plugins/Linux/2022/libembeddedRL4.so.8.0.8
Normal file
BIN
plugins/Linux/2022/libembeddedRL4.so.8.0.8
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/MayaUE4RBFPlugin2023.mll
Normal file
BIN
plugins/Linux/2023/MayaUE4RBFPlugin2023.mll
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Linux/2023/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/_py3dnacalib.so
Normal file
BIN
plugins/Linux/2023/_py3dnacalib.so
Normal file
Binary file not shown.
1172
plugins/Linux/2023/dnacalib.py
Normal file
1172
plugins/Linux/2023/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/2023/libdnacalib.so.6
Normal file
BIN
plugins/Linux/2023/libdnacalib.so.6
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/libembeddedRL4.so
Normal file
BIN
plugins/Linux/2023/libembeddedRL4.so
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/libembeddedRL4.so.8
Normal file
BIN
plugins/Linux/2023/libembeddedRL4.so.8
Normal file
Binary file not shown.
BIN
plugins/Linux/2023/libembeddedRL4.so.8.0.8
Normal file
BIN
plugins/Linux/2023/libembeddedRL4.so.8.0.8
Normal file
Binary file not shown.
BIN
plugins/Linux/2024/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Linux/2024/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Linux/2024/_py3dnacalib.so
Normal file
BIN
plugins/Linux/2024/_py3dnacalib.so
Normal file
Binary file not shown.
1172
plugins/Linux/2024/dnacalib.py
Normal file
1172
plugins/Linux/2024/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/2024/libdnacalib.so.6
Normal file
BIN
plugins/Linux/2024/libdnacalib.so.6
Normal file
Binary file not shown.
BIN
plugins/Linux/2024/libembeddedRL4.so
Normal file
BIN
plugins/Linux/2024/libembeddedRL4.so
Normal file
Binary file not shown.
0
plugins/Linux/2025/MayaUERBFPlugin.mll
Normal file
0
plugins/Linux/2025/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Linux/2025/_py3dnacalib.so
Normal file
BIN
plugins/Linux/2025/_py3dnacalib.so
Normal file
Binary file not shown.
633
plugins/Linux/2025/dnacalib.py
Normal file
633
plugins/Linux/2025/dnacalib.py
Normal file
@@ -0,0 +1,633 @@
|
||||
# This file was automatically generated by SWIG (http://www.swig.org).
|
||||
# Version 4.0.2
|
||||
#
|
||||
# Do not make changes to this file unless you know what you are doing--modify
|
||||
# the SWIG interface file instead.
|
||||
|
||||
from sys import version_info as _swig_python_version_info
|
||||
if _swig_python_version_info < (2, 7, 0):
|
||||
raise RuntimeError("Python 2.7 or later required")
|
||||
|
||||
# Import the low-level C/C++ module
|
||||
if __package__ or "." in __name__:
|
||||
from . import _py3dnacalib
|
||||
else:
|
||||
import _py3dnacalib
|
||||
|
||||
try:
|
||||
import builtins as __builtin__
|
||||
except ImportError:
|
||||
import __builtin__
|
||||
|
||||
def _swig_repr(self):
|
||||
try:
|
||||
strthis = "proxy of " + self.this.__repr__()
|
||||
except __builtin__.Exception:
|
||||
strthis = ""
|
||||
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
|
||||
|
||||
|
||||
def _swig_setattr_nondynamic_instance_variable(set):
|
||||
def set_instance_attr(self, name, value):
|
||||
if name == "thisown":
|
||||
self.this.own(value)
|
||||
elif name == "this":
|
||||
set(self, name, value)
|
||||
elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
|
||||
set(self, name, value)
|
||||
else:
|
||||
raise AttributeError("You cannot add instance attributes to %s" % self)
|
||||
return set_instance_attr
|
||||
|
||||
|
||||
def _swig_setattr_nondynamic_class_variable(set):
|
||||
def set_class_attr(cls, name, value):
|
||||
if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
|
||||
set(cls, name, value)
|
||||
else:
|
||||
raise AttributeError("You cannot add class attributes to %s" % cls)
|
||||
return set_class_attr
|
||||
|
||||
|
||||
def _swig_add_metaclass(metaclass):
|
||||
"""Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
|
||||
def wrapper(cls):
|
||||
return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
|
||||
return wrapper
|
||||
|
||||
|
||||
class _SwigNonDynamicMeta(type):
|
||||
"""Meta class to enforce nondynamic attributes (no new attributes) for a class"""
|
||||
__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)
|
||||
|
||||
|
||||
|
||||
def __new_decorator(factory_func, original_new):
|
||||
@staticmethod
|
||||
def __new(cls, *args, **kwargs):
|
||||
# FIXME: while this workaround solves the immediate issue with the set of classes we currently have,
|
||||
# it will fail for classes that use a factory function but need no parameters at all, in which case
|
||||
# the factory function will never be invoked, only the original __new__ function.
|
||||
if args or kwargs:
|
||||
return factory_func(*args, **kwargs)
|
||||
return original_new(cls)
|
||||
return __new
|
||||
|
||||
def __managed_init(self, *args, **kwargs):
|
||||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
|
||||
import dna
|
||||
class VersionInfo(object):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
|
||||
@staticmethod
|
||||
def getMajorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMajorVersion()
|
||||
|
||||
@staticmethod
|
||||
def getMinorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMinorVersion()
|
||||
|
||||
@staticmethod
|
||||
def getPatchVersion():
|
||||
return _py3dnacalib.VersionInfo_getPatchVersion()
|
||||
|
||||
@staticmethod
|
||||
def getVersionString():
|
||||
return _py3dnacalib.VersionInfo_getVersionString()
|
||||
|
||||
def __init__(self):
|
||||
_py3dnacalib.VersionInfo_swiginit(self, _py3dnacalib.new_VersionInfo())
|
||||
__swig_destroy__ = _py3dnacalib.delete_VersionInfo
|
||||
|
||||
# Register VersionInfo in _py3dnacalib:
|
||||
_py3dnacalib.VersionInfo_swigregister(VersionInfo)
|
||||
|
||||
def VersionInfo_getMajorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMajorVersion()
|
||||
|
||||
def VersionInfo_getMinorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMinorVersion()
|
||||
|
||||
def VersionInfo_getPatchVersion():
|
||||
return _py3dnacalib.VersionInfo_getPatchVersion()
|
||||
|
||||
def VersionInfo_getVersionString():
|
||||
return _py3dnacalib.VersionInfo_getVersionString()
|
||||
|
||||
class DNACalibDNAReader(dna.Reader):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise AttributeError("No constructor defined - class is abstract")
|
||||
__repr__ = _swig_repr
|
||||
|
||||
@staticmethod
|
||||
def create(*args):
|
||||
return _py3dnacalib.DNACalibDNAReader_create(*args)
|
||||
|
||||
@staticmethod
|
||||
def destroy(instance):
|
||||
return _py3dnacalib.DNACalibDNAReader_destroy(instance)
|
||||
|
||||
# Register DNACalibDNAReader in _py3dnacalib:
|
||||
_py3dnacalib.DNACalibDNAReader_swigregister(DNACalibDNAReader)
|
||||
|
||||
def DNACalibDNAReader_create(*args):
|
||||
return _py3dnacalib.DNACalibDNAReader_create(*args)
|
||||
|
||||
def DNACalibDNAReader_destroy(instance):
|
||||
return _py3dnacalib.DNACalibDNAReader_destroy(instance)
|
||||
|
||||
|
||||
DNACalibDNAReader.__new__ = __new_decorator(DNACalibDNAReader_create, DNACalibDNAReader.__new__)
|
||||
DNACalibDNAReader.__del__ = lambda instance: DNACalibDNAReader_destroy(instance)
|
||||
DNACalibDNAReader.__init__ = __managed_init
|
||||
del DNACalibDNAReader.create
|
||||
del DNACalibDNAReader.destroy
|
||||
|
||||
class Command(object):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise AttributeError("No constructor defined - class is abstract")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_Command
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.Command_run(self, output)
|
||||
|
||||
# Register Command in _py3dnacalib:
|
||||
_py3dnacalib.Command_swigregister(Command)
|
||||
|
||||
VectorOperation_Interpolate = _py3dnacalib.VectorOperation_Interpolate
|
||||
VectorOperation_Add = _py3dnacalib.VectorOperation_Add
|
||||
VectorOperation_Subtract = _py3dnacalib.VectorOperation_Subtract
|
||||
VectorOperation_Multiply = _py3dnacalib.VectorOperation_Multiply
|
||||
class CommandSequence(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_CommandSequence
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.CommandSequence_swiginit(self, _py3dnacalib.new_CommandSequence(*args))
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.CommandSequence_run(self, output)
|
||||
|
||||
def add(self, command):
|
||||
return _py3dnacalib.CommandSequence_add(self, command)
|
||||
|
||||
def remove(self, command):
|
||||
return _py3dnacalib.CommandSequence_remove(self, command)
|
||||
|
||||
def contains(self, command):
|
||||
return _py3dnacalib.CommandSequence_contains(self, command)
|
||||
|
||||
def size(self):
|
||||
return _py3dnacalib.CommandSequence_size(self)
|
||||
|
||||
# Register CommandSequence in _py3dnacalib:
|
||||
_py3dnacalib.CommandSequence_swigregister(CommandSequence)
|
||||
|
||||
|
||||
def command_sequence_init(_init):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
self._commands = []
|
||||
_init(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def command_sequence_add(_add):
|
||||
def wrapper(self, command):
|
||||
self._commands.append(command)
|
||||
_add(self, command)
|
||||
return wrapper
|
||||
|
||||
def command_sequence_remove(_remove):
|
||||
def wrapper(self, command):
|
||||
self._commands.remove(command)
|
||||
_remove(self, command)
|
||||
return wrapper
|
||||
|
||||
CommandSequence.__init__ = command_sequence_init(CommandSequence.__init__)
|
||||
CommandSequence.add = command_sequence_add(CommandSequence.add)
|
||||
CommandSequence.remove = command_sequence_remove(CommandSequence.remove)
|
||||
|
||||
class CalculateMeshLowerLODsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_CalculateMeshLowerLODsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.CalculateMeshLowerLODsCommand_swiginit(self, _py3dnacalib.new_CalculateMeshLowerLODsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.CalculateMeshLowerLODsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.CalculateMeshLowerLODsCommand_run(self, output)
|
||||
|
||||
# Register CalculateMeshLowerLODsCommand in _py3dnacalib:
|
||||
_py3dnacalib.CalculateMeshLowerLODsCommand_swigregister(CalculateMeshLowerLODsCommand)
|
||||
|
||||
class ClearBlendShapesCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_ClearBlendShapesCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.ClearBlendShapesCommand_swiginit(self, _py3dnacalib.new_ClearBlendShapesCommand(*args))
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.ClearBlendShapesCommand_run(self, output)
|
||||
|
||||
# Register ClearBlendShapesCommand in _py3dnacalib:
|
||||
_py3dnacalib.ClearBlendShapesCommand_swigregister(ClearBlendShapesCommand)
|
||||
|
||||
class PruneBlendShapeTargetsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_PruneBlendShapeTargetsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.PruneBlendShapeTargetsCommand_swiginit(self, _py3dnacalib.new_PruneBlendShapeTargetsCommand(*args))
|
||||
|
||||
def setThreshold(self, threshold):
|
||||
return _py3dnacalib.PruneBlendShapeTargetsCommand_setThreshold(self, threshold)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.PruneBlendShapeTargetsCommand_run(self, output)
|
||||
|
||||
# Register PruneBlendShapeTargetsCommand in _py3dnacalib:
|
||||
_py3dnacalib.PruneBlendShapeTargetsCommand_swigregister(PruneBlendShapeTargetsCommand)
|
||||
|
||||
class RemoveAnimatedMapCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveAnimatedMapCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveAnimatedMapCommand_swiginit(self, _py3dnacalib.new_RemoveAnimatedMapCommand(*args))
|
||||
|
||||
def setAnimatedMapIndex(self, animatedMapIndex):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_setAnimatedMapIndex(self, animatedMapIndex)
|
||||
|
||||
def setAnimatedMapIndices(self, animatedMapIndices):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_setAnimatedMapIndices(self, animatedMapIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_run(self, output)
|
||||
|
||||
# Register RemoveAnimatedMapCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveAnimatedMapCommand_swigregister(RemoveAnimatedMapCommand)
|
||||
|
||||
class RemoveBlendShapeCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveBlendShapeCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveBlendShapeCommand_swiginit(self, _py3dnacalib.new_RemoveBlendShapeCommand(*args))
|
||||
|
||||
def setBlendShapeIndex(self, blendShapeIndex):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_setBlendShapeIndex(self, blendShapeIndex)
|
||||
|
||||
def setBlendShapeIndices(self, blendShapeIndices):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_setBlendShapeIndices(self, blendShapeIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_run(self, output)
|
||||
|
||||
# Register RemoveBlendShapeCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveBlendShapeCommand_swigregister(RemoveBlendShapeCommand)
|
||||
|
||||
class RemoveJointAnimationCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveJointAnimationCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveJointAnimationCommand_swiginit(self, _py3dnacalib.new_RemoveJointAnimationCommand(*args))
|
||||
|
||||
def setJointIndex(self, jointIndex):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_setJointIndex(self, jointIndex)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_run(self, output)
|
||||
|
||||
# Register RemoveJointAnimationCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveJointAnimationCommand_swigregister(RemoveJointAnimationCommand)
|
||||
|
||||
class RemoveJointCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveJointCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveJointCommand_swiginit(self, _py3dnacalib.new_RemoveJointCommand(*args))
|
||||
|
||||
def setJointIndex(self, jointIndex):
|
||||
return _py3dnacalib.RemoveJointCommand_setJointIndex(self, jointIndex)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.RemoveJointCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveJointCommand_run(self, output)
|
||||
|
||||
# Register RemoveJointCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveJointCommand_swigregister(RemoveJointCommand)
|
||||
|
||||
class RemoveMeshCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveMeshCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveMeshCommand_swiginit(self, _py3dnacalib.new_RemoveMeshCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.RemoveMeshCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setMeshIndices(self, meshIndices):
|
||||
return _py3dnacalib.RemoveMeshCommand_setMeshIndices(self, meshIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveMeshCommand_run(self, output)
|
||||
|
||||
# Register RemoveMeshCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveMeshCommand_swigregister(RemoveMeshCommand)
|
||||
|
||||
class RenameAnimatedMapCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameAnimatedMapCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameAnimatedMapCommand_swiginit(self, _py3dnacalib.new_RenameAnimatedMapCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameAnimatedMapCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameAnimatedMapCommand_run(self, output)
|
||||
|
||||
# Register RenameAnimatedMapCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameAnimatedMapCommand_swigregister(RenameAnimatedMapCommand)
|
||||
|
||||
class RenameBlendShapeCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameBlendShapeCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameBlendShapeCommand_swiginit(self, _py3dnacalib.new_RenameBlendShapeCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameBlendShapeCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameBlendShapeCommand_run(self, output)
|
||||
|
||||
# Register RenameBlendShapeCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameBlendShapeCommand_swigregister(RenameBlendShapeCommand)
|
||||
|
||||
class RenameJointCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameJointCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameJointCommand_swiginit(self, _py3dnacalib.new_RenameJointCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameJointCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameJointCommand_run(self, output)
|
||||
|
||||
# Register RenameJointCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameJointCommand_swigregister(RenameJointCommand)
|
||||
|
||||
class RenameMeshCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameMeshCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameMeshCommand_swiginit(self, _py3dnacalib.new_RenameMeshCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameMeshCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameMeshCommand_run(self, output)
|
||||
|
||||
# Register RenameMeshCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameMeshCommand_swigregister(RenameMeshCommand)
|
||||
|
||||
class RotateCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RotateCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RotateCommand_swiginit(self, _py3dnacalib.new_RotateCommand(*args))
|
||||
|
||||
def setRotation(self, degrees):
|
||||
return _py3dnacalib.RotateCommand_setRotation(self, degrees)
|
||||
|
||||
def setOrigin(self, origin):
|
||||
return _py3dnacalib.RotateCommand_setOrigin(self, origin)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RotateCommand_run(self, output)
|
||||
|
||||
# Register RotateCommand in _py3dnacalib:
|
||||
_py3dnacalib.RotateCommand_swigregister(RotateCommand)
|
||||
|
||||
class ScaleCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_ScaleCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.ScaleCommand_swiginit(self, _py3dnacalib.new_ScaleCommand(*args))
|
||||
|
||||
def setScale(self, scale):
|
||||
return _py3dnacalib.ScaleCommand_setScale(self, scale)
|
||||
|
||||
def setOrigin(self, origin):
|
||||
return _py3dnacalib.ScaleCommand_setOrigin(self, origin)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.ScaleCommand_run(self, output)
|
||||
|
||||
# Register ScaleCommand in _py3dnacalib:
|
||||
_py3dnacalib.ScaleCommand_swigregister(ScaleCommand)
|
||||
|
||||
class SetBlendShapeTargetDeltasCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetBlendShapeTargetDeltasCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetBlendShapeTargetDeltasCommand_swiginit(self, _py3dnacalib.new_SetBlendShapeTargetDeltasCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setBlendShapeTargetIndex(self, blendShapeTargetIndex):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setBlendShapeTargetIndex(self, blendShapeTargetIndex)
|
||||
|
||||
def setDeltas(self, *args):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setDeltas(self, *args)
|
||||
|
||||
def setVertexIndices(self, vertexIndices):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setVertexIndices(self, vertexIndices)
|
||||
|
||||
def setMasks(self, masks):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setMasks(self, masks)
|
||||
|
||||
def setOperation(self, operation):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setOperation(self, operation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_run(self, output)
|
||||
|
||||
# Register SetBlendShapeTargetDeltasCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetBlendShapeTargetDeltasCommand_swigregister(SetBlendShapeTargetDeltasCommand)
|
||||
cvar = _py3dnacalib.cvar
|
||||
SetBlendShapeTargetDeltasCommand.VertexIndicesOutOfBoundsError = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_VertexIndicesOutOfBoundsError
|
||||
SetBlendShapeTargetDeltasCommand.NoVertexIndicesSetError = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_NoVertexIndicesSetError
|
||||
SetBlendShapeTargetDeltasCommand.DeltasVertexIndicesCountMismatch = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_DeltasVertexIndicesCountMismatch
|
||||
SetBlendShapeTargetDeltasCommand.DeltasMasksCountMismatch = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_DeltasMasksCountMismatch
|
||||
|
||||
class SetLODsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetLODsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetLODsCommand_swiginit(self, _py3dnacalib.new_SetLODsCommand(*args))
|
||||
|
||||
def setLODs(self, lods):
|
||||
return _py3dnacalib.SetLODsCommand_setLODs(self, lods)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetLODsCommand_run(self, output)
|
||||
|
||||
# Register SetLODsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetLODsCommand_swigregister(SetLODsCommand)
|
||||
|
||||
class SetNeutralJointRotationsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetNeutralJointRotationsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetNeutralJointRotationsCommand_swiginit(self, _py3dnacalib.new_SetNeutralJointRotationsCommand(*args))
|
||||
|
||||
def setRotations(self, *args):
|
||||
return _py3dnacalib.SetNeutralJointRotationsCommand_setRotations(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetNeutralJointRotationsCommand_run(self, output)
|
||||
|
||||
# Register SetNeutralJointRotationsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetNeutralJointRotationsCommand_swigregister(SetNeutralJointRotationsCommand)
|
||||
|
||||
class SetNeutralJointTranslationsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetNeutralJointTranslationsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetNeutralJointTranslationsCommand_swiginit(self, _py3dnacalib.new_SetNeutralJointTranslationsCommand(*args))
|
||||
|
||||
def setTranslations(self, *args):
|
||||
return _py3dnacalib.SetNeutralJointTranslationsCommand_setTranslations(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetNeutralJointTranslationsCommand_run(self, output)
|
||||
|
||||
# Register SetNeutralJointTranslationsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetNeutralJointTranslationsCommand_swigregister(SetNeutralJointTranslationsCommand)
|
||||
|
||||
class SetSkinWeightsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetSkinWeightsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetSkinWeightsCommand_swiginit(self, _py3dnacalib.new_SetSkinWeightsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setVertexIndex(self, vertexIndex):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setVertexIndex(self, vertexIndex)
|
||||
|
||||
def setWeights(self, weights):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setWeights(self, weights)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_run(self, output)
|
||||
|
||||
# Register SetSkinWeightsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetSkinWeightsCommand_swigregister(SetSkinWeightsCommand)
|
||||
|
||||
class SetVertexPositionsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetVertexPositionsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetVertexPositionsCommand_swiginit(self, _py3dnacalib.new_SetVertexPositionsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setPositions(self, *args):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setPositions(self, *args)
|
||||
|
||||
def setMasks(self, masks):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setMasks(self, masks)
|
||||
|
||||
def setOperation(self, operation):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setOperation(self, operation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_run(self, output)
|
||||
|
||||
# Register SetVertexPositionsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetVertexPositionsCommand_swigregister(SetVertexPositionsCommand)
|
||||
SetVertexPositionsCommand.PositionsMasksCountMismatch = _py3dnacalib.cvar.SetVertexPositionsCommand_PositionsMasksCountMismatch
|
||||
|
||||
class TranslateCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_TranslateCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.TranslateCommand_swiginit(self, _py3dnacalib.new_TranslateCommand(*args))
|
||||
|
||||
def setTranslation(self, translation):
|
||||
return _py3dnacalib.TranslateCommand_setTranslation(self, translation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.TranslateCommand_run(self, output)
|
||||
|
||||
# Register TranslateCommand in _py3dnacalib:
|
||||
_py3dnacalib.TranslateCommand_swigregister(TranslateCommand)
|
||||
|
||||
|
||||
|
0
plugins/Linux/2025/embeddedRL4.so
Normal file
0
plugins/Linux/2025/embeddedRL4.so
Normal file
BIN
plugins/Linux/2025/libdnacalib.so.6
Normal file
BIN
plugins/Linux/2025/libdnacalib.so.6
Normal file
Binary file not shown.
BIN
plugins/Linux/pydna/python3/_py3dna.so
Normal file
BIN
plugins/Linux/pydna/python3/_py3dna.so
Normal file
Binary file not shown.
1403
plugins/Linux/pydna/python3/dna.py
Normal file
1403
plugins/Linux/pydna/python3/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/pydna/python3/libdna.so.7.1.0
Normal file
BIN
plugins/Linux/pydna/python3/libdna.so.7.1.0
Normal file
Binary file not shown.
BIN
plugins/Linux/pydna/python3108/_py3dna.so
Normal file
BIN
plugins/Linux/pydna/python3108/_py3dna.so
Normal file
Binary file not shown.
1403
plugins/Linux/pydna/python3108/dna.py
Normal file
1403
plugins/Linux/pydna/python3108/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/pydna/python3108/libdna.so.7.1.0
Normal file
BIN
plugins/Linux/pydna/python3108/libdna.so.7.1.0
Normal file
Binary file not shown.
0
plugins/Linux/pydna/python311/_py3dna.so
Normal file
0
plugins/Linux/pydna/python311/_py3dna.so
Normal file
1403
plugins/Linux/pydna/python311/dna.py
Normal file
1403
plugins/Linux/pydna/python311/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
0
plugins/Linux/pydna/python311/libdna.so.7
Normal file
0
plugins/Linux/pydna/python311/libdna.so.7
Normal file
BIN
plugins/Linux/pydna/python397/_py3dna.so
Normal file
BIN
plugins/Linux/pydna/python397/_py3dna.so
Normal file
Binary file not shown.
1403
plugins/Linux/pydna/python397/dna.py
Normal file
1403
plugins/Linux/pydna/python397/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Linux/pydna/python397/libdna.so.7.1.0
Normal file
BIN
plugins/Linux/pydna/python397/libdna.so.7.1.0
Normal file
Binary file not shown.
BIN
plugins/Windows/2018/MayaUE4RBFPlugin2018.mll
Normal file
BIN
plugins/Windows/2018/MayaUE4RBFPlugin2018.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2018/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2018/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2018/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2018/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2019/MayaUE4RBFPlugin2019.mll
Normal file
BIN
plugins/Windows/2019/MayaUE4RBFPlugin2019.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2019/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2019/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2019/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2019/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2020/MayaUE4RBFPlugin2020.mll
Normal file
BIN
plugins/Windows/2020/MayaUE4RBFPlugin2020.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2020/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2020/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2020/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2020/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2022/MayaUE4RBFPlugin2022.mll
Normal file
BIN
plugins/Windows/2022/MayaUE4RBFPlugin2022.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2022/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2022/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2022/_py3dnacalib.pyd
Normal file
BIN
plugins/Windows/2022/_py3dnacalib.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/2022/dnacalib.dll
Normal file
BIN
plugins/Windows/2022/dnacalib.dll
Normal file
Binary file not shown.
1127
plugins/Windows/2022/dnacalib.py
Normal file
1127
plugins/Windows/2022/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/2022/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2022/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2023/MayaUE4RBFPlugin2023.mll
Normal file
BIN
plugins/Windows/2023/MayaUE4RBFPlugin2023.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2023/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2023/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2023/_py3dnacalib.pyd
Normal file
BIN
plugins/Windows/2023/_py3dnacalib.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/2023/dnacalib.dll
Normal file
BIN
plugins/Windows/2023/dnacalib.dll
Normal file
Binary file not shown.
1127
plugins/Windows/2023/dnacalib.py
Normal file
1127
plugins/Windows/2023/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/2023/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2023/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2024/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2024/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2024/_py3dnacalib.pyd
Normal file
BIN
plugins/Windows/2024/_py3dnacalib.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/2024/dnacalib.dll
Normal file
BIN
plugins/Windows/2024/dnacalib.dll
Normal file
Binary file not shown.
1127
plugins/Windows/2024/dnacalib.py
Normal file
1127
plugins/Windows/2024/dnacalib.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/2024/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2024/embeddedRL4.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2025/MayaUERBFPlugin.mll
Normal file
BIN
plugins/Windows/2025/MayaUERBFPlugin.mll
Normal file
Binary file not shown.
BIN
plugins/Windows/2025/_py3dnacalib.pyd
Normal file
BIN
plugins/Windows/2025/_py3dnacalib.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/2025/dnacalib.dll
Normal file
BIN
plugins/Windows/2025/dnacalib.dll
Normal file
Binary file not shown.
633
plugins/Windows/2025/dnacalib.py
Normal file
633
plugins/Windows/2025/dnacalib.py
Normal file
@@ -0,0 +1,633 @@
|
||||
# This file was automatically generated by SWIG (http://www.swig.org).
|
||||
# Version 4.0.1
|
||||
#
|
||||
# Do not make changes to this file unless you know what you are doing--modify
|
||||
# the SWIG interface file instead.
|
||||
|
||||
from sys import version_info as _swig_python_version_info
|
||||
if _swig_python_version_info < (2, 7, 0):
|
||||
raise RuntimeError("Python 2.7 or later required")
|
||||
|
||||
# Import the low-level C/C++ module
|
||||
if __package__ or "." in __name__:
|
||||
from . import _py3dnacalib
|
||||
else:
|
||||
import _py3dnacalib
|
||||
|
||||
try:
|
||||
import builtins as __builtin__
|
||||
except ImportError:
|
||||
import __builtin__
|
||||
|
||||
def _swig_repr(self):
|
||||
try:
|
||||
strthis = "proxy of " + self.this.__repr__()
|
||||
except __builtin__.Exception:
|
||||
strthis = ""
|
||||
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
|
||||
|
||||
|
||||
def _swig_setattr_nondynamic_instance_variable(set):
|
||||
def set_instance_attr(self, name, value):
|
||||
if name == "thisown":
|
||||
self.this.own(value)
|
||||
elif name == "this":
|
||||
set(self, name, value)
|
||||
elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
|
||||
set(self, name, value)
|
||||
else:
|
||||
raise AttributeError("You cannot add instance attributes to %s" % self)
|
||||
return set_instance_attr
|
||||
|
||||
|
||||
def _swig_setattr_nondynamic_class_variable(set):
|
||||
def set_class_attr(cls, name, value):
|
||||
if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
|
||||
set(cls, name, value)
|
||||
else:
|
||||
raise AttributeError("You cannot add class attributes to %s" % cls)
|
||||
return set_class_attr
|
||||
|
||||
|
||||
def _swig_add_metaclass(metaclass):
|
||||
"""Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
|
||||
def wrapper(cls):
|
||||
return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
|
||||
return wrapper
|
||||
|
||||
|
||||
class _SwigNonDynamicMeta(type):
|
||||
"""Meta class to enforce nondynamic attributes (no new attributes) for a class"""
|
||||
__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)
|
||||
|
||||
|
||||
|
||||
def __new_decorator(factory_func, original_new):
|
||||
@staticmethod
|
||||
def __new(cls, *args, **kwargs):
|
||||
# FIXME: while this workaround solves the immediate issue with the set of classes we currently have,
|
||||
# it will fail for classes that use a factory function but need no parameters at all, in which case
|
||||
# the factory function will never be invoked, only the original __new__ function.
|
||||
if args or kwargs:
|
||||
return factory_func(*args, **kwargs)
|
||||
return original_new(cls)
|
||||
return __new
|
||||
|
||||
def __managed_init(self, *args, **kwargs):
|
||||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
|
||||
import dna
|
||||
class VersionInfo(object):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
|
||||
@staticmethod
|
||||
def getMajorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMajorVersion()
|
||||
|
||||
@staticmethod
|
||||
def getMinorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMinorVersion()
|
||||
|
||||
@staticmethod
|
||||
def getPatchVersion():
|
||||
return _py3dnacalib.VersionInfo_getPatchVersion()
|
||||
|
||||
@staticmethod
|
||||
def getVersionString():
|
||||
return _py3dnacalib.VersionInfo_getVersionString()
|
||||
|
||||
def __init__(self):
|
||||
_py3dnacalib.VersionInfo_swiginit(self, _py3dnacalib.new_VersionInfo())
|
||||
__swig_destroy__ = _py3dnacalib.delete_VersionInfo
|
||||
|
||||
# Register VersionInfo in _py3dnacalib:
|
||||
_py3dnacalib.VersionInfo_swigregister(VersionInfo)
|
||||
|
||||
def VersionInfo_getMajorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMajorVersion()
|
||||
|
||||
def VersionInfo_getMinorVersion():
|
||||
return _py3dnacalib.VersionInfo_getMinorVersion()
|
||||
|
||||
def VersionInfo_getPatchVersion():
|
||||
return _py3dnacalib.VersionInfo_getPatchVersion()
|
||||
|
||||
def VersionInfo_getVersionString():
|
||||
return _py3dnacalib.VersionInfo_getVersionString()
|
||||
|
||||
class DNACalibDNAReader(dna.Reader):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise AttributeError("No constructor defined - class is abstract")
|
||||
__repr__ = _swig_repr
|
||||
|
||||
@staticmethod
|
||||
def create(*args):
|
||||
return _py3dnacalib.DNACalibDNAReader_create(*args)
|
||||
|
||||
@staticmethod
|
||||
def destroy(instance):
|
||||
return _py3dnacalib.DNACalibDNAReader_destroy(instance)
|
||||
|
||||
# Register DNACalibDNAReader in _py3dnacalib:
|
||||
_py3dnacalib.DNACalibDNAReader_swigregister(DNACalibDNAReader)
|
||||
|
||||
def DNACalibDNAReader_create(*args):
|
||||
return _py3dnacalib.DNACalibDNAReader_create(*args)
|
||||
|
||||
def DNACalibDNAReader_destroy(instance):
|
||||
return _py3dnacalib.DNACalibDNAReader_destroy(instance)
|
||||
|
||||
|
||||
DNACalibDNAReader.__new__ = __new_decorator(DNACalibDNAReader_create, DNACalibDNAReader.__new__)
|
||||
DNACalibDNAReader.__del__ = lambda instance: DNACalibDNAReader_destroy(instance)
|
||||
DNACalibDNAReader.__init__ = __managed_init
|
||||
del DNACalibDNAReader.create
|
||||
del DNACalibDNAReader.destroy
|
||||
|
||||
class Command(object):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise AttributeError("No constructor defined - class is abstract")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_Command
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.Command_run(self, output)
|
||||
|
||||
# Register Command in _py3dnacalib:
|
||||
_py3dnacalib.Command_swigregister(Command)
|
||||
|
||||
VectorOperation_Interpolate = _py3dnacalib.VectorOperation_Interpolate
|
||||
VectorOperation_Add = _py3dnacalib.VectorOperation_Add
|
||||
VectorOperation_Subtract = _py3dnacalib.VectorOperation_Subtract
|
||||
VectorOperation_Multiply = _py3dnacalib.VectorOperation_Multiply
|
||||
class CommandSequence(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_CommandSequence
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.CommandSequence_swiginit(self, _py3dnacalib.new_CommandSequence(*args))
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.CommandSequence_run(self, output)
|
||||
|
||||
def add(self, command):
|
||||
return _py3dnacalib.CommandSequence_add(self, command)
|
||||
|
||||
def remove(self, command):
|
||||
return _py3dnacalib.CommandSequence_remove(self, command)
|
||||
|
||||
def contains(self, command):
|
||||
return _py3dnacalib.CommandSequence_contains(self, command)
|
||||
|
||||
def size(self):
|
||||
return _py3dnacalib.CommandSequence_size(self)
|
||||
|
||||
# Register CommandSequence in _py3dnacalib:
|
||||
_py3dnacalib.CommandSequence_swigregister(CommandSequence)
|
||||
|
||||
|
||||
def command_sequence_init(_init):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
self._commands = []
|
||||
_init(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def command_sequence_add(_add):
|
||||
def wrapper(self, command):
|
||||
self._commands.append(command)
|
||||
_add(self, command)
|
||||
return wrapper
|
||||
|
||||
def command_sequence_remove(_remove):
|
||||
def wrapper(self, command):
|
||||
self._commands.remove(command)
|
||||
_remove(self, command)
|
||||
return wrapper
|
||||
|
||||
CommandSequence.__init__ = command_sequence_init(CommandSequence.__init__)
|
||||
CommandSequence.add = command_sequence_add(CommandSequence.add)
|
||||
CommandSequence.remove = command_sequence_remove(CommandSequence.remove)
|
||||
|
||||
class CalculateMeshLowerLODsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_CalculateMeshLowerLODsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.CalculateMeshLowerLODsCommand_swiginit(self, _py3dnacalib.new_CalculateMeshLowerLODsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.CalculateMeshLowerLODsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.CalculateMeshLowerLODsCommand_run(self, output)
|
||||
|
||||
# Register CalculateMeshLowerLODsCommand in _py3dnacalib:
|
||||
_py3dnacalib.CalculateMeshLowerLODsCommand_swigregister(CalculateMeshLowerLODsCommand)
|
||||
|
||||
class ClearBlendShapesCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_ClearBlendShapesCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.ClearBlendShapesCommand_swiginit(self, _py3dnacalib.new_ClearBlendShapesCommand(*args))
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.ClearBlendShapesCommand_run(self, output)
|
||||
|
||||
# Register ClearBlendShapesCommand in _py3dnacalib:
|
||||
_py3dnacalib.ClearBlendShapesCommand_swigregister(ClearBlendShapesCommand)
|
||||
|
||||
class PruneBlendShapeTargetsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_PruneBlendShapeTargetsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.PruneBlendShapeTargetsCommand_swiginit(self, _py3dnacalib.new_PruneBlendShapeTargetsCommand(*args))
|
||||
|
||||
def setThreshold(self, threshold):
|
||||
return _py3dnacalib.PruneBlendShapeTargetsCommand_setThreshold(self, threshold)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.PruneBlendShapeTargetsCommand_run(self, output)
|
||||
|
||||
# Register PruneBlendShapeTargetsCommand in _py3dnacalib:
|
||||
_py3dnacalib.PruneBlendShapeTargetsCommand_swigregister(PruneBlendShapeTargetsCommand)
|
||||
|
||||
class RemoveAnimatedMapCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveAnimatedMapCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveAnimatedMapCommand_swiginit(self, _py3dnacalib.new_RemoveAnimatedMapCommand(*args))
|
||||
|
||||
def setAnimatedMapIndex(self, animatedMapIndex):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_setAnimatedMapIndex(self, animatedMapIndex)
|
||||
|
||||
def setAnimatedMapIndices(self, animatedMapIndices):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_setAnimatedMapIndices(self, animatedMapIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveAnimatedMapCommand_run(self, output)
|
||||
|
||||
# Register RemoveAnimatedMapCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveAnimatedMapCommand_swigregister(RemoveAnimatedMapCommand)
|
||||
|
||||
class RemoveBlendShapeCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveBlendShapeCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveBlendShapeCommand_swiginit(self, _py3dnacalib.new_RemoveBlendShapeCommand(*args))
|
||||
|
||||
def setBlendShapeIndex(self, blendShapeIndex):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_setBlendShapeIndex(self, blendShapeIndex)
|
||||
|
||||
def setBlendShapeIndices(self, blendShapeIndices):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_setBlendShapeIndices(self, blendShapeIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveBlendShapeCommand_run(self, output)
|
||||
|
||||
# Register RemoveBlendShapeCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveBlendShapeCommand_swigregister(RemoveBlendShapeCommand)
|
||||
|
||||
class RemoveJointAnimationCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveJointAnimationCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveJointAnimationCommand_swiginit(self, _py3dnacalib.new_RemoveJointAnimationCommand(*args))
|
||||
|
||||
def setJointIndex(self, jointIndex):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_setJointIndex(self, jointIndex)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveJointAnimationCommand_run(self, output)
|
||||
|
||||
# Register RemoveJointAnimationCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveJointAnimationCommand_swigregister(RemoveJointAnimationCommand)
|
||||
|
||||
class RemoveJointCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveJointCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveJointCommand_swiginit(self, _py3dnacalib.new_RemoveJointCommand(*args))
|
||||
|
||||
def setJointIndex(self, jointIndex):
|
||||
return _py3dnacalib.RemoveJointCommand_setJointIndex(self, jointIndex)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.RemoveJointCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveJointCommand_run(self, output)
|
||||
|
||||
# Register RemoveJointCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveJointCommand_swigregister(RemoveJointCommand)
|
||||
|
||||
class RemoveMeshCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RemoveMeshCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RemoveMeshCommand_swiginit(self, _py3dnacalib.new_RemoveMeshCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.RemoveMeshCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setMeshIndices(self, meshIndices):
|
||||
return _py3dnacalib.RemoveMeshCommand_setMeshIndices(self, meshIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RemoveMeshCommand_run(self, output)
|
||||
|
||||
# Register RemoveMeshCommand in _py3dnacalib:
|
||||
_py3dnacalib.RemoveMeshCommand_swigregister(RemoveMeshCommand)
|
||||
|
||||
class RenameAnimatedMapCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameAnimatedMapCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameAnimatedMapCommand_swiginit(self, _py3dnacalib.new_RenameAnimatedMapCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameAnimatedMapCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameAnimatedMapCommand_run(self, output)
|
||||
|
||||
# Register RenameAnimatedMapCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameAnimatedMapCommand_swigregister(RenameAnimatedMapCommand)
|
||||
|
||||
class RenameBlendShapeCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameBlendShapeCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameBlendShapeCommand_swiginit(self, _py3dnacalib.new_RenameBlendShapeCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameBlendShapeCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameBlendShapeCommand_run(self, output)
|
||||
|
||||
# Register RenameBlendShapeCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameBlendShapeCommand_swigregister(RenameBlendShapeCommand)
|
||||
|
||||
class RenameJointCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameJointCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameJointCommand_swiginit(self, _py3dnacalib.new_RenameJointCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameJointCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameJointCommand_run(self, output)
|
||||
|
||||
# Register RenameJointCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameJointCommand_swigregister(RenameJointCommand)
|
||||
|
||||
class RenameMeshCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RenameMeshCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RenameMeshCommand_swiginit(self, _py3dnacalib.new_RenameMeshCommand(*args))
|
||||
|
||||
def setName(self, *args):
|
||||
return _py3dnacalib.RenameMeshCommand_setName(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RenameMeshCommand_run(self, output)
|
||||
|
||||
# Register RenameMeshCommand in _py3dnacalib:
|
||||
_py3dnacalib.RenameMeshCommand_swigregister(RenameMeshCommand)
|
||||
|
||||
class RotateCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_RotateCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.RotateCommand_swiginit(self, _py3dnacalib.new_RotateCommand(*args))
|
||||
|
||||
def setRotation(self, degrees):
|
||||
return _py3dnacalib.RotateCommand_setRotation(self, degrees)
|
||||
|
||||
def setOrigin(self, origin):
|
||||
return _py3dnacalib.RotateCommand_setOrigin(self, origin)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.RotateCommand_run(self, output)
|
||||
|
||||
# Register RotateCommand in _py3dnacalib:
|
||||
_py3dnacalib.RotateCommand_swigregister(RotateCommand)
|
||||
|
||||
class ScaleCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_ScaleCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.ScaleCommand_swiginit(self, _py3dnacalib.new_ScaleCommand(*args))
|
||||
|
||||
def setScale(self, scale):
|
||||
return _py3dnacalib.ScaleCommand_setScale(self, scale)
|
||||
|
||||
def setOrigin(self, origin):
|
||||
return _py3dnacalib.ScaleCommand_setOrigin(self, origin)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.ScaleCommand_run(self, output)
|
||||
|
||||
# Register ScaleCommand in _py3dnacalib:
|
||||
_py3dnacalib.ScaleCommand_swigregister(ScaleCommand)
|
||||
|
||||
class SetBlendShapeTargetDeltasCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetBlendShapeTargetDeltasCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetBlendShapeTargetDeltasCommand_swiginit(self, _py3dnacalib.new_SetBlendShapeTargetDeltasCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setBlendShapeTargetIndex(self, blendShapeTargetIndex):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setBlendShapeTargetIndex(self, blendShapeTargetIndex)
|
||||
|
||||
def setDeltas(self, *args):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setDeltas(self, *args)
|
||||
|
||||
def setVertexIndices(self, vertexIndices):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setVertexIndices(self, vertexIndices)
|
||||
|
||||
def setMasks(self, masks):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setMasks(self, masks)
|
||||
|
||||
def setOperation(self, operation):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_setOperation(self, operation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetBlendShapeTargetDeltasCommand_run(self, output)
|
||||
|
||||
# Register SetBlendShapeTargetDeltasCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetBlendShapeTargetDeltasCommand_swigregister(SetBlendShapeTargetDeltasCommand)
|
||||
cvar = _py3dnacalib.cvar
|
||||
SetBlendShapeTargetDeltasCommand.VertexIndicesOutOfBoundsError = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_VertexIndicesOutOfBoundsError
|
||||
SetBlendShapeTargetDeltasCommand.NoVertexIndicesSetError = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_NoVertexIndicesSetError
|
||||
SetBlendShapeTargetDeltasCommand.DeltasVertexIndicesCountMismatch = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_DeltasVertexIndicesCountMismatch
|
||||
SetBlendShapeTargetDeltasCommand.DeltasMasksCountMismatch = _py3dnacalib.cvar.SetBlendShapeTargetDeltasCommand_DeltasMasksCountMismatch
|
||||
|
||||
class SetLODsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetLODsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetLODsCommand_swiginit(self, _py3dnacalib.new_SetLODsCommand(*args))
|
||||
|
||||
def setLODs(self, lods):
|
||||
return _py3dnacalib.SetLODsCommand_setLODs(self, lods)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetLODsCommand_run(self, output)
|
||||
|
||||
# Register SetLODsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetLODsCommand_swigregister(SetLODsCommand)
|
||||
|
||||
class SetNeutralJointRotationsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetNeutralJointRotationsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetNeutralJointRotationsCommand_swiginit(self, _py3dnacalib.new_SetNeutralJointRotationsCommand(*args))
|
||||
|
||||
def setRotations(self, *args):
|
||||
return _py3dnacalib.SetNeutralJointRotationsCommand_setRotations(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetNeutralJointRotationsCommand_run(self, output)
|
||||
|
||||
# Register SetNeutralJointRotationsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetNeutralJointRotationsCommand_swigregister(SetNeutralJointRotationsCommand)
|
||||
|
||||
class SetNeutralJointTranslationsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetNeutralJointTranslationsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetNeutralJointTranslationsCommand_swiginit(self, _py3dnacalib.new_SetNeutralJointTranslationsCommand(*args))
|
||||
|
||||
def setTranslations(self, *args):
|
||||
return _py3dnacalib.SetNeutralJointTranslationsCommand_setTranslations(self, *args)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetNeutralJointTranslationsCommand_run(self, output)
|
||||
|
||||
# Register SetNeutralJointTranslationsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetNeutralJointTranslationsCommand_swigregister(SetNeutralJointTranslationsCommand)
|
||||
|
||||
class SetSkinWeightsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetSkinWeightsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetSkinWeightsCommand_swiginit(self, _py3dnacalib.new_SetSkinWeightsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setVertexIndex(self, vertexIndex):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setVertexIndex(self, vertexIndex)
|
||||
|
||||
def setWeights(self, weights):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setWeights(self, weights)
|
||||
|
||||
def setJointIndices(self, jointIndices):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_setJointIndices(self, jointIndices)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetSkinWeightsCommand_run(self, output)
|
||||
|
||||
# Register SetSkinWeightsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetSkinWeightsCommand_swigregister(SetSkinWeightsCommand)
|
||||
|
||||
class SetVertexPositionsCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_SetVertexPositionsCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.SetVertexPositionsCommand_swiginit(self, _py3dnacalib.new_SetVertexPositionsCommand(*args))
|
||||
|
||||
def setMeshIndex(self, meshIndex):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setMeshIndex(self, meshIndex)
|
||||
|
||||
def setPositions(self, *args):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setPositions(self, *args)
|
||||
|
||||
def setMasks(self, masks):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setMasks(self, masks)
|
||||
|
||||
def setOperation(self, operation):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_setOperation(self, operation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.SetVertexPositionsCommand_run(self, output)
|
||||
|
||||
# Register SetVertexPositionsCommand in _py3dnacalib:
|
||||
_py3dnacalib.SetVertexPositionsCommand_swigregister(SetVertexPositionsCommand)
|
||||
SetVertexPositionsCommand.PositionsMasksCountMismatch = _py3dnacalib.cvar.SetVertexPositionsCommand_PositionsMasksCountMismatch
|
||||
|
||||
class TranslateCommand(Command):
|
||||
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
|
||||
__repr__ = _swig_repr
|
||||
__swig_destroy__ = _py3dnacalib.delete_TranslateCommand
|
||||
|
||||
def __init__(self, *args):
|
||||
_py3dnacalib.TranslateCommand_swiginit(self, _py3dnacalib.new_TranslateCommand(*args))
|
||||
|
||||
def setTranslation(self, translation):
|
||||
return _py3dnacalib.TranslateCommand_setTranslation(self, translation)
|
||||
|
||||
def run(self, output):
|
||||
return _py3dnacalib.TranslateCommand_run(self, output)
|
||||
|
||||
# Register TranslateCommand in _py3dnacalib:
|
||||
_py3dnacalib.TranslateCommand_swigregister(TranslateCommand)
|
||||
|
||||
|
||||
|
BIN
plugins/Windows/2025/embeddedRL4.mll
Normal file
BIN
plugins/Windows/2025/embeddedRL4.mll
Normal file
Binary file not shown.
0
plugins/Windows/pydna/__init__.py
Normal file
0
plugins/Windows/pydna/__init__.py
Normal file
BIN
plugins/Windows/pydna/python3/_py3dna.pyd
Normal file
BIN
plugins/Windows/pydna/python3/_py3dna.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python3/dna.dll
Normal file
BIN
plugins/Windows/pydna/python3/dna.dll
Normal file
Binary file not shown.
1432
plugins/Windows/pydna/python3/dna.py
Normal file
1432
plugins/Windows/pydna/python3/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/pydna/python3108/_py3dna.pyd
Normal file
BIN
plugins/Windows/pydna/python3108/_py3dna.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python3108/dna.dll
Normal file
BIN
plugins/Windows/pydna/python3108/dna.dll
Normal file
Binary file not shown.
1403
plugins/Windows/pydna/python3108/dna.py
Normal file
1403
plugins/Windows/pydna/python3108/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/pydna/python311/_py3dna.pyd
Normal file
BIN
plugins/Windows/pydna/python311/_py3dna.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python311/_py3dna9_4_3.pyd
Normal file
BIN
plugins/Windows/pydna/python311/_py3dna9_4_3.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python311/dna.dll
Normal file
BIN
plugins/Windows/pydna/python311/dna.dll
Normal file
Binary file not shown.
1403
plugins/Windows/pydna/python311/dna.py
Normal file
1403
plugins/Windows/pydna/python311/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/Windows/pydna/python311/dna9_4_3.dll
Normal file
BIN
plugins/Windows/pydna/python311/dna9_4_3.dll
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python311/polyalloc1_3_12.dll
Normal file
BIN
plugins/Windows/pydna/python311/polyalloc1_3_12.dll
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python311/statuscode1_2_6.dll
Normal file
BIN
plugins/Windows/pydna/python311/statuscode1_2_6.dll
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python311/trio4_0_16.dll
Normal file
BIN
plugins/Windows/pydna/python311/trio4_0_16.dll
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python397/_py3dna.pyd
Normal file
BIN
plugins/Windows/pydna/python397/_py3dna.pyd
Normal file
Binary file not shown.
BIN
plugins/Windows/pydna/python397/dna.dll
Normal file
BIN
plugins/Windows/pydna/python397/dna.dll
Normal file
Binary file not shown.
1432
plugins/Windows/pydna/python397/dna.py
Normal file
1432
plugins/Windows/pydna/python397/dna.py
Normal file
File diff suppressed because it is too large
Load Diff
284
scripts/Main.py
Normal file
284
scripts/Main.py
Normal file
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Main module for Metahuman customize plugin
|
||||
主模块 - 负责初始化UI和功能集成
|
||||
功能: 从ui模块加载子模块显示
|
||||
作者: Virtuos Games
|
||||
版本: Alpha v1.0.0
|
||||
"""
|
||||
#===================================== IMPORT MODULES =====================================
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from Qt.QtCompat import wrapInstance
|
||||
from maya import OpenMayaUI as omui
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.utils as utils
|
||||
import webbrowser
|
||||
import subprocess
|
||||
import importlib
|
||||
import traceback
|
||||
import locale
|
||||
import sys
|
||||
import os
|
||||
#===================================== IMPORT UI MODULES ===================================
|
||||
from scripts.ui import toolbar
|
||||
from scripts.ui import geometry
|
||||
from scripts.ui import rigging
|
||||
from scripts.ui import behaviour
|
||||
from scripts.ui import definition
|
||||
#========================================== CONFIG ========================================
|
||||
import config
|
||||
TOOL_NAME = config.TOOL_NAME
|
||||
TOOL_VERSION = config.TOOL_VERSION
|
||||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||||
TOOL_YEAR = config.TOOL_VERSION
|
||||
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
|
||||
TOOL_LANG = config.TOOL_LANG
|
||||
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
|
||||
TOOL_HELP_URL = config.TOOL_HELP_URL
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||||
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
|
||||
UI_PATH = config.UI_PATH
|
||||
STYLE_FILE = config.STYLE_FILE
|
||||
ICONS_PATH = config.ICONS_PATH
|
||||
TOOL_ICON = config.TOOL_ICON
|
||||
ASSETS_PATH = config.ASSETS_PATH
|
||||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||||
DNA_IMG_PATH = config.DNA_IMG_PATH
|
||||
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
|
||||
#====================================== LOCALIZATION ==================================
|
||||
from scripts.ui import localization
|
||||
LANG = localization.LANG
|
||||
#========================================= INIT =======================================
|
||||
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)
|
||||
#===================================== MAIN FUNCTION ===================================
|
||||
class MainWindow(QtWidgets.QWidget):
|
||||
def __init__(self, parent=maya_main_window()):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
self.setWindowTitle(TOOL_NAME)
|
||||
self.setObjectName(f"{TOOL_NAME}MainWindow")
|
||||
self.setWindowFlags(QtCore.Qt.Window)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
|
||||
# 设置自适应大小策略
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
self.setMinimumSize(300, 600) # 减小最小高度,让窗口更灵活
|
||||
|
||||
self.create_widgets()
|
||||
self.create_layouts()
|
||||
self.create_connections()
|
||||
if os.path.exists(TOOL_ICON):
|
||||
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
else:
|
||||
print("WARNING: Icon file not found: {}".format(TOOL_ICON))
|
||||
|
||||
def dock_to_maya(self):
|
||||
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
|
||||
cmds.deleteUI(TOOL_WSCL_NAME)
|
||||
|
||||
def create_control():
|
||||
try:
|
||||
workspace_control = cmds.workspaceControl(
|
||||
TOOL_WSCL_NAME,
|
||||
label=TOOL_NAME,
|
||||
floating=True,
|
||||
retain=True,
|
||||
resizeWidth=True,
|
||||
initialWidth=300,
|
||||
minimumWidth=300
|
||||
)
|
||||
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("Error creating workspace control: {}".format(e))
|
||||
|
||||
cmds.evalDeferred(create_control)
|
||||
#===================================== UI COMPONENTS =====================================
|
||||
def create_widgets(self):
|
||||
# 创建滚动区域
|
||||
self.scroll_area = QtWidgets.QScrollArea()
|
||||
self.scroll_area.setObjectName("main_scroll_area")
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) # 改回按需显示
|
||||
self.scroll_area.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
# 创建滚动区域内容控件
|
||||
self.scroll_content = QtWidgets.QWidget()
|
||||
self.scroll_content.setObjectName("scroll_content")
|
||||
self.scroll_content.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
# 创建主标签页
|
||||
self.main_tab = QtWidgets.QTabWidget()
|
||||
self.main_tab.setObjectName("main_tab")
|
||||
self.main_tab.setTabPosition(QtWidgets.QTabWidget.North)
|
||||
self.main_tab.setTabShape(QtWidgets.QTabWidget.Rounded)
|
||||
self.main_tab.setDocumentMode(True)
|
||||
self.main_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
self.main_tab.setMinimumHeight(400)
|
||||
|
||||
# 创建各功能模块标签页
|
||||
self.geometry_tab = QtWidgets.QWidget()
|
||||
self.geometry_tab.setObjectName("geometry_tab")
|
||||
self.geometry_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
self.rigging_tab = QtWidgets.QWidget()
|
||||
self.rigging_tab.setObjectName("rigging_tab")
|
||||
self.rigging_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
self.behaviour_tab = QtWidgets.QWidget()
|
||||
self.behaviour_tab.setObjectName("behaviour_tab")
|
||||
self.behaviour_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
self.definition_tab = QtWidgets.QWidget()
|
||||
self.definition_tab.setObjectName("definition_tab")
|
||||
self.definition_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
# 创建工具栏
|
||||
self.toolbar_frame = QtWidgets.QFrame()
|
||||
self.toolbar_frame.setObjectName("toolbar_frame")
|
||||
self.toolbar_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.toolbar_frame.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.toolbar_frame.setMaximumHeight(40)
|
||||
self.toolbar_frame.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
# 创建状态栏
|
||||
self.status_bar = QtWidgets.QStatusBar()
|
||||
self.status_bar.setObjectName("status_bar")
|
||||
self.status_bar.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
self.status_bar.showMessage(f"{TOOL_NAME} {TOOL_VERSION}")
|
||||
|
||||
# 初始化各模块UI组件
|
||||
toolbar.widgets()
|
||||
geometry.widgets()
|
||||
rigging.widgets()
|
||||
behaviour.widgets()
|
||||
definition.widgets()
|
||||
|
||||
def create_layouts(self):
|
||||
# 主布局
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(2, 2, 2, 2)
|
||||
self.main_layout.setSpacing(2)
|
||||
|
||||
# 滚动区域内容布局
|
||||
self.scroll_content_layout = QtWidgets.QVBoxLayout(self.scroll_content)
|
||||
self.scroll_content_layout.setContentsMargins(2, 2, 2, 2)
|
||||
self.scroll_content_layout.setSpacing(2)
|
||||
|
||||
# 添加工具栏
|
||||
self.toolbar_layout = QtWidgets.QVBoxLayout(self.toolbar_frame)
|
||||
self.toolbar_layout.setContentsMargins(0, 0, 0, 0)
|
||||
toolbar.layouts(parent_frame=self.toolbar_frame)
|
||||
|
||||
# 设置各标签页布局
|
||||
self.geometry_layout = QtWidgets.QVBoxLayout(self.geometry_tab)
|
||||
self.geometry_layout.setContentsMargins(4, 4, 4, 4)
|
||||
geometry.layouts(parent_tab=self.geometry_tab)
|
||||
|
||||
self.rigging_layout = QtWidgets.QVBoxLayout(self.rigging_tab)
|
||||
self.rigging_layout.setContentsMargins(4, 4, 4, 4)
|
||||
rigging.layouts(parent_tab=self.rigging_tab)
|
||||
|
||||
self.behaviour_layout = QtWidgets.QVBoxLayout(self.behaviour_tab)
|
||||
self.behaviour_layout.setContentsMargins(4, 4, 4, 4)
|
||||
behaviour.layouts(parent_tab=self.behaviour_tab)
|
||||
|
||||
self.definition_layout = QtWidgets.QVBoxLayout(self.definition_tab)
|
||||
self.definition_layout.setContentsMargins(4, 4, 4, 4)
|
||||
definition.layouts(parent_tab=self.definition_tab)
|
||||
|
||||
# 添加标签页到主标签控件
|
||||
self.main_tab.addTab(self.geometry_tab, "几何模型")
|
||||
self.main_tab.addTab(self.rigging_tab, "绑定系统")
|
||||
self.main_tab.addTab(self.behaviour_tab, "行为系统")
|
||||
self.main_tab.addTab(self.definition_tab, "定义系统")
|
||||
|
||||
# 将组件添加到滚动区域内容布局
|
||||
self.scroll_content_layout.addWidget(self.toolbar_frame)
|
||||
self.scroll_content_layout.addWidget(self.main_tab)
|
||||
|
||||
# 设置滚动区域的内容控件
|
||||
self.scroll_area.setWidget(self.scroll_content)
|
||||
|
||||
# 将滚动区域和状态栏添加到主布局
|
||||
self.main_layout.addWidget(self.scroll_area)
|
||||
self.main_layout.addWidget(self.status_bar)
|
||||
|
||||
# 加载样式表
|
||||
if os.path.exists(STYLE_FILE):
|
||||
try:
|
||||
with open(STYLE_FILE, "r", encoding="utf-8") as f:
|
||||
style = f.read()
|
||||
self.setStyleSheet(style)
|
||||
except UnicodeDecodeError:
|
||||
# 尝试使用系统默认编码
|
||||
encoding = get_system_encoding()
|
||||
try:
|
||||
with open(STYLE_FILE, "r", encoding=encoding) as f:
|
||||
style = f.read()
|
||||
self.setStyleSheet(style)
|
||||
except Exception as e:
|
||||
print(f"警告: 无法加载样式表文件: {e}")
|
||||
else:
|
||||
print(f"警告: 样式表文件不存在: {STYLE_FILE}")
|
||||
|
||||
def create_connections(self):
|
||||
# 连接各模块的信号和槽
|
||||
toolbar.connections()
|
||||
geometry.connections()
|
||||
rigging.connections()
|
||||
behaviour.connections()
|
||||
definition.connections()
|
||||
|
||||
# 标签页切换信号
|
||||
self.main_tab.currentChanged.connect(self.on_tab_changed)
|
||||
|
||||
def on_tab_changed(self, index):
|
||||
tab_name = self.main_tab.tabText(index)
|
||||
self.status_bar.showMessage(f"当前模块: {tab_name}")
|
||||
print(f"切换到模块: {tab_name}")
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to initialize and show the Plugin UI
|
||||
"""
|
||||
try:
|
||||
# Initialize UI modules with placeholder functions
|
||||
# Each module only contains UI framework without actual functionality
|
||||
toolbar.toolbar_temp_function()
|
||||
geometry.geometry_temp_function()
|
||||
rigging.rigging_temp_function()
|
||||
behaviour.behaviour_temp_function()
|
||||
definition.definition_temp_function()
|
||||
|
||||
# Create and show main window
|
||||
global tool_window
|
||||
tool_window = MainWindow()
|
||||
tool_window.dock_to_maya()
|
||||
|
||||
print(f"{TOOL_NAME} plugin initialized successfully!")
|
||||
return tool_window
|
||||
except Exception as e:
|
||||
error_msg = f"Error initializing {TOOL_NAME} plugin: {str(e)}"
|
||||
print(error_msg)
|
||||
traceback.print_exc()
|
||||
cmds.warning(error_msg)
|
||||
return None
|
||||
|
||||
|
||||
# Auto-run when imported
|
||||
if __name__ == "__main__":
|
||||
main()
|
741
scripts/ReloadModules.py
Normal file
741
scripts/ReloadModules.py
Normal file
@@ -0,0 +1,741 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Tool - Module Reload Tool
|
||||
Provides module reloading functionality for the plugin, supporting hot updates
|
||||
|
||||
用法说明:
|
||||
1. 在Maya中直接运行:
|
||||
import scripts.ReloadModules
|
||||
scripts.ReloadModules.main()
|
||||
|
||||
2. 从命令行重新加载所有模块:
|
||||
import scripts.ReloadModules
|
||||
scripts.ReloadModules.reload_all()
|
||||
|
||||
3. 重新加载特定模块:
|
||||
import scripts.ReloadModules
|
||||
scripts.ReloadModules.ModuleReloader.reload_module('module_name')
|
||||
|
||||
4. 在开发过程中使用:
|
||||
- 当修改了代码后,运行此模块可以热更新所有更改
|
||||
- 无需重启Maya即可测试新功能
|
||||
- 支持UI和命令行两种方式
|
||||
|
||||
注意事项:
|
||||
- 某些深度依赖的模块可能需要手动重启Maya
|
||||
- 如果遇到导入错误,请检查模块路径是否正确
|
||||
- 重新加载可能不会影响已经创建的对象实例
|
||||
"""
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
import os
|
||||
import traceback
|
||||
import shutil
|
||||
from maya import cmds
|
||||
|
||||
# 设置工具路径
|
||||
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
|
||||
# 设置脚本路径
|
||||
SCRIPTS_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
|
||||
# 导入主配置 - 多级尝试导入策略
|
||||
def import_main_config():
|
||||
"""尝试多种方式导入主配置文件"""
|
||||
global TOOL_NAME, TOOL_VERSION, TOOL_AUTHOR, TOOL_PATH, SCRIPTS_PATH
|
||||
global UI_PATH, STYLE_FILE, ICONS_PATH, ASSETS_PATH, DNA_FILE_PATH, Config
|
||||
|
||||
# 尝试直接导入
|
||||
try:
|
||||
import config
|
||||
importlib.reload(config) # 确保获取最新配置
|
||||
# 从主配置文件导入路径和设置
|
||||
TOOL_NAME = config.TOOL_NAME
|
||||
TOOL_VERSION = config.TOOL_VERSION
|
||||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||||
UI_PATH = config.UI_PATH
|
||||
STYLE_FILE = config.STYLE_FILE
|
||||
ICONS_PATH = config.ICONS_PATH
|
||||
ASSETS_PATH = config.ASSETS_PATH
|
||||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||||
|
||||
print(f"成功从主配置文件导入配置")
|
||||
return True
|
||||
except ImportError as e:
|
||||
print(f"直接导入主配置文件失败: {str(e)}")
|
||||
|
||||
# 尝试从上级目录导入
|
||||
parent_dir = os.path.dirname(TOOL_PATH)
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
try:
|
||||
import config
|
||||
importlib.reload(config) # 确保获取最新配置
|
||||
# 从主配置文件导入路径和设置
|
||||
TOOL_NAME = config.TOOL_NAME
|
||||
TOOL_VERSION = config.TOOL_VERSION
|
||||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||||
UI_PATH = config.UI_PATH
|
||||
STYLE_FILE = config.STYLE_FILE
|
||||
ICONS_PATH = config.ICONS_PATH
|
||||
ASSETS_PATH = config.ASSETS_PATH
|
||||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||||
|
||||
print(f"成功从上级目录导入主配置文件")
|
||||
return True
|
||||
except ImportError as e:
|
||||
print(f"从上级目录导入主配置文件失败: {str(e)}")
|
||||
if parent_dir in sys.path:
|
||||
sys.path.remove(parent_dir)
|
||||
|
||||
return False
|
||||
|
||||
# 尝试导入主配置
|
||||
if not import_main_config():
|
||||
print("警告: 无法导入主配置文件,使用默认配置")
|
||||
# 使用默认路径
|
||||
TOOL_NAME = "Delos"
|
||||
TOOL_VERSION = "Alpha v1.0.0"
|
||||
TOOL_AUTHOR = "Virtuos Games"
|
||||
TOOL_PATH = TOOL_PATH
|
||||
UI_PATH = os.path.join(SCRIPTS_PATH, 'ui')
|
||||
STYLE_FILE = os.path.join(SCRIPTS_PATH, 'ui', 'style.qss')
|
||||
ICONS_PATH = os.path.join(TOOL_PATH, 'icons')
|
||||
ASSETS_PATH = os.path.join(TOOL_PATH, 'assets')
|
||||
DNA_FILE_PATH = os.path.join(TOOL_PATH, 'assets', 'dna')
|
||||
|
||||
# 尝试导入Config模块
|
||||
try:
|
||||
from scripts.utils import Config
|
||||
except ImportError:
|
||||
try:
|
||||
from utils import Config
|
||||
except ImportError:
|
||||
try:
|
||||
import Config
|
||||
except ImportError:
|
||||
print("警告: 无法导入Config模块")
|
||||
Config = None
|
||||
|
||||
# 创建默认Config对象
|
||||
class DefaultConfig:
|
||||
# 使用TOOL_PATH而不是ROOT_DIR
|
||||
DNA_CONFIG = {
|
||||
'DNA_PATH': os.path.join(TOOL_PATH, 'assets', 'dna'),
|
||||
'DNA_FILE_PATH': os.path.join(TOOL_PATH, 'assets', 'dna', 'default.dna'),
|
||||
'DNA_VERSION': 'DNAv4',
|
||||
'LOD_LEVELS': [0, 1, 2, 3, 4],
|
||||
'DEFAULT_MESH_INDICES': [0],
|
||||
'GUI_PATH': os.path.join(TOOL_PATH, 'ui'),
|
||||
'ASSEMBLE_SCRIPT': os.path.join(TOOL_PATH, 'scripts', 'assemble.py')
|
||||
}
|
||||
DNA_LIB_PATHS = [
|
||||
os.path.join(TOOL_PATH, 'lib'),
|
||||
os.path.join(TOOL_PATH, 'lib', 'windows' if sys.platform == 'win32' else 'linux')
|
||||
]
|
||||
Config = DefaultConfig()
|
||||
|
||||
|
||||
# 尝试导入 Qt 模块
|
||||
# 首先尝试使用项目中的 Qt.py 兼容层
|
||||
try:
|
||||
# 添加父目录到路径中以确保可以导入 Qt.py
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.append(parent_dir)
|
||||
|
||||
# 尝试导入 Qt.py
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from Qt.QtCompat import wrapInstance
|
||||
|
||||
# 获取 Maya 主窗口
|
||||
def getMayaMainWindow():
|
||||
from maya import OpenMayaUI as omui
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(mainWindowPtr), QtWidgets.QWidget)
|
||||
|
||||
HAS_QT = True
|
||||
print("Successfully imported Qt modules using Qt.py compatibility layer")
|
||||
|
||||
except ImportError:
|
||||
# 如果无法导入 Qt.py,尝试直接导入 PySide2/PySide6
|
||||
try:
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
from shiboken2 import wrapInstance
|
||||
from maya import OpenMayaUI as omui
|
||||
|
||||
def getMayaMainWindow():
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(mainWindowPtr), QtWidgets.QWidget)
|
||||
|
||||
HAS_QT = True
|
||||
print("Successfully imported Qt modules using PySide2")
|
||||
|
||||
except ImportError:
|
||||
try:
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from shiboken6 import wrapInstance
|
||||
from maya import OpenMayaUI as omui
|
||||
|
||||
def getMayaMainWindow():
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(mainWindowPtr), QtWidgets.QWidget)
|
||||
|
||||
HAS_QT = True
|
||||
print("Successfully imported Qt modules using PySide6")
|
||||
|
||||
except ImportError:
|
||||
# 如果所有导入尝试都失败,设置标志并显示警告
|
||||
HAS_QT = False
|
||||
cmds.warning("Failed to import Qt modules. UI functionality will be limited to command line only.")
|
||||
|
||||
|
||||
def clean_pycache(root_dir):
|
||||
"""Delete all __pycache__ directories and .pyc files under the given root directory
|
||||
|
||||
Args:
|
||||
root_dir (str): Root directory to search for __pycache__ folders and .pyc files
|
||||
|
||||
Returns:
|
||||
int: Number of __pycache__ directories and .pyc files removed
|
||||
"""
|
||||
count = 0
|
||||
try:
|
||||
# 首先确保目录存在
|
||||
if not os.path.exists(root_dir) or not os.path.isdir(root_dir):
|
||||
print(f"Warning: Directory does not exist or is not a directory: {root_dir}")
|
||||
return count
|
||||
|
||||
print(f"Cleaning __pycache__ in directory: {root_dir}")
|
||||
|
||||
# 收集所有需要删除的路径,避免在遍历时修改目录结构
|
||||
pycache_dirs = []
|
||||
pyc_files = []
|
||||
|
||||
# 首先收集所有 __pycache__ 目录和 .pyc 文件
|
||||
for root, dirs, files in os.walk(root_dir):
|
||||
# 收集 __pycache__ 目录
|
||||
if "__pycache__" in dirs:
|
||||
pycache_path = os.path.join(root, "__pycache__")
|
||||
pycache_dirs.append(pycache_path)
|
||||
|
||||
# 收集 .pyc 文件
|
||||
for file in files:
|
||||
if file.endswith(".pyc"):
|
||||
file_path = os.path.join(root, file)
|
||||
pyc_files.append(file_path)
|
||||
|
||||
# 优先使用批处理命令删除 __pycache__ 目录(更可靠)
|
||||
if os.name == 'nt': # Windows
|
||||
# 创建临时批处理文件
|
||||
temp_bat = os.path.join(os.environ.get('TEMP', '.'), 'clean_pycache_temp.bat')
|
||||
with open(temp_bat, 'w') as f:
|
||||
f.write('@echo off\n')
|
||||
# 添加删除 __pycache__ 目录的命令
|
||||
for pycache_path in pycache_dirs:
|
||||
f.write(f'if exist "{pycache_path}" rd /s /q "{pycache_path}"\n')
|
||||
# 添加删除 .pyc 文件的命令
|
||||
for file_path in pyc_files:
|
||||
f.write(f'if exist "{file_path}" del /f /q "{file_path}"\n')
|
||||
|
||||
# 执行批处理文件
|
||||
os.system(f'"{temp_bat}"')
|
||||
|
||||
# 删除临时批处理文件
|
||||
try:
|
||||
os.remove(temp_bat)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 验证是否删除成功
|
||||
remaining_pycache = 0
|
||||
for pycache_path in pycache_dirs:
|
||||
if not os.path.exists(pycache_path):
|
||||
count += 1
|
||||
else:
|
||||
remaining_pycache += 1
|
||||
|
||||
remaining_pyc = 0
|
||||
for file_path in pyc_files:
|
||||
if not os.path.exists(file_path):
|
||||
count += 1
|
||||
else:
|
||||
remaining_pyc += 1
|
||||
|
||||
if remaining_pycache > 0 or remaining_pyc > 0:
|
||||
print(f"Warning: {remaining_pycache} __pycache__ directories and {remaining_pyc} .pyc files could not be removed")
|
||||
else: # Unix/Linux
|
||||
# 使用系统命令删除 __pycache__ 目录
|
||||
for pycache_path in pycache_dirs:
|
||||
if os.system(f'rm -rf "{pycache_path}"') == 0:
|
||||
print(f"Removed __pycache__ directory: {pycache_path}")
|
||||
count += 1
|
||||
else:
|
||||
print(f"Failed to remove {pycache_path}")
|
||||
|
||||
# 使用系统命令删除 .pyc 文件
|
||||
for file_path in pyc_files:
|
||||
if os.system(f'rm -f "{file_path}"') == 0:
|
||||
print(f"Removed .pyc file: {file_path}")
|
||||
count += 1
|
||||
else:
|
||||
print(f"Failed to remove {file_path}")
|
||||
|
||||
except Exception as error:
|
||||
print(f"Error cleaning __pycache__ directories: {str(error)}")
|
||||
print(f"Removed {count} files/directories: {len(pycache_dirs)} __pycache__ directories and {len(pyc_files)} .pyc files")
|
||||
traceback.print_exc()
|
||||
|
||||
return count
|
||||
|
||||
|
||||
class ModuleReloader(object):
|
||||
"""Class for reloading modules in the Plugin"""
|
||||
|
||||
@staticmethod
|
||||
def get_package_modules(package_name):
|
||||
"""
|
||||
Get all modules in a package
|
||||
|
||||
Args:
|
||||
package_name (str): Name of the package
|
||||
|
||||
Returns:
|
||||
list: List of module names
|
||||
"""
|
||||
package_modules = []
|
||||
try:
|
||||
# 尝试导入包
|
||||
package = importlib.import_module(package_name)
|
||||
package_path = os.path.dirname(package.__file__)
|
||||
|
||||
# 遍历包目录查找模块
|
||||
for root, dirs, files in os.walk(package_path):
|
||||
for file in files:
|
||||
if file.endswith('.py') and file != '__init__.py':
|
||||
# 计算相对路径
|
||||
rel_path = os.path.relpath(os.path.join(root, file), package_path)
|
||||
# 转换为模块路径格式
|
||||
module_path = os.path.splitext(rel_path)[0].replace(os.sep, '.')
|
||||
# 构建完整模块名
|
||||
module_name = f"{package_name}.{module_path}"
|
||||
package_modules.append(module_name)
|
||||
|
||||
# 添加子包
|
||||
for dir_name in dirs:
|
||||
init_file = os.path.join(root, dir_name, '__init__.py')
|
||||
if os.path.exists(init_file):
|
||||
rel_path = os.path.relpath(os.path.join(root, dir_name), package_path)
|
||||
module_name = f"{package_name}.{rel_path.replace(os.sep, '.')}"
|
||||
package_modules.append(module_name)
|
||||
|
||||
return package_modules
|
||||
except Exception as e:
|
||||
print(f"错误: 无法导入包 {package_name}: {str(e)}")
|
||||
traceback.print_exc()
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def reload_module(module_name):
|
||||
"""Reload a specific module
|
||||
|
||||
Args:
|
||||
module_name (str): Name of the module to reload
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# 尝试导入模块
|
||||
try:
|
||||
module = __import__(module_name, fromlist=["*"])
|
||||
except ImportError:
|
||||
# 如果失败,尝试将点替换为斜杠
|
||||
module_path = module_name.replace('.', '/')
|
||||
if os.path.exists(os.path.join(TOOL_PATH, module_path + '.py')):
|
||||
sys_path_modified = False
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
sys_path_modified = True
|
||||
|
||||
module = __import__(module_name, fromlist=["*"])
|
||||
|
||||
if sys_path_modified:
|
||||
sys.path.remove(TOOL_PATH)
|
||||
else:
|
||||
raise ImportError(f"Module {module_name} not found")
|
||||
|
||||
# 重新加载模块
|
||||
importlib.reload(module)
|
||||
|
||||
print(f"成功重新加载模块: {module_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"重新加载模块 {module_name} 时出错: {str(e)}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def reload_all_modules(cls):
|
||||
"""
|
||||
Reload all Plugin modules
|
||||
|
||||
Returns:
|
||||
dict: Results of reloading each module
|
||||
"""
|
||||
# 首先声明全局变量
|
||||
global TOOL_NAME, TOOL_VERSION, TOOL_AUTHOR, TOOL_PATH, SCRIPTS_PATH
|
||||
global UI_PATH, STYLE_FILE, ICONS_PATH, ASSETS_PATH, DNA_FILE_PATH
|
||||
|
||||
results = {}
|
||||
|
||||
# 清理缓存文件
|
||||
clean_pycache(TOOL_PATH)
|
||||
|
||||
# 首先尝试重新加载主配置文件
|
||||
try:
|
||||
# 尝试直接导入主配置
|
||||
if 'config' in sys.modules:
|
||||
config_module = sys.modules['config']
|
||||
importlib.reload(config_module)
|
||||
print("成功重新加载主配置文件")
|
||||
results['config'] = True
|
||||
|
||||
TOOL_NAME = config_module.TOOL_NAME
|
||||
TOOL_VERSION = config_module.TOOL_VERSION
|
||||
TOOL_AUTHOR = config_module.TOOL_AUTHOR
|
||||
TOOL_PATH = config_module.TOOL_PATH
|
||||
SCRIPTS_PATH = config_module.SCRIPTS_PATH
|
||||
UI_PATH = config_module.UI_PATH
|
||||
STYLE_FILE = config_module.STYLE_FILE
|
||||
ICONS_PATH = config_module.ICONS_PATH
|
||||
ASSETS_PATH = config_module.ASSETS_PATH
|
||||
DNA_FILE_PATH = config_module.DNA_FILE_PATH
|
||||
else:
|
||||
# 尝试重新导入
|
||||
import_main_config()
|
||||
results['config'] = True
|
||||
except Exception as e:
|
||||
print(f"重新加载主配置文件时出错: {str(e)}")
|
||||
results['config'] = False
|
||||
|
||||
# 定义要重新加载的模块
|
||||
modules_to_reload = [
|
||||
"scripts.utils.Config", # 首先重新加载配置
|
||||
"scripts.utils",
|
||||
"scripts.ui",
|
||||
"scripts.Main"
|
||||
]
|
||||
|
||||
# 获取所有子模块
|
||||
all_modules = []
|
||||
for module in modules_to_reload:
|
||||
all_modules.append(module)
|
||||
try:
|
||||
submodules = cls.get_package_modules(module)
|
||||
if submodules:
|
||||
all_modules.extend(submodules)
|
||||
except Exception as e:
|
||||
print(f"获取模块 {module} 的子模块时出错: {str(e)}")
|
||||
|
||||
# 去除重复项并排序
|
||||
all_modules = sorted(list(set(all_modules)))
|
||||
|
||||
# 按照依赖关系对模块进行排序
|
||||
# 确保配置模块先重新加载
|
||||
priority_modules = []
|
||||
normal_modules = []
|
||||
|
||||
for module in all_modules:
|
||||
if "Config" in module or module.endswith(".config"):
|
||||
priority_modules.append(module)
|
||||
else:
|
||||
normal_modules.append(module)
|
||||
|
||||
# 先重新加载优先级模块,然后是普通模块
|
||||
for module in priority_modules + normal_modules:
|
||||
results[module] = cls.reload_module(module)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def reload_all():
|
||||
"""Reload all Plugin modules and display results"""
|
||||
print("\n" + "-"*50)
|
||||
print("Reloading Plugin modules...")
|
||||
print("-"*50)
|
||||
|
||||
# 获取插件根目录 - 使用config中定义的TOOL_PATH
|
||||
try:
|
||||
import config
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
except ImportError:
|
||||
# 如果无法导入config,则使用相对路径
|
||||
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
print(f"Plugin root directory: {TOOL_PATH}")
|
||||
|
||||
# 清理 __pycache__ 目录
|
||||
print("Cleaning __pycache__ directories...")
|
||||
pycache_count = clean_pycache(TOOL_PATH)
|
||||
print(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||||
|
||||
# Reload modules
|
||||
results = ModuleReloader.reload_all_modules()
|
||||
|
||||
success_count = sum(1 for success in results.values() if success)
|
||||
total_count = len(results)
|
||||
|
||||
print("\nReload Summary:")
|
||||
print(f"Successfully reloaded {success_count} of {total_count} modules")
|
||||
|
||||
if success_count < total_count:
|
||||
print("\nFailed modules:")
|
||||
for module, success in results.items():
|
||||
if not success:
|
||||
print(f" - {module}")
|
||||
|
||||
print("-"*50)
|
||||
return results
|
||||
|
||||
|
||||
def show_reload_ui():
|
||||
"""Show a UI for reloading modules"""
|
||||
# 检查是否有Qt模块可用
|
||||
if not 'HAS_QT' in globals() or not HAS_QT:
|
||||
cmds.warning("Qt modules not available. Falling back to command line reload.")
|
||||
return reload_all()
|
||||
|
||||
try:
|
||||
# 创建对话框
|
||||
dialog = QtWidgets.QDialog(getMayaMainWindow())
|
||||
dialog.setWindowTitle("Plugin Reload UI")
|
||||
dialog.setMinimumWidth(450)
|
||||
dialog.setWindowFlags(dialog.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint)
|
||||
|
||||
# 创建布局
|
||||
layout = QtWidgets.QVBoxLayout(dialog)
|
||||
|
||||
# 创建信息标签
|
||||
info_label = QtWidgets.QLabel("Reload Plugin modules, no need to restart Maya.")
|
||||
layout.addWidget(info_label)
|
||||
|
||||
# 创建模块选择区域
|
||||
module_group = QtWidgets.QGroupBox("Select Modules to Reload")
|
||||
module_layout = QtWidgets.QVBoxLayout(module_group)
|
||||
|
||||
# 创建全选复选框
|
||||
all_modules_cb = QtWidgets.QCheckBox("All Modules")
|
||||
all_modules_cb.setChecked(True)
|
||||
module_layout.addWidget(all_modules_cb)
|
||||
|
||||
# 创建模块类别复选框
|
||||
module_cbs = {}
|
||||
module_categories = [
|
||||
("Core Modules", [
|
||||
"scripts",
|
||||
"scripts.Main",
|
||||
"scripts.ReloadModules"
|
||||
]
|
||||
),
|
||||
("UI Modules", [
|
||||
"scripts.ui",
|
||||
"scripts.ui.toolbar",
|
||||
"scripts.ui.geometry",
|
||||
"scripts.ui.rigging",
|
||||
"scripts.ui.behaviour",
|
||||
"scripts.ui.definition"
|
||||
]
|
||||
),
|
||||
("Utility Modules", [
|
||||
"scripts.utils",
|
||||
"scripts.utils.utils_toolbar",
|
||||
"scripts.utils.utils_geometry",
|
||||
"scripts.utils.utils_rigging",
|
||||
"scripts.utils.utils_behaviour",
|
||||
"scripts.utils.utils_definition"
|
||||
]
|
||||
),
|
||||
("Config", ["config"])
|
||||
]
|
||||
for category, modules in module_categories:
|
||||
category_cb = QtWidgets.QCheckBox(category)
|
||||
category_cb.setChecked(True)
|
||||
category_cb.setEnabled(False)
|
||||
module_layout.addWidget(category_cb)
|
||||
module_cbs[category] = (category_cb, modules)
|
||||
|
||||
# 全选/取消全选逻辑
|
||||
def toggle_all_modules():
|
||||
checked = all_modules_cb.isChecked()
|
||||
for category, (cb, _) in module_cbs.items():
|
||||
cb.setChecked(checked)
|
||||
cb.setEnabled(not checked)
|
||||
|
||||
all_modules_cb.toggled.connect(toggle_all_modules)
|
||||
|
||||
layout.addWidget(module_group)
|
||||
|
||||
# 创建操作按钮区域
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
|
||||
# 创建重载按钮
|
||||
reload_button = QtWidgets.QPushButton("Reload Selected Modules")
|
||||
reload_button.setMinimumHeight(30)
|
||||
button_layout.addWidget(reload_button)
|
||||
|
||||
# 创建清理缓存按钮
|
||||
clean_button = QtWidgets.QPushButton("Clean Caches Only")
|
||||
clean_button.setMinimumHeight(30)
|
||||
button_layout.addWidget(clean_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# 创建结果文本区域
|
||||
results_text = QtWidgets.QTextEdit()
|
||||
results_text.setReadOnly(True)
|
||||
results_text.setMinimumHeight(300)
|
||||
layout.addWidget(results_text)
|
||||
|
||||
# 创建关闭按钮
|
||||
close_button = QtWidgets.QPushButton("Close")
|
||||
close_button.setMinimumHeight(30)
|
||||
close_button.clicked.connect(dialog.close)
|
||||
layout.addWidget(close_button)
|
||||
|
||||
# 重载并更新UI的函数
|
||||
def reload_and_update_ui(text_widget):
|
||||
text_widget.clear()
|
||||
text_widget.append("Reloading modules...\n")
|
||||
|
||||
# 获取插件根目录 - 使用config中定义的TOOL_PATH
|
||||
try:
|
||||
import config
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
except ImportError:
|
||||
# 如果无法导入config,则使用相对路径
|
||||
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
text_widget.append(f"Plugin root directory: {TOOL_PATH}")
|
||||
|
||||
# 清理 __pycache__ 目录
|
||||
text_widget.append("Cleaning __pycache__ directories...")
|
||||
pycache_count = clean_pycache(TOOL_PATH)
|
||||
text_widget.append(f"Removed {pycache_count} __pycache__ directories and .pyc files\n")
|
||||
|
||||
# 确定要重载的模块
|
||||
results = {}
|
||||
|
||||
if all_modules_cb.isChecked():
|
||||
# 重载所有模块
|
||||
results = ModuleReloader.reload_all_modules()
|
||||
else:
|
||||
# 重载选定的模块类别
|
||||
selected_categories = []
|
||||
for category, (cb, modules) in module_cbs.items():
|
||||
if cb.isChecked():
|
||||
selected_categories.extend(modules)
|
||||
|
||||
if not selected_categories:
|
||||
text_widget.append("No module was selected, the operation was canceled.")
|
||||
return
|
||||
|
||||
# 获取所有模块
|
||||
all_modules = []
|
||||
for package in selected_categories:
|
||||
if package.startswith("scripts."):
|
||||
all_modules.extend(ModuleReloader.get_package_modules(package))
|
||||
else:
|
||||
all_modules.append(package)
|
||||
|
||||
# 重载选定的模块
|
||||
results = {}
|
||||
for module in all_modules:
|
||||
results[module] = ModuleReloader.reload_module(module)
|
||||
|
||||
# 更新UI显示结果
|
||||
success_count = sum(1 for success in results.values() if success)
|
||||
total_count = len(results)
|
||||
|
||||
text_widget.append(f"Successfully reloaded {success_count}/{total_count} modules\n")
|
||||
|
||||
# 显示成功重载的模块
|
||||
text_widget.append("Successfully reloaded modules:")
|
||||
for module, success in results.items():
|
||||
if success:
|
||||
text_widget.append(f" - {module}")
|
||||
|
||||
# 显示失败的模块(如果有)
|
||||
if success_count < total_count:
|
||||
text_widget.append("\nFailed to reload modules:")
|
||||
for module, success in results.items():
|
||||
if not success:
|
||||
text_widget.append(f" - {module}")
|
||||
|
||||
# 提示用户重载完成
|
||||
text_widget.append("\nReload operation completed!")
|
||||
|
||||
# 仅清理缓存的函数
|
||||
def clean_cache_only(text_widget):
|
||||
text_widget.clear()
|
||||
text_widget.append("Cleaning caches only...\n")
|
||||
|
||||
# 获取插件根目录
|
||||
try:
|
||||
import config
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
except ImportError:
|
||||
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
text_widget.append(f"Plugin root directory: {TOOL_PATH}")
|
||||
|
||||
# 清理 __pycache__ 目录
|
||||
pycache_count = clean_pycache(TOOL_PATH)
|
||||
text_widget.append(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||||
text_widget.append("\nCache cleaning operation completed!")
|
||||
|
||||
# 连接按钮信号
|
||||
reload_button.clicked.connect(lambda: reload_and_update_ui(results_text))
|
||||
clean_button.clicked.connect(lambda: clean_cache_only(results_text))
|
||||
|
||||
# 显示对话框
|
||||
dialog.show()
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
cmds.warning(f"Failed to show UI: {str(e)}. Falling back to command line reload.")
|
||||
return reload_all()
|
||||
|
||||
|
||||
# Main function to be called from Maya
|
||||
def main():
|
||||
"""Main function to be called from Maya"""
|
||||
try:
|
||||
if HAS_QT:
|
||||
show_reload_ui()
|
||||
else:
|
||||
reload_all()
|
||||
|
||||
print("Modules reloaded successfully!")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error during module reload: {str(e)}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
# Allow running this script directly
|
||||
if __name__ == "__main__":
|
||||
main()
|
32
scripts/__init__.py
Normal file
32
scripts/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
MetaHuman DNA工具包
|
||||
|
||||
这个包包含了用于处理MetaHuman DNA文件的工具和实用程序。
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import config
|
||||
# 将主配置中的变量导入到当前模块的命名空间
|
||||
TOOL_NAME = config.TOOL_NAME
|
||||
TOOL_VERSION = config.TOOL_VERSION
|
||||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||||
TOOL_PATH = config.TOOL_PATH
|
||||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||||
UI_PATH = config.UI_PATH
|
||||
STYLE_FILE = config.STYLE_FILE
|
||||
ICONS_PATH = config.ICONS_PATH
|
||||
ASSETS_PATH = config.ASSETS_PATH
|
||||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||||
|
||||
# 确保项目路径在sys.path中
|
||||
if TOOL_PATH not in sys.path:
|
||||
sys.path.insert(0, TOOL_PATH)
|
||||
|
||||
# 确保scripts路径在sys.path中
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
4
scripts/builder/__init__.py
Normal file
4
scripts/builder/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import *
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user