前言
去年的时候我有水过一篇文章 Maya 命令 自定义触发 & Mel 源码获取
看标题就知道介绍了 Maya 一些基础的命令调用方式,并且详细介绍了几个好用的 Mel 命令来辅助解析 Maya 回显的命令。
利用whatIs
和runTimeCommand
可以非常方便查询到背后的命令。
但是使用 Mel 其实有更加 Python 的方法,那就是利用
pymel
来实现。
Pymel 调用 Mel
1 | import pymel.core as pm |
直接 pymel.mel 就可以直接类似调用 Python 方法的方式调用 mel 函数。
返回结果和 mel.eval 方法类似。
1 | import pymel.core as pm |
从上面可以看到 mel 是 pymel language.py 脚本中的
Mel
类实例。
这个脚本当中还有env
和melGlobals
的实例
env 的调用可以参考文档链接
melGlobals 这个实例则可以获取到 mel 的全局变量。
1 | import pymel.core as pm |
利用上面的方法,可以完全用 Python 的调用方式来获取 Mel 才能调用的功能。
附注: Mel how to 文档有获取 Mel 所有全局变量的方法 链接
1 | string $allGlobals[] = `env`; |
Pymel 运行 Mel 原理
Mel
类重载了__getattr__
方法。
1 | def __getattr__(self, command): |
这个方法返回
MelCallable
实例。
1 | class MelCallable(object): |
调用的时候会调用
pythonToMelCmd
方法,将函数名和参数转换为 mel 语句。
1 | import pymel.core as pm |
最后调用 Mel 类的
_eval
的方法来执行 mel 语句。
从上面的代码可以看到
OpenMaya.MGlobal.executeCommand
利用 Maya C++ API 实现 mel 代码运行
并通过上面的 返回值处理返回对应的数据。
附注: pm.mel 执行的返回都是字符串,并非是 PyNode 处理过的对象需要特别注意。
实用 Mel 命令汇总
经过上面的操作可以很方便调用 Mel 内置的函数。
因此我们可以了解一下 Mel 内置了哪些便利的函数,可以更加方便地调用,而不需要从零开始写代码。
下面的代码可能默认没有加载到 Maya 里面,直接调用可能会有找不到函数的错误提示。
这个时候可以利用 source 语句 source 脚本来解决问题
1 | pm.mel.isOnlyNurbsSelected() |
如上所示通过 source 方法就可以加载到相关的 Mel 函数了
https://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/Commands/index.html
Mel 命令文档里面有不少函数是 M 后缀的,也就是 Mel 方法特有的,正常情况下无法通过 Python 调用。
当然这些只是记录在文档里面的,还有一些藏在{Maya安装目录}/scripts/others/*.mel
这个scripts
路径下了。
翻阅里面的 Mel 可以看到很多脚本都是 98 99 写的远古 Mel 代码。
现在新工具新插件的代码都不用 Mel 而是 Python 了。
并且 Mel 有很多函数是复刻 Python 的,比如 startsWith strip dirname basename
除此之外还有很多非常 native 的用法,调用官方的写法更加省心。
copyAttrValues
copyAttrValues
全局函数选择两个物体,最后选择物体的通道选择上,会根据选择的通道同步属性值。
用这个函数复制属性值非常便利,不需要再一个值一个值地进行传递。
这个因为要选择对应的属性,更偏向于实际使用,而非代码
duplicateAttr
duplicateAttr
和上面函数的用法类似,只是可以复制属性通道。
selectedChannelBoxAttributes
1 | selectedChannelBoxAttributes; |
selectedChannelBoxAttributes
获取当前通道盒选中的属性值
selectedChannelBoxPlugs
1 | selectedChannelBoxPlugs; |
selectedChannelBoxPlugs
获取当前通道盒选中的通道插槽
selectedChannelBoxObjects
1 | selectedChannelBoxObjects; |
selectedChannelBoxObjects
注意必须是选中通道才可以获取到物体
raiseChannelBox & isChannelBoxRaised
raiseChannelBox
将通道盒置顶
isChannelBoxRaised
判断通道盒是否置顶
findRelatedDeformer
1 | import pymel.core as pm |
利用这个获取到
blendshape
等关联的变形器节点。
蒙皮节点也属于变形器的一种,因此也可以获取。
findRelatedSkinCluster
1 | import pymel.core as pm |
与上面的方法类似,只是针对获取蒙皮节点。
利用这个方法获取蒙皮节点非常省事
getFaces & getEdges & getUVs & getVerts
getFaces
getEdges
getUVs
getVerts
上面四个函数可以在不同的选择的 component 下切换。
attributeExists
1 | import pymel.core as pm |
这个函数可以非常方便地判断属性是否存在。
rootOf & longNameOf & shortNameOf
1 | import pymel.core as pm |
这些功能其实 pymel 都有所覆盖
还有isSameObject
isParentOf
就不再赘述了。
getCurrentCamera & getPanelCamera
1 | import pymel.core as pm |
利用 mel 封装的方法可以非常方便获取到当前聚焦的 ModelPanel
缺点就是脚本编辑器下聚焦不在 modelPanel 用 mel 运行获取不到具体的 modelPanel 需要做成 工具架按钮触发才可以。
getCurrentOutlinerPanel
1 | import pymel.core as pm |
获取当前聚焦的 Outliner
同样需要注意在 脚本编辑器下执行聚焦点不在 outliner 没有返回值
getSceneName
1 | import pymel.core as pm |
直接获取当前文件名(不带后缀),可以和
pm.sceneName()
方法获取文件路径相辅相成。
getAssignedMaterials & getConnectedShaders & createAndAssignShader
1 | import pymel.core as pm |
getAssignedMaterials
可以直接通过 shape 节点获取到关联的材质。
1 | whatIs getAssignedMaterials; |
需要注意这个函数在
unsupported
目录下,默认不加载,需要 source 文件,也不排除哪天就没了(:з」∠)
所以我推荐另一个自带的方法getConnectedShaders
1 | import pymel.core as pm |
getConnectedShaders
可以快速获取模型的 shadingEngine 节点,通过这个节点可以找到关联的材质。shadingDependNode
是所有渲染节点的基类,囊括了所有的材质以及一些不搭噶的渲染节点。
1 | import pymel.core as pm |
getConnectedShaders
第二个参数为 True 时可以获取到详细关联 shadingEngine 和 component 的数据。
gotoBindPose
1 | import pymel.core as pm |
绑定下可以利用这个命令回到 bindpose 状态。
deleteIfNotReferenced
1 | import pymel.core as pm |
更加安全的删除命令,无法删除的物体返回 0
replaceNode
https://ilmvfx.wordpress.com/2014/01/27/how-do-i-change-a-materials-type-from-lambert-to-blinn-for-example-using-mel-chs/
参考上面的链接 Mel How-to 系列
这个命令就是实现材质替换的功能,不过理论上可以替换任何 Maya 节点。
1 | import pymel.core as pm |
上面执行生成了两个 方块, 因为利用
replaceNode
将 球体生成节点 替换为了 方块生成节点。
总结
以上就是关于 pymel 的用法探索,有机会还要加一篇文章分析 pymel 的实现原理,这个库简直是神器。
虽然 pymel 存在一定的性能问题,但是它的背后其实都是调用 OpenMaya1.0 的API,如果在高性能情况下用 pymel 转换为 C++ 的方式调用,性能未必就差的,pymel 有内置的 api 转换方法,有机会也补充一下~