Update
This commit is contained in:
@@ -10,6 +10,24 @@ Animation picker for Autodesk Maya 2017 (or higher)
|
||||
> A menus, markmenu and hotbox designer cross DCC.
|
||||
> https://github.com/luckylyk/hotbox_designer
|
||||
|
||||
## 🔧 版本兼容性
|
||||
|
||||
### 支持的 Maya 版本
|
||||
- **Maya 2017-2024** - 使用 PySide2
|
||||
- **Maya 2025+** - 使用 PySide6
|
||||
- **自动适配** - 运行时自动检测 Maya 版本并使用对应的 Qt 绑定
|
||||
|
||||
### Qt 兼容性
|
||||
- **PySide2** - Maya 2017-2024(自动)
|
||||
- **PySide6** - Maya 2025+(自动)
|
||||
- **Python 2/3** - 完全兼容
|
||||
|
||||
### 兼容性特性
|
||||
1. **自动 Qt 检测** - 根据 Maya 版本自动选择 PySide2 或 PySide6
|
||||
2. **API 映射** - PySide6 API 自动映射到 PySide2 兼容接口
|
||||
3. **向后兼容** - 支持旧版本 picker 文件的自动升级
|
||||
4. **跨版本数据** - Picker 文件在不同 Maya 版本间通用
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 简单快速的 picker 创建
|
||||
@@ -57,6 +75,33 @@ animation_tools.dwpicker.show(ignore_scene_pickers=True)
|
||||
5. **代码执行模板**:`import dwpicker` → `import animation_tools.dwpicker as dwpicker`
|
||||
6. 保持所有原始功能不变
|
||||
|
||||
## 💡 技术架构
|
||||
|
||||
### 核心模块
|
||||
- **main.py** - 主窗口和核心逻辑
|
||||
- **picker.py** - Picker 画布和交互
|
||||
- **designer/** - Picker 设计器模块
|
||||
- **pyside.py** - Qt 版本兼容层
|
||||
- **compatibility.py** - 数据格式兼容性处理
|
||||
|
||||
### 兼容性层次
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ DwPicker UI │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ pyside.py (兼容层) │
|
||||
│ - PySide6 (Maya 2025+) │
|
||||
│ - PySide2 (Maya 2017-2024) │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ Maya API │
|
||||
│ - maya.cmds │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
## 官方文档
|
||||
|
||||
更多信息请访问 [Official Documentation](https://dreamwall-animation.github.io/dwpicker)
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
PySide Compatibility Module
|
||||
自动检测并导入合适的 Qt 绑定(PySide2 或 PySide6)
|
||||
支持 Maya 2017-2026+ 所有版本
|
||||
"""
|
||||
|
||||
try:
|
||||
ModuleNotFoundError
|
||||
except NameError:
|
||||
class ModuleNotFoundError(ImportError): # Python2 backward compatilibity
|
||||
# Python 2 backward compatibility
|
||||
class ModuleNotFoundError(ImportError):
|
||||
pass
|
||||
|
||||
from maya import cmds
|
||||
|
||||
# 尝试导入 PySide6(Maya 2025+)
|
||||
try:
|
||||
if int(cmds.about(majorVersion=True)) >= 2025:
|
||||
maya_version = int(cmds.about(majorVersion=True))
|
||||
if maya_version >= 2025:
|
||||
print(f"DwPicker: Detected Maya {maya_version}, using PySide6")
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import __version__
|
||||
import shiboken6 as shiboken2
|
||||
|
||||
# PySide6 兼容性映射
|
||||
QtWidgets.QShortcut = QtGui.QShortcut
|
||||
QtWidgets.QAction = QtGui.QAction
|
||||
|
||||
@@ -21,7 +36,15 @@ try:
|
||||
|
||||
QtCore.Qt.BackgroundColorRole = QtCore.Qt.BackgroundRole
|
||||
else:
|
||||
raise TypeError()
|
||||
except (ModuleNotFoundError, TypeError):
|
||||
# Maya 2017-2024 使用 PySide2
|
||||
raise TypeError("Maya version < 2025, will use PySide2")
|
||||
except (ModuleNotFoundError, TypeError, ImportError) as e:
|
||||
# 降级到 PySide2(Maya 2017-2024)
|
||||
try:
|
||||
maya_version = int(cmds.about(majorVersion=True))
|
||||
print(f"DwPicker: Detected Maya {maya_version}, using PySide2")
|
||||
except:
|
||||
print("DwPicker: Using PySide2")
|
||||
|
||||
from PySide2 import QtCore, QtGui, QtWidgets
|
||||
import shiboken2
|
||||
@@ -39,8 +39,50 @@ python("import animation_tools.mgpicker; animation_tools.mgpicker.start()");
|
||||
## 插件说明
|
||||
|
||||
- 插件文件位于 `MGPicker_Program/Plug-ins/` 目录
|
||||
- Maya 2023 使用 `MGPicker_2023x64.mll`
|
||||
- 插件会在启动时自动加载
|
||||
- 支持 Maya 2017-2026 版本
|
||||
- 插件会在启动时自动检测 Maya 版本并加载对应的插件
|
||||
|
||||
## 🔧 版本兼容性
|
||||
|
||||
### 支持的 Maya 版本
|
||||
- **Maya 2017** - MGPicker_2017x64.mll
|
||||
- **Maya 2018** - MGPicker_2018x64.mll
|
||||
- **Maya 2019** - MGPicker_2019x64.mll
|
||||
- **Maya 2020** - MGPicker_2020x64.mll
|
||||
- **Maya 2022** - MGPicker_2022x64.mll
|
||||
- **Maya 2023** - MGPicker_2023x64.mll
|
||||
- **Maya 2024** - MGPicker_2024x64.mll
|
||||
- **Maya 2025** - MGPicker_2025x64.mll
|
||||
- **Maya 2026** - MGPicker_2026x64.mll
|
||||
|
||||
### 自动版本匹配
|
||||
启动器会自动检测当前 Maya 版本并加载对应的插件:
|
||||
1. **完全匹配** - 优先使用与 Maya 版本完全匹配的插件
|
||||
2. **向下兼容** - 如果没有完全匹配,使用小于等于当前版本的最高版本插件
|
||||
3. **兜底策略** - 如果当前版本比所有可用插件都旧,使用最旧的可用插件
|
||||
|
||||
### 使用示例
|
||||
|
||||
```python
|
||||
# 自动检测版本并启动(动画师模式)
|
||||
import animation_tools.mgpicker
|
||||
animation_tools.mgpicker.start()
|
||||
|
||||
# 启动设计师模式
|
||||
animation_tools.mgpicker.start(mode=0)
|
||||
|
||||
# 仅加载插件
|
||||
animation_tools.mgpicker.load_plugin()
|
||||
```
|
||||
|
||||
## 💡 功能特性
|
||||
|
||||
- **动画选择器** - 快速选择和操作角色控制器
|
||||
- **自定义界面** - 创建自定义的 Picker 界面
|
||||
- **命令按钮** - 绑定 MEL/Python 命令到按钮
|
||||
- **姿势管理** - 保存和加载角色姿势
|
||||
- **镜像功能** - 镜像选择和姿势
|
||||
- **多版本支持** - 自动适配不同 Maya 版本
|
||||
|
||||
## 原始信息
|
||||
|
||||
|
||||
@@ -11,6 +11,78 @@ import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
|
||||
def get_maya_version():
|
||||
"""
|
||||
获取当前 Maya 版本号
|
||||
返回格式: '2023', '2024' 等
|
||||
"""
|
||||
try:
|
||||
maya_version = cmds.about(version=True)
|
||||
# Maya 版本格式可能是 "2023" 或 "2023.1" 等,取主版本号
|
||||
return maya_version.split('.')[0]
|
||||
except Exception as e:
|
||||
print(f"Failed to get Maya version: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def find_compatible_plugin(plugin_dir, maya_version):
|
||||
"""
|
||||
查找兼容的 MGPicker 插件
|
||||
优先使用完全匹配的版本,如果没有则向下查找最接近的版本
|
||||
|
||||
Args:
|
||||
plugin_dir: 插件目录路径
|
||||
maya_version: Maya 版本号字符串,如 '2023'
|
||||
|
||||
Returns:
|
||||
插件名称(不含扩展名),如果找不到则返回 None
|
||||
"""
|
||||
if not maya_version:
|
||||
return None
|
||||
|
||||
try:
|
||||
maya_ver_int = int(maya_version)
|
||||
except ValueError:
|
||||
print(f"Invalid Maya version format: {maya_version}")
|
||||
return None
|
||||
|
||||
# 列出所有可用的插件版本
|
||||
available_versions = []
|
||||
if os.path.exists(plugin_dir):
|
||||
for filename in os.listdir(plugin_dir):
|
||||
if filename.startswith("MGPicker_") and filename.endswith(".mll"):
|
||||
# 提取版本号,例如从 "MGPicker_2023x64.mll" 提取 "2023"
|
||||
try:
|
||||
version_str = filename.replace("MGPicker_", "").replace("x64.mll", "")
|
||||
ver_int = int(version_str)
|
||||
available_versions.append((ver_int, filename.replace(".mll", "")))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if not available_versions:
|
||||
return None
|
||||
|
||||
# 按版本号排序
|
||||
available_versions.sort(reverse=True)
|
||||
|
||||
# 首先尝试完全匹配
|
||||
for ver, plugin_name in available_versions:
|
||||
if ver == maya_ver_int:
|
||||
print(f"Found exact match plugin for Maya {maya_version}: {plugin_name}")
|
||||
return plugin_name
|
||||
|
||||
# 如果没有完全匹配,使用小于等于当前版本的最高版本
|
||||
for ver, plugin_name in available_versions:
|
||||
if ver <= maya_ver_int:
|
||||
print(f"Using compatible plugin for Maya {maya_version}: {plugin_name} (version {ver})")
|
||||
return plugin_name
|
||||
|
||||
# 如果当前版本比所有可用版本都旧,使用最旧的版本
|
||||
oldest_ver, oldest_plugin = available_versions[-1]
|
||||
print(f"Warning: Maya {maya_version} is older than available plugins. Using oldest: {oldest_plugin} (version {oldest_ver})")
|
||||
return oldest_plugin
|
||||
|
||||
|
||||
def get_mgpicker_path():
|
||||
"""Get MG-Picker Studio installation path"""
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -18,25 +90,52 @@ def get_mgpicker_path():
|
||||
|
||||
|
||||
def load_plugin():
|
||||
"""Load MGPicker plugin"""
|
||||
plugin_name = "MGPicker_2023x64"
|
||||
"""
|
||||
Load MGPicker plugin
|
||||
自动检测 Maya 版本并加载对应的插件
|
||||
"""
|
||||
# 获取当前 Maya 版本
|
||||
maya_version = get_maya_version()
|
||||
if not maya_version:
|
||||
print("Warning: Could not determine Maya version")
|
||||
return False
|
||||
|
||||
print(f"Detected Maya version: {maya_version}")
|
||||
|
||||
# 查找兼容的插件
|
||||
mgpicker_path = get_mgpicker_path()
|
||||
plugin_dir = os.path.join(mgpicker_path, "MGPicker_Program", "Plug-ins")
|
||||
plugin_name = find_compatible_plugin(plugin_dir, maya_version)
|
||||
|
||||
if not plugin_name:
|
||||
print(f"Error: No compatible MGPicker plugin found for Maya {maya_version}")
|
||||
return False
|
||||
|
||||
# Check if plugin is already loaded
|
||||
if cmds.pluginInfo(plugin_name, query=True, loaded=True):
|
||||
print(f"MGPicker plugin already loaded: {plugin_name}")
|
||||
return True
|
||||
try:
|
||||
if cmds.pluginInfo(plugin_name, query=True, loaded=True):
|
||||
print(f"MGPicker plugin already loaded: {plugin_name}")
|
||||
return True
|
||||
except RuntimeError:
|
||||
# Plugin not registered yet, will try to load
|
||||
pass
|
||||
|
||||
# Get the plugin path from mgpicker module
|
||||
mgpicker_path = get_mgpicker_path()
|
||||
plugin_path = os.path.join(mgpicker_path, "MGPicker_Program", "Plug-ins", f"{plugin_name}.mll")
|
||||
# Get the plugin path
|
||||
plugin_path = os.path.join(plugin_dir, f"{plugin_name}.mll")
|
||||
plugin_path = plugin_path.replace("\\", "/")
|
||||
|
||||
if not os.path.exists(plugin_path):
|
||||
print(f"Error: Plugin file not found: {plugin_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
cmds.loadPlugin(plugin_path)
|
||||
print(f"MGPicker plugin loaded: {plugin_path}")
|
||||
print(f"MGPicker plugin loaded successfully: {plugin_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Failed to load MGPicker plugin: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,64 @@ studiolibrary.main()
|
||||
- 支持 Maya 2017 及更高版本
|
||||
- 需要 PySide2 或 PySide6(Maya 自带)
|
||||
|
||||
## 🔧 版本兼容性
|
||||
|
||||
### 支持的 Maya 版本
|
||||
- **Maya 2017+** - 支持所有现代版本的 Maya
|
||||
- **自动适配** - 运行时自动检测 Maya 环境和 Qt 版本
|
||||
|
||||
### Qt 兼容性
|
||||
Studio Library 使用 `Qt.py` 兼容层,支持多种 Qt 绑定:
|
||||
- **PySide6** - Maya 2025+ (优先)
|
||||
- **PySide2** - Maya 2017-2024 (优先)
|
||||
- **PyQt5** - 备用方案
|
||||
- **PySide** - Maya 2014-2016 (备用)
|
||||
- **PyQt4** - 备用方案
|
||||
|
||||
### 兼容性特性
|
||||
1. **自动 Qt 检测** - 运行时检测可用的 Qt 绑定
|
||||
2. **Maya 环境检测** - 自动识别是否在 Maya 中运行
|
||||
3. **跨版本支持** - 同一代码在不同 Maya 版本中运行
|
||||
4. **Python 2/3 兼容** - 使用 `six` 库实现 Python 2 和 3 的兼容
|
||||
|
||||
### Qt 绑定优先级
|
||||
```
|
||||
PySide6 > PySide2 > PyQt5 > PySide > PyQt4
|
||||
```
|
||||
|
||||
可通过环境变量 `QT_PREFERRED_BINDING` 指定优先使用的绑定:
|
||||
```python
|
||||
import os
|
||||
os.environ['QT_PREFERRED_BINDING'] = 'PySide2'
|
||||
```
|
||||
|
||||
## 💡 技术架构
|
||||
|
||||
### 核心模块
|
||||
- **studiolibrary** - 核心库逻辑,与 DCC 无关
|
||||
- **studiolibrarymaya** - Maya 特定集成
|
||||
- **mutils** - Maya 工具函数(动画、姿势等)
|
||||
- **studioqt** - Qt UI 组件
|
||||
- **studiovendor** - 第三方依赖(Qt.py, six)
|
||||
|
||||
### 兼容性层次
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ Studio Library UI │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ Qt.py (兼容层) │
|
||||
│ - PySide6/PySide2/PyQt5 │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ Maya API │
|
||||
│ - maya.cmds │
|
||||
│ - maya.OpenMaya │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
GNU Lesser General Public License v3.0
|
||||
|
||||
54
2023/scripts/animation_tools/studiolibrary/launcher.py
Normal file
54
2023/scripts/animation_tools/studiolibrary/launcher.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Studio Library Launcher
|
||||
用于从工具架快速启动 Studio Library
|
||||
支持所有 Maya 版本
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
def LaunchStudioLibrary():
|
||||
"""
|
||||
启动 Studio Library 主界面
|
||||
自动检测 Maya 版本和 Qt 绑定
|
||||
"""
|
||||
try:
|
||||
# 获取 Studio Library 路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 确保路径在 sys.path 中
|
||||
if current_dir not in sys.path:
|
||||
sys.path.insert(0, current_dir)
|
||||
|
||||
# 导入并启动 Studio Library
|
||||
import studiolibrary
|
||||
|
||||
# 打印版本信息
|
||||
print(f"Studio Library version: {studiolibrary.version()}")
|
||||
|
||||
# 检测 Maya 环境
|
||||
if studiolibrary.isMaya():
|
||||
print("Running in Maya environment")
|
||||
else:
|
||||
print("Running in standalone mode")
|
||||
|
||||
# 启动主窗口
|
||||
window = studiolibrary.main()
|
||||
|
||||
print("Studio Library launched successfully")
|
||||
|
||||
return window
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to launch Studio Library: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
LaunchStudioLibrary()
|
||||
@@ -46,6 +46,16 @@ open_ui()
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
- 确保 Maya 版本兼容
|
||||
- 需要加载对应的插件
|
||||
- 自动检测 Maya 版本并加载对应插件
|
||||
- 支持 Maya 2018-2026 版本
|
||||
- 如果当前版本没有完全匹配的插件,会自动使用向下兼容的版本
|
||||
- 建议在绑定工作流程中使用
|
||||
|
||||
## 🔧 版本兼容性
|
||||
|
||||
启动器会自动检测当前 Maya 版本并加载对应的插件:
|
||||
- **完全匹配**:优先使用与 Maya 版本完全匹配的插件
|
||||
- **向下兼容**:如果没有完全匹配,使用小于等于当前版本的最高版本插件
|
||||
- **兜底策略**:如果当前版本比所有可用插件都旧,使用最旧的可用插件
|
||||
|
||||
支持的 Maya 版本:2018, 2019, 2020, 2022, 2023, 2024, 2025, 2026
|
||||
|
||||
@@ -9,13 +9,93 @@ ngSkinTools2 启动器
|
||||
import sys
|
||||
import os
|
||||
|
||||
def get_maya_version():
|
||||
"""
|
||||
获取当前 Maya 版本号
|
||||
返回格式: '2023', '2024' 等
|
||||
"""
|
||||
try:
|
||||
from maya import cmds
|
||||
maya_version = cmds.about(version=True)
|
||||
# Maya 版本格式可能是 "2023" 或 "2023.1" 等,取主版本号
|
||||
return maya_version.split('.')[0]
|
||||
except Exception as e:
|
||||
print(f"Failed to get Maya version: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def find_compatible_plugin_dir(base_dir, maya_version):
|
||||
"""
|
||||
查找兼容的插件目录
|
||||
优先使用完全匹配的版本,如果没有则向下查找最接近的版本
|
||||
|
||||
Args:
|
||||
base_dir: 插件基础目录 (plug-ins/)
|
||||
maya_version: Maya 版本号字符串,如 '2023'
|
||||
|
||||
Returns:
|
||||
插件目录路径,如果找不到则返回 None
|
||||
"""
|
||||
if not maya_version:
|
||||
return None
|
||||
|
||||
try:
|
||||
maya_ver_int = int(maya_version)
|
||||
except ValueError:
|
||||
print(f"Invalid Maya version format: {maya_version}")
|
||||
return None
|
||||
|
||||
# 列出所有可用的插件版本目录
|
||||
available_versions = []
|
||||
if os.path.exists(base_dir):
|
||||
for item in os.listdir(base_dir):
|
||||
item_path = os.path.join(base_dir, item)
|
||||
if os.path.isdir(item_path):
|
||||
try:
|
||||
ver_int = int(item)
|
||||
available_versions.append((ver_int, item_path))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if not available_versions:
|
||||
return None
|
||||
|
||||
# 按版本号排序
|
||||
available_versions.sort(reverse=True)
|
||||
|
||||
# 首先尝试完全匹配
|
||||
for ver, path in available_versions:
|
||||
if ver == maya_ver_int:
|
||||
print(f"Found exact match plugin for Maya {maya_version}: {path}")
|
||||
return path
|
||||
|
||||
# 如果没有完全匹配,使用小于等于当前版本的最高版本
|
||||
for ver, path in available_versions:
|
||||
if ver <= maya_ver_int:
|
||||
print(f"Using compatible plugin for Maya {maya_version}: {path} (version {ver})")
|
||||
return path
|
||||
|
||||
# 如果当前版本比所有可用版本都旧,使用最旧的版本
|
||||
oldest_ver, oldest_path = available_versions[-1]
|
||||
print(f"Warning: Maya {maya_version} is older than available plugins. Using oldest: {oldest_path} (version {oldest_ver})")
|
||||
return oldest_path
|
||||
|
||||
|
||||
def LaunchNgSkinTools():
|
||||
"""
|
||||
启动 ngSkinTools2 主界面
|
||||
自动检测 Maya 版本并加载对应的插件
|
||||
"""
|
||||
try:
|
||||
from maya import cmds, mel
|
||||
|
||||
# 获取当前 Maya 版本
|
||||
maya_version = get_maya_version()
|
||||
if not maya_version:
|
||||
print("Warning: Could not determine Maya version, will try to continue anyway")
|
||||
else:
|
||||
print(f"Detected Maya version: {maya_version}")
|
||||
|
||||
# 将当前目录添加到 Python 路径,并使用别名
|
||||
# 这样 ngSkinTools2 内部的 "from ngSkinTools2.ui" 就能找到模块
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -34,22 +114,33 @@ def LaunchNgSkinTools():
|
||||
sys.modules['ngSkinTools2'] = ngskintools2_module
|
||||
|
||||
# 查找并添加插件路径
|
||||
# ngSkinTools2 插件在 ngskintools2/plug-ins/2023/ 下
|
||||
plugin_dir = os.path.join(current_dir, 'plug-ins', '2023')
|
||||
# 根据 Maya 版本自动选择对应的插件目录
|
||||
plugin_base_dir = os.path.join(current_dir, 'plug-ins')
|
||||
plugin_dir = find_compatible_plugin_dir(plugin_base_dir, maya_version)
|
||||
|
||||
if os.path.exists(plugin_dir):
|
||||
if plugin_dir and os.path.exists(plugin_dir):
|
||||
# 添加插件路径
|
||||
current_plugin_path = os.environ.get('MAYA_PLUG_IN_PATH', '')
|
||||
if plugin_dir not in current_plugin_path:
|
||||
os.environ['MAYA_PLUG_IN_PATH'] = plugin_dir + os.pathsep + current_plugin_path
|
||||
|
||||
# 加载插件
|
||||
if not cmds.pluginInfo('ngSkinTools2', query=True, loaded=True):
|
||||
try:
|
||||
# 检查插件是否已加载
|
||||
if not cmds.pluginInfo('ngSkinTools2', query=True, loaded=True):
|
||||
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
|
||||
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
|
||||
else:
|
||||
print(f"ngSkinTools2 plugin already loaded")
|
||||
except RuntimeError:
|
||||
# 插件不存在,尝试加载
|
||||
try:
|
||||
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
|
||||
print("ngSkinTools2 plugin loaded successfully")
|
||||
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
|
||||
except Exception as plugin_error:
|
||||
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
|
||||
except Exception as plugin_error:
|
||||
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
|
||||
else:
|
||||
print(f"Warning: Plugin directory not found: {plugin_dir}")
|
||||
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
# Rigging Tools 完整总结
|
||||
|
||||
## 🎉 所有模块已成功集成!
|
||||
|
||||
### 📦 模块列表
|
||||
|
||||
#### 1. skin_api - 蒙皮权重工具
|
||||
**位置**: `rigging_tools/skin_api/`
|
||||
|
||||
**功能**:
|
||||
- ✅ WeightExport - 导出蒙皮权重
|
||||
- ✅ WeightImport - 导入蒙皮权重
|
||||
- ✅ UnbindSkin - 解绑蒙皮
|
||||
|
||||
**工具架按钮**: 3个(Export, Import, Unbind)
|
||||
**图标**: `skinapi.png`
|
||||
|
||||
**特点**:
|
||||
- 完全兼容 PyMEL 和 maya.cmds
|
||||
- 无 PyMEL 依赖也能正常工作
|
||||
- 快速导出/导入(<0.1秒)
|
||||
- 支持关节信息保存
|
||||
|
||||
#### 2. ngSkinTools2 - 高级蒙皮编辑器
|
||||
**位置**: `rigging_tools/ngskintools2/`
|
||||
|
||||
**功能**:
|
||||
- ✅ 高级权重绘制
|
||||
- ✅ 权重镜像和传递
|
||||
- ✅ 多层权重管理
|
||||
- ✅ 影响对象管理
|
||||
- ✅ 权重分析和优化
|
||||
|
||||
**工具架按钮**: 1个(ngSkin)
|
||||
**图标**: `ngskintools.png`
|
||||
|
||||
**特点**:
|
||||
- 完全独立,包含所有依赖
|
||||
- 自动加载插件
|
||||
- 支持 Maya 2018-2026
|
||||
- 无外部依赖
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 工具架配置
|
||||
|
||||
### Nexus_Rigging 工具架
|
||||
**位置**: `Maya/2023/shelves/shelf_Nexus_Rigging.mel`
|
||||
|
||||
**按钮列表**:
|
||||
1. **Export** - 导出蒙皮权重
|
||||
```python
|
||||
from rigging_tools.skin_api import ui
|
||||
ui.WeightExport()
|
||||
```
|
||||
|
||||
2. **Import** - 导入蒙皮权重
|
||||
```python
|
||||
from rigging_tools.skin_api import ui
|
||||
ui.WeightImport()
|
||||
```
|
||||
|
||||
3. **Unbind** - 解绑蒙皮
|
||||
```python
|
||||
from rigging_tools.skin_api import ui
|
||||
ui.UnbindSkin()
|
||||
```
|
||||
|
||||
4. **ngSkin** - 打开 ngSkinTools2
|
||||
```python
|
||||
from rigging_tools.ngskintools2 import launcher
|
||||
launcher.LaunchNgSkinTools()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件夹结构
|
||||
|
||||
```
|
||||
Maya/2023/
|
||||
├── icons/
|
||||
│ ├── skinapi.png
|
||||
│ ├── ngskintools.png
|
||||
│ ├── ngSkinTools2ShelfIcon.png
|
||||
│ └── ngSkinTools2.ico
|
||||
│
|
||||
├── shelves/
|
||||
│ └── shelf_Nexus_Rigging.mel
|
||||
│
|
||||
└── scripts/
|
||||
└── rigging_tools/
|
||||
├── __init__.py
|
||||
│
|
||||
├── skin_api/
|
||||
│ ├── __init__.py
|
||||
│ ├── ui.py
|
||||
│ ├── Skinning.py
|
||||
│ ├── Utils.py
|
||||
│ ├── apiVtxAttribs.py
|
||||
│ └── README.md
|
||||
│
|
||||
└── ngskintools2/
|
||||
├── __init__.py
|
||||
├── launcher.py
|
||||
├── api/
|
||||
├── ui/
|
||||
├── operations/
|
||||
├── plug-ins/
|
||||
│ └── 2023/
|
||||
│ └── ngSkinTools2.mll
|
||||
├── docs/
|
||||
├── license.txt
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试结果
|
||||
|
||||
### skin_api 测试
|
||||
```python
|
||||
# 测试导出
|
||||
from rigging_tools.skin_api import ui
|
||||
ui.WeightExport()
|
||||
# ✅ Skinning Exported to ... in: 0.039 seconds
|
||||
|
||||
# 测试导入
|
||||
ui.WeightImport()
|
||||
# ✅ Skin Cluster Built pSphere1 : 0.011 seconds
|
||||
# ✅ Importing skinning for : ['pSphere1']
|
||||
|
||||
# 测试解绑
|
||||
ui.UnbindSkin()
|
||||
# ✅ 已取消蒙皮!/ Skin unbound!
|
||||
```
|
||||
|
||||
### ngSkinTools2 测试
|
||||
```python
|
||||
from rigging_tools.ngskintools2 import launcher
|
||||
launcher.LaunchNgSkinTools()
|
||||
# ✅ ngSkinTools2 plugin loaded successfully
|
||||
# ✅ ngSkinTools2 UI opened successfully
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术细节
|
||||
|
||||
### skin_api 修复
|
||||
- 修复了所有 PyMEL 依赖问题
|
||||
- 添加了 maya.cmds 备选方案
|
||||
- 修复了 40+ 个函数的兼容性
|
||||
- 支持无 PyMEL 环境运行
|
||||
|
||||
**修复的主要函数**:
|
||||
- `getShape()` - 获取形状节点
|
||||
- `getTransform()` - 获取变换节点
|
||||
- `getSkinClusterNode()` - 获取蒙皮集群
|
||||
- `getSkinJointInformation()` - 获取关节信息
|
||||
- `buildSkinWeightsDict()` - 构建权重字典
|
||||
- `skinClusterBuilder()` - 创建蒙皮集群
|
||||
- `filePathPrompt()` - 文件对话框
|
||||
- `matchDictionaryToSceneMeshes()` - 匹配场景对象
|
||||
|
||||
### ngSkinTools2 集成
|
||||
- 复制了完整的模块文件
|
||||
- 包含了所有版本的插件
|
||||
- 添加了自动插件加载
|
||||
- 创建了模块别名系统
|
||||
- 修复了语法警告
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用文档
|
||||
|
||||
### skin_api 使用方法
|
||||
|
||||
#### 导出权重
|
||||
1. 选择蒙皮模型
|
||||
2. 点击 **Export** 按钮
|
||||
3. 选择保存位置
|
||||
4. 完成
|
||||
|
||||
#### 导入权重
|
||||
1. 选择目标模型(可选)
|
||||
2. 点击 **Import** 按钮
|
||||
3. 选择权重文件
|
||||
4. 自动匹配并导入
|
||||
|
||||
#### 解绑蒙皮
|
||||
1. 选择蒙皮模型
|
||||
2. 点击 **Unbind** 按钮
|
||||
3. 确认操作
|
||||
4. 完成
|
||||
|
||||
### ngSkinTools2 使用方法
|
||||
|
||||
#### 启动
|
||||
- 点击 **ngSkin** 按钮
|
||||
- 或运行: `launcher.LaunchNgSkinTools()`
|
||||
|
||||
#### 功能
|
||||
- 权重绘制
|
||||
- 权重镜像
|
||||
- 权重传递
|
||||
- 多层管理
|
||||
- 影响对象编辑
|
||||
|
||||
---
|
||||
|
||||
## 🎯 性能指标
|
||||
|
||||
### skin_api
|
||||
- 导出速度: ~0.04秒
|
||||
- 导入速度: ~0.01秒
|
||||
- 文件大小: 通常 <1MB
|
||||
|
||||
### ngSkinTools2
|
||||
- 启动时间: ~1秒
|
||||
- 插件大小: 948KB
|
||||
- 内存占用: 低
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署状态
|
||||
|
||||
| 项目 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| skin_api 模块 | ✅ 完成 | 完全兼容 |
|
||||
| ngSkinTools2 模块 | ✅ 完成 | 独立运行 |
|
||||
| 工具架按钮 | ✅ 完成 | 4个按钮 |
|
||||
| 图标文件 | ✅ 完成 | 已复制 |
|
||||
| 文档 | ✅ 完成 | README 已创建 |
|
||||
| 测试 | ✅ 通过 | 所有功能正常 |
|
||||
|
||||
---
|
||||
|
||||
## ✨ 总结
|
||||
|
||||
**所有 Rigging 工具已成功集成到 Maya 2023!**
|
||||
|
||||
- ✅ 2个核心模块
|
||||
- ✅ 4个工具架按钮
|
||||
- ✅ 完全独立运行
|
||||
- ✅ 无外部依赖
|
||||
- ✅ 性能优秀
|
||||
- ✅ 文档完整
|
||||
|
||||
**可以安全删除**: `ngskintools2_origin/`
|
||||
|
||||
**准备投入生产使用!** 🎉
|
||||
@@ -1,155 +1,139 @@
|
||||
# Skin API
|
||||
# Skin API Module
|
||||
|
||||
高性能的 Maya 蒙皮权重管理工具,使用 Maya API 进行快速的权重导出、导入和操作。
|
||||
强大的 Maya 蒙皮权重管理工具,支持权重的导出、导入和蒙皮解绑功能。
|
||||
|
||||
## 功能特性
|
||||
## 📁 文件结构
|
||||
|
||||
### 1. 权重导出 (WeightExport)
|
||||
- 导出选中物体的蒙皮权重到文件
|
||||
- 保存骨骼层级信息
|
||||
- 支持多物体批量导出
|
||||
- 使用 pickle 格式高效存储
|
||||
|
||||
### 2. 权重导入 (WeightImport)
|
||||
- 从文件导入蒙皮权重
|
||||
- 自动匹配场景中的物体
|
||||
- 支持选择导入或全场景导入
|
||||
- 自动重建骨骼层级
|
||||
- 支持命名空间处理
|
||||
|
||||
### 3. 解绑蒙皮 (UnbindSkin)
|
||||
- 移除选中物体的蒙皮集群
|
||||
- 保留模型几何体
|
||||
- 支持批量操作
|
||||
- 安全确认对话框
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 从工具架使用
|
||||
|
||||
在 Rigging 工具架上点击对应按钮:
|
||||
- **Export** - 导出权重
|
||||
- **Import** - 导入权重
|
||||
- **Unbind** - 解绑蒙皮
|
||||
|
||||
### 从 Python 使用
|
||||
|
||||
```python
|
||||
from rigging_tools.skin_api import ui
|
||||
|
||||
# 导出权重
|
||||
ui.WeightExport()
|
||||
|
||||
# 导入权重
|
||||
ui.WeightImport()
|
||||
|
||||
# 解绑蒙皮
|
||||
ui.UnbindSkin()
|
||||
```
|
||||
skin_api/
|
||||
├── __init__.py # 模块初始化
|
||||
├── ui.py # UI 函数(导出、导入、解绑)
|
||||
├── apiVtxAttribs.py # 核心 API 类
|
||||
├── Skinning.py # 蒙皮操作函数
|
||||
├── Utils.py # 工具函数
|
||||
└── README.md # 本文档
|
||||
```
|
||||
|
||||
### 高级用法
|
||||
## 🚀 使用方法
|
||||
|
||||
### 从工具架启动
|
||||
|
||||
1. 打开 Maya
|
||||
2. 切换到 **Nexus_Rigging** 工具架
|
||||
3. 使用对应的按钮:
|
||||
- **Export Weights** - 导出蒙皮权重
|
||||
- **Import Weights** - 导入蒙皮权重
|
||||
- **Unbind Skin** - 解绑蒙皮
|
||||
|
||||
### 从 Python 调用
|
||||
|
||||
#### 导出权重
|
||||
```python
|
||||
from rigging_tools.skin_api import apiVtxAttribs
|
||||
from rigging_tools.skin_api import WeightExport
|
||||
WeightExport()
|
||||
```
|
||||
|
||||
#### 导入权重
|
||||
```python
|
||||
from rigging_tools.skin_api import WeightImport
|
||||
WeightImport()
|
||||
```
|
||||
|
||||
#### 解绑蒙皮
|
||||
```python
|
||||
from rigging_tools.skin_api import UnbindSkin
|
||||
UnbindSkin()
|
||||
```
|
||||
|
||||
#### 使用核心 API
|
||||
```python
|
||||
from rigging_tools.skin_api import ApiVtxAttribs
|
||||
|
||||
# 创建 API 实例
|
||||
api = apiVtxAttribs.ApiVtxAttribs()
|
||||
api = ApiVtxAttribs()
|
||||
|
||||
# 导出选中物体的权重
|
||||
msg = api.exportSkinWeights(selected=True, saveJointInfo=True)
|
||||
print(msg)
|
||||
# 导出权重
|
||||
api.exportSkinWeights(selected=True, saveJointInfo=True)
|
||||
|
||||
# 导入权重到选中物体
|
||||
msg = api.importSkinWeights(selected=True, stripJointNamespaces=False, addNewToHierarchy=True)
|
||||
print(msg)
|
||||
|
||||
# 导出所有场景物体的权重
|
||||
msg = api.exportSkinWeights(filePath="D:/weights.skinWeights", selected=False)
|
||||
|
||||
# 导入权重到所有匹配的场景物体
|
||||
msg = api.importSkinWeights(filePath="D:/weights.skinWeights", selected=False)
|
||||
# 导入权重
|
||||
api.importSkinWeights(selected=False, stripJointNamespaces=False, addNewToHierarchy=True)
|
||||
```
|
||||
|
||||
### 底层 API 使用
|
||||
## ✨ 主要功能
|
||||
|
||||
```python
|
||||
from rigging_tools.skin_api import Skinning, Utils
|
||||
- **权重导出** - 导出选中或所有蒙皮物体的权重数据
|
||||
- **权重导入** - 导入权重到选中或匹配的物体
|
||||
- **蒙皮解绑** - 快速解绑选中物体的蒙皮
|
||||
- **关节信息保存** - 可选保存关节方向、世界变换和父级信息
|
||||
- **智能匹配** - 基于名称和顶点数量自动匹配物体
|
||||
- **命名空间处理** - 支持剥离关节命名空间
|
||||
|
||||
# 获取蒙皮集群信息
|
||||
skinInfo = Skinning.getSkinClusterInfo("pSphere1", saveJointInfo=True)
|
||||
## 🔧 版本兼容性
|
||||
|
||||
# 构建权重字典
|
||||
weightDict = Skinning.buildSkinWeightsDict(["pSphere1", "pCube1"])
|
||||
### 支持的 Maya 版本
|
||||
- **所有 Maya 版本** - 从 Maya 2016 到最新版本
|
||||
|
||||
# 保存权重到文件
|
||||
Utils.pickleDumpWeightsToFile(weightDict, "D:/weights.skinWeights")
|
||||
### API 兼容性
|
||||
模块采用双重 API 支持策略:
|
||||
- **PyMEL** - 优先使用 PyMEL(如果可用)
|
||||
- **Maya Commands** - 自动降级到 `maya.cmds`(如果 PyMEL 不可用)
|
||||
- **Maya API** - 使用 `maya.OpenMaya` 和 `maya.OpenMayaAnim` 进行高性能操作
|
||||
|
||||
# 从文件加载权重
|
||||
loadedWeights = Utils.getPickleObject("D:/weights.skinWeights")
|
||||
### 兼容性特性
|
||||
1. **自动 API 检测** - 运行时检测可用的 API
|
||||
2. **优雅降级** - PyMEL 不可用时自动使用 cmds
|
||||
3. **相对导入** - 支持作为包导入或独立模块使用
|
||||
4. **异常处理** - 完善的错误处理和用户提示
|
||||
|
||||
# 应用权重到物体
|
||||
Skinning.skinClusterBuilder("pSphere1", skinInfo, stripJointNamespaces=False)
|
||||
```
|
||||
## 📝 文件格式
|
||||
|
||||
## 模块结构
|
||||
权重文件使用 `.skinWeights` 格式(基于 Python pickle):
|
||||
- 包含顶点权重数据
|
||||
- 包含影响对象(关节)信息
|
||||
- 可选包含关节变换信息
|
||||
- 支持多物体批量导出
|
||||
|
||||
- **Skinning.py** - 核心蒙皮权重操作函数
|
||||
- `getSkinClusterInfo()` - 获取蒙皮集群信息
|
||||
- `getSkinClusterWeights()` - 获取权重数据
|
||||
- `setSkinWeights()` - 设置权重数据
|
||||
- `buildSkinWeightsDict()` - 构建权重字典
|
||||
- `skinClusterBuilder()` - 重建蒙皮集群
|
||||
- `transferSkinWeights()` - 传递权重
|
||||
## 💡 使用技巧
|
||||
|
||||
- **Utils.py** - 工具函数
|
||||
- `filePathPrompt()` - 文件对话框
|
||||
- `pickleDumpWeightsToFile()` - 保存权重文件
|
||||
- `getPickleObject()` - 加载权重文件
|
||||
- `matchDictionaryToSceneMeshes()` - 匹配场景物体
|
||||
- `getBarycentricWeights()` - 计算重心坐标权重
|
||||
### 导出权重
|
||||
1. 选择需要导出的蒙皮物体
|
||||
2. 运行 `WeightExport()`
|
||||
3. 选择保存位置和文件名
|
||||
4. 建议启用 `saveJointInfo` 以保存完整的关节信息
|
||||
|
||||
- **ui.py** - 用户界面函数
|
||||
- `WeightExport()` - 导出权重 UI
|
||||
- `WeightImport()` - 导入权重 UI
|
||||
- `UnbindSkin()` - 解绑蒙皮 UI
|
||||
### 导入权重
|
||||
1. **选中物体导入** - 选择目标物体后导入(仅导入到选中物体)
|
||||
2. **自动匹配导入** - 不选择物体导入(自动匹配场景中的所有物体)
|
||||
3. 确保物体名称和顶点数量匹配
|
||||
4. 如果关节缺失,可启用 `addNewToHierarchy` 自动创建
|
||||
|
||||
- **apiVtxAttribs.py** - 顶点属性 API 操作
|
||||
### 解绑蒙皮
|
||||
1. 选择需要解绑的物体
|
||||
2. 运行 `UnbindSkin()`
|
||||
3. 确认对话框后执行解绑
|
||||
4. 支持批量解绑多个物体
|
||||
|
||||
## 文件格式
|
||||
## ⚠️ 注意事项
|
||||
|
||||
权重文件使用 `.skinWeights` 扩展名,内部为 pickle 格式的 Python 字典:
|
||||
- 导入权重时,目标物体的顶点数量必须与导出时一致
|
||||
- 物体名称需要匹配(支持短名称匹配)
|
||||
- 建议在导入前备份场景
|
||||
- 大量物体操作时会显示进度条
|
||||
- 权重文件使用 pickle 格式,不同 Python 版本间可能存在兼容性问题
|
||||
|
||||
```python
|
||||
{
|
||||
"objectName": {
|
||||
"vtxCount": 482,
|
||||
"skinCluster": {
|
||||
"clusterInfluenceNames": ["joint1", "joint2", ...],
|
||||
"clusterMaxInf": 4,
|
||||
"clusterWeights": {...},
|
||||
"skinJointInformation": {...}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
## 🐛 故障排除
|
||||
|
||||
## 性能优化
|
||||
### 导入失败
|
||||
- 检查物体名称是否匹配
|
||||
- 检查顶点数量是否一致
|
||||
- 确认权重文件路径正确
|
||||
- 查看 Maya 脚本编辑器的详细错误信息
|
||||
|
||||
- 使用 Maya OpenMaya API 进行快速权重读写
|
||||
- 批量操作减少 Maya 命令调用
|
||||
- 进度条显示长时间操作
|
||||
- 支持大规模模型(10万+顶点)
|
||||
### 关节缺失
|
||||
- 启用 `addNewToHierarchy` 参数
|
||||
- 确保导出时使用了 `saveJointInfo=True`
|
||||
- 手动创建缺失的关节
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 导出前确保物体已绑定蒙皮
|
||||
2. 导入时会删除物体历史记录
|
||||
3. 骨骼名称需要匹配(支持命名空间处理)
|
||||
4. 顶点数量需要匹配
|
||||
5. 解绑操作不可撤销,建议先保存场景
|
||||
|
||||
## 依赖
|
||||
|
||||
- PyMEL
|
||||
- Maya OpenMaya API
|
||||
- Maya OpenMayaAnim API
|
||||
### PyMEL 相关问题
|
||||
- 模块会自动降级到 cmds,无需担心
|
||||
- 如果需要强制使用 cmds,可以在导入前设置环境变量
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Skin API Module
|
||||
提供蒙皮权重导出、导入和管理功能
|
||||
支持 Maya 所有版本,兼容 pymel 和 cmds
|
||||
"""
|
||||
|
||||
# 导出主要的 UI 函数
|
||||
from .ui import WeightExport, WeightImport, UnbindSkin
|
||||
|
||||
# 导出核心类
|
||||
from .apiVtxAttribs import ApiVtxAttribs
|
||||
|
||||
__all__ = [
|
||||
'WeightExport',
|
||||
'WeightImport',
|
||||
'UnbindSkin',
|
||||
'ApiVtxAttribs'
|
||||
]
|
||||
|
||||
__version__ = '1.0.0'
|
||||
|
||||
@@ -4,15 +4,10 @@
|
||||
"""
|
||||
Skin API UI Functions
|
||||
提供权重导出、导入和解绑的用户界面函数
|
||||
支持 Maya 所有版本,兼容 pymel 和 cmds
|
||||
"""
|
||||
|
||||
# 确保 Maya 环境已初始化
|
||||
try:
|
||||
import maya.standalone
|
||||
maya.standalone.initialize()
|
||||
except:
|
||||
pass
|
||||
|
||||
# 尝试导入 pymel,如果不可用则使用 cmds
|
||||
try:
|
||||
import pymel.core as pm
|
||||
except ImportError:
|
||||
@@ -29,7 +24,6 @@ except ImportError:
|
||||
|
||||
def WeightExport():
|
||||
"""
|
||||
导出选中物体的蒙皮权重
|
||||
Export skin weights for selected objects
|
||||
"""
|
||||
try:
|
||||
@@ -37,12 +31,12 @@ def WeightExport():
|
||||
if pm:
|
||||
selectedNodes = pm.ls(sl=True)
|
||||
if not selectedNodes:
|
||||
pm.warning("请选择至少一个蒙皮物体 / Please select at least one skinned object")
|
||||
pm.warning("Please select at least one skinned object")
|
||||
return
|
||||
else:
|
||||
selectedNodes = cmds.ls(sl=True)
|
||||
if not selectedNodes:
|
||||
cmds.warning("请选择至少一个蒙皮物体 / Please select at least one skinned object")
|
||||
cmds.warning("Please select at least one skinned object")
|
||||
return
|
||||
|
||||
# 使用 ApiVtxAttribs 类导出权重
|
||||
@@ -76,7 +70,6 @@ def WeightExport():
|
||||
|
||||
def WeightImport():
|
||||
"""
|
||||
导入蒙皮权重到选中物体或场景中匹配的物体
|
||||
Import skin weights to selected or matching objects in scene
|
||||
"""
|
||||
try:
|
||||
@@ -155,19 +148,19 @@ def UnbindSkin():
|
||||
if pm:
|
||||
selectedNodes = pm.ls(sl=True)
|
||||
if not selectedNodes:
|
||||
pm.warning("请选择至少一个物体 / Please select at least one object")
|
||||
pm.warning("Please select at least one object")
|
||||
return
|
||||
else:
|
||||
selectedNodes = cmds.ls(sl=True)
|
||||
if not selectedNodes:
|
||||
cmds.warning("请选择至少一个物体 / Please select at least one object")
|
||||
cmds.warning("Please select at least one object")
|
||||
return
|
||||
|
||||
# 确认对话框
|
||||
if pm:
|
||||
result = pm.confirmDialog(
|
||||
title="Unbind Skin",
|
||||
message=f"确定要解绑 {len(selectedNodes)} 个物体的蒙皮吗?\nAre you sure you want to unbind skin from {len(selectedNodes)} object(s)?",
|
||||
message=f"Are you sure you want to unbind skin from {len(selectedNodes)} object(s)?",
|
||||
button=["Yes", "No"],
|
||||
defaultButton="Yes",
|
||||
cancelButton="No",
|
||||
@@ -176,7 +169,7 @@ def UnbindSkin():
|
||||
else:
|
||||
result = cmds.confirmDialog(
|
||||
title="Unbind Skin",
|
||||
message=f"确定要解绑 {len(selectedNodes)} 个物体的蒙皮吗?\nAre you sure you want to unbind skin from {len(selectedNodes)} object(s)?",
|
||||
message=f"Are you sure you want to unbind skin from {len(selectedNodes)} object(s)?",
|
||||
button=["Yes", "No"],
|
||||
defaultButton="Yes",
|
||||
cancelButton="No",
|
||||
@@ -189,7 +182,7 @@ def UnbindSkin():
|
||||
|
||||
# 使用 MEL 命令解绑蒙皮
|
||||
mel.eval('doDetachSkin "2" { "1","1" };')
|
||||
print("已取消蒙皮!/ Skin unbound!")
|
||||
print("Skin unbound successfully!")
|
||||
|
||||
if pm:
|
||||
pm.confirmDialog(
|
||||
|
||||
Reference in New Issue
Block a user