178 lines
8.8 KiB
Markdown
178 lines
8.8 KiB
Markdown
|
---
|
|||
|
title: 使用Python开发Maya插件学习笔记
|
|||
|
date: 2022-11-04 10:20:38
|
|||
|
excerpt:
|
|||
|
tags:
|
|||
|
rating: ⭐
|
|||
|
---
|
|||
|
# 前言
|
|||
|
前段时间研究使用Maya重定向动画,但一个一个手动操作还是有点心烦,所以我花了2天时间学习了Pyhton并写了这个插件(本人有c++、qt、JavaScript经验所以学的快),在这个过程也积累了一些心得在此分享给大家。另外祝大家劳动节快乐。
|
|||
|
|
|||
|
以下是我写的插件,一个通过HumanIK批量重定向动画的工具:
|
|||
|
https://github.com/blueroseslol/DccTool
|
|||
|
|
|||
|
可以帮助动画公司将biped骨骼动画批量重定向到Ue4或者其他骨骼上。里面有很多HumanIK控制代码以及文件导入与导出代码可以参考。如果有什么问题欢迎交流。
|
|||
|
|
|||
|
# 前期准备
|
|||
|
开发环境搭建推荐看这篇文章 https://www.jianshu.com/p/813b2cc71ca2
|
|||
|
|
|||
|
本人是通过《Maya Python游戏与影视编程指南》一书来学习实用Python开发Maya插件,书中也介绍了Python的语法。使得没有Python基础的人也可以很好的学习。同时他也介绍了Maya插件开发的命令模式与API模式。通俗的说就是用Maya的内置命令与实用Maya的API。Maya API更加适合专业插件开发者使用。举个书中例子,API中的基础对象MObject是一个指针对象。所以使用c#或者c++会更加适合API模式的开发吧。
|
|||
|
|
|||
|
不过这本书的很多翻译都感觉怪怪,这是它位移的缺点。
|
|||
|
|
|||
|
我使用Vscode进行开发,除了必须的Python(打开一个Py文件就会提示安装)插件外,我还使用**MayaPy**与**MayaCode**。为了能让MayaPy将代码发送到Maya中执行,还需要再Maya中执行一段开启端口的命令:
|
|||
|
|
|||
|
**Mel**
|
|||
|
```
|
|||
|
commandPort -name "localhost:7001" -sourceType "mel" -echoOutput;
|
|||
|
```
|
|||
|
|
|||
|
**Python**
|
|||
|
```
|
|||
|
import maya.cmds as cmds
|
|||
|
cmds.commandPort(name=":7001", sourceType="mel",echoOutput=True)
|
|||
|
```
|
|||
|
端口与MayaPy中设置的端口有关。如果你不想每次启动Maya都手动执行命令,那么可以新建一个脚本文件并将代码填入。之后放到指定目录中:
|
|||
|
```
|
|||
|
Windows: <drive>:\Documents and Settings\<你的windows用户名>\My Documents\maya\<你maya的版本号>\scripts
|
|||
|
(其实就是我的文档下面maya文件夹)
|
|||
|
MacOSX: ~/Library/Preferences/Autodesk/maya/<你maya的版本号>/scripts.
|
|||
|
Linux: ~/maya/<你maya的版本号>/scripts.
|
|||
|
```
|
|||
|
|
|||
|
**Maya Python路径设置及代码自动补全**<br>
|
|||
|
VS Code中按Ctrl+Shift+P,输入Settings打开settings.json配置文件,在大括号里加入下面代码:
|
|||
|
|
|||
|
```
|
|||
|
//python.pythonPath是指定Python命令路径,请根据你maya的安装路径来做修改
|
|||
|
"python.pythonPath": "C:/Program Files/Autodesk/Maya2019/bin/mayapy.exe",
|
|||
|
//python.autoComplete.extraPaths是代码自动补全路径,同样根据你自己的maya安装路径来写
|
|||
|
"python.autoComplete.extraPaths": "C:/Program Files/Autodesk/Maya2019/devkit/other/pymel/extras/completion/py"
|
|||
|
```
|
|||
|
|
|||
|
注意:settings.json文件中,每一项设置用","隔开,最后一项设置后面没有",",如果报错,检查一下是不是这里出现了问题。
|
|||
|
|
|||
|
# 编码篇
|
|||
|
初次学习可以参考YivanLee的文章
|
|||
|
https://zhuanlan.zhihu.com/p/76957745
|
|||
|
|
|||
|
我认为首先你需要了解Maya中的物体都是节点式的,当然我个人认为Maya的节点更加偏向于组件,而非Houdini那样的流程节点。
|
|||
|
|
|||
|
## 文档与搜索技巧
|
|||
|
请使用谷歌进行搜索(不推荐bing以及baidu),包括搜索API,这样可以节约大量时间。
|
|||
|
|
|||
|
**官方文档**:http://help.autodesk.com/view/MAYAUL/2018/ENU/
|
|||
|
|
|||
|
>Pyside2是python版本的Qt库。你只要看一下它的模块数目就能明白它的强大。另外百度的时候请搜索PyQt5,虽然Pyside才是官方正版。
|
|||
|
|
|||
|
**Pyside2**:https://doc.qt.io/qtforpython/modules.html
|
|||
|
|
|||
|
>PyMel与maya.cmds不同在于,它返回的不是字符串,而是一个PyNode对象。它可以直接修改节点属性值,无需调用getAttr与setAttr。更加适合于习惯了OOP语言人士使用。同时PyMel可以简化GUI的构建。但你看了文档就会明白这个玩意就是个残废。
|
|||
|
|
|||
|
**Pymel文档**:http://help.autodesk.com/cloudhelp/2018/JPN/Maya-Tech-Docs/PyMel/modules.html
|
|||
|
|
|||
|
**FBX Mel命令**:https://knowledge.autodesk.com/zh-hans/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2019/CHS/Maya-DataExchange/files/GUID-335F2172-DD4D-4D79-B969-55238C08F2EF-htm.html
|
|||
|
|
|||
|
# pyside2与GUI
|
|||
|
实用Python构建Maya插件UI有3种方式:
|
|||
|
1. 调用Maya内置命令创建。
|
|||
|
2. 直接调用Pyside2函数创建。
|
|||
|
|
|||
|
本人使用第三种,使用Qt的界面设计师工具创建。虽然本质上就是第二种方法,但效率高。需要注意的是maya目录下的designer.exe是不能直接使用的。需要将安装目录下的qt-plugins中的所有文件都复制到bin所在目录中。之后的步骤就是用Qt的界面设计师工具设计界面了。
|
|||
|
|
|||
|
但本人电脑上有Qt,所有直接就用自己这个版本了。推荐还是用Maya目录下的版本(自己去下个5.6版本的也是可以的)原因后面会说。
|
|||
|
|
|||
|
Qt的界面设计师工具可以输出*.ui文件,但Python是无法直接使用的,(虽然可以通过loadUI载入,但只能调用Mel命令,无法关联python函数)所以之后需要安装Pyside2,目的是为了使用 pyside-uic.exe工具,它可以将 *.ui文件转化为python代码。
|
|||
|
|
|||
|
Pyside2对应python3.x,所以你需要下载3.0的版本并安装。安装完之后,打开CMD切换到安装目录下的script文件夹。执行
|
|||
|
```
|
|||
|
pip install PySide2
|
|||
|
```
|
|||
|
时候再执行
|
|||
|
```
|
|||
|
//请注意文件路径,推荐奖*.ui文件复制到script文件夹中
|
|||
|
pyside-uic -o output.py input.ui
|
|||
|
```
|
|||
|
可能是我用的Qt版本与Maya的不同,最后生成出来的python存在一些小错误:按钮上的setText函数中会多出一个莫名其妙的函数。还有一个问题我倒最后也没搞懂:Pyside2不是对应python3.x与Qt5么,那为什么Maya使用python2.7却可以调用Pyside2呢?是因为预编译了对应的库么?
|
|||
|
|
|||
|
# 学习建议
|
|||
|
我个人建议,如果你想深入地使用pyside2开发插件,强烈推荐先去学习一到两个月的Qt。之后再来学习pyside2你就会非常的顺利。尤其需要了解的是Qt的信号与槽机制、GUI绘制与线程、Qt事件传递机制。
|
|||
|
|
|||
|
# 实用代码
|
|||
|
## 防止窗口重复创建
|
|||
|
```
|
|||
|
def main():
|
|||
|
global win
|
|||
|
try:
|
|||
|
win.close() # 为了不让窗口出现多个,因为第一次运行还没初始化,所以要try,在这里尝试先关闭,再重新新建一个窗口
|
|||
|
except:
|
|||
|
pass
|
|||
|
|
|||
|
//MainWindow为窗口类
|
|||
|
win = MainWindow()
|
|||
|
win.show()
|
|||
|
```
|
|||
|
## 信号槽与解决生命周期问题
|
|||
|
|
|||
|
```
|
|||
|
//其中SIGNAL需要先导入
|
|||
|
from PySide2.QtCore import SIGNAL, QObject
|
|||
|
|
|||
|
class MainWindow(QWidget, Ui_Form):
|
|||
|
def slotBtnClicked(self):
|
|||
|
//为了防止消息框易一出现就被回收,需要给它设置父对象
|
|||
|
msgBox = QMessageBox(self)
|
|||
|
msgBox.setText(u"The document has been modified.")
|
|||
|
msgBox.setInformativeText(u"Do you want to save your changes?")
|
|||
|
msgBox.setStandardButtons(QMessageBox.Save)
|
|||
|
msgBox.setDefaultButton(QMessageBox.Save)
|
|||
|
msgBox.show()
|
|||
|
def __init__(self, parent=None):
|
|||
|
self.pushButton_stop.clicked.connect(self.slotBtnClicked)
|
|||
|
QObject.connect(self.pushButton_targetSkin,SIGNAL('clicked()'), self.slotBtnClicked)
|
|||
|
```
|
|||
|
## python HumanIK
|
|||
|
相关的控制代码可以在 安装目录\scripts\others下搜索hik找到,主要在hikCharacterControlsUI.mel与hikGlobalUtils.mel文件中,也可以参考我插件中的代码。
|
|||
|
|
|||
|
## FBX导出命令
|
|||
|
导入文件可以只用cmds.file命令,但是导出就不太好用了,比如需要烘焙动画什么的,所以需要调用以下Mel命令。
|
|||
|
```
|
|||
|
# FBX Exporter options. Set as required.
|
|||
|
# You can find a reference guide here: http://download.autodesk.com/us/fbx/20112/Maya/_index.html
|
|||
|
# Just add/change what you need.
|
|||
|
|
|||
|
# Geometry
|
|||
|
mm.eval("FBXExportSmoothingGroups -v true")
|
|||
|
mm.eval("FBXExportHardEdges -v false")
|
|||
|
mm.eval("FBXExportTangents -v false")
|
|||
|
mm.eval("FBXExportSmoothMesh -v true")
|
|||
|
mm.eval("FBXExportInstances -v false")
|
|||
|
mm.eval("FBXExportReferencedContainersContent -v false")
|
|||
|
# Animation
|
|||
|
mm.eval("FBXExportBakeComplexAnimation -v true")
|
|||
|
mm.eval("FBXExportBakeComplexStart -v "+str(exportStartFrame[x]))
|
|||
|
mm.eval("FBXExportBakeComplexEnd -v "+str(exportEndFrame[x]))
|
|||
|
mm.eval("FBXExportBakeComplexStep -v 1")
|
|||
|
# mm.eval("FBXExportBakeResampleAll -v true")
|
|||
|
mm.eval("FBXExportUseSceneName -v false")
|
|||
|
mm.eval("FBXExportQuaternion -v euler")
|
|||
|
mm.eval("FBXExportShapes -v true")
|
|||
|
mm.eval("FBXExportSkins -v true")
|
|||
|
# Constraints
|
|||
|
mm.eval("FBXExportConstraints -v false")
|
|||
|
# Cameras
|
|||
|
mm.eval("FBXExportCameras -v false")
|
|||
|
# Lights
|
|||
|
mm.eval("FBXExportLights -v false")
|
|||
|
# Embed Media
|
|||
|
mm.eval("FBXExportEmbeddedTextures -v false")
|
|||
|
# Connections
|
|||
|
mm.eval("FBXExportInputConnections -v false")
|
|||
|
# Axis Conversion
|
|||
|
mm.eval("FBXExportUpAxis y")
|
|||
|
|
|||
|
# Export!
|
|||
|
mm.eval("FBXExport -f "+exportNames[x]+".fbx -s")
|
|||
|
```
|
|||
|
|