前言

  最近被工作压垮,没有时间学习和研究新的东西,博客也停更了不少的时间(:з」∠)
  5月份只能更新一篇文章,还是因为各种催稿的压力,只好挤牙膏式地更新。
  ε=(´ο`*))),上个月的文章有说过要来讲解一下 pymel 的一些用法,对比 cmds 的好处和坏处。
  这次算是姗姗来迟地填坑吧

  填坑之前决定先介绍一下 Maya 以及相关库的一些前世今生,想一下也够写上一篇文章了(:з」∠)
  关于 OpenMaya 以及 cmds mel pymel 这些内置 Python 库的关系,很久之前我也写过他们之间的关系 链接
  只是藏得比较深,所以这篇文章我重新梳理一下他们之间的关系。

Maya Mel & Python

  Maya 安装之后又两种不同的编程语言 Mel & Python | mel 百度百科
  为何 Maya 需要配置 Mel 和 Python 这些动态语言呢。
  主要是为了方便第三方扩展和辅助开发,动态语言开发工具快速便捷,方便调试,不容易导致软件崩溃。
  所以上古版本的 Maya 包括当时诸多类似的程序都有动态语言扩展的需求。

  用 C++ 扩展软件的功能,成本和周期都显得过高,
  Maya 最古老的版本可以追溯到 1998 年甚至更早,那个时候 Python 还没有那么流行,所以那个年代不同 DCC 软件会选择各种不同的动态编程语言作为辅助开发。

alt
出处 https://www.bilibili.com/video/BV1dJ411571P?p=3&t=60

  比如说 3dsMax 开发了 MaxScript , Nuke 选择了 Tcl ,而 Maya 自个用 Mel 语言。
  后来 Python 作为 C++ 环境的胶水语言,其简单易用方便嵌入进行扩展调用,并且还有极其强大的生态,于是大量被各种软件库集成作为软件函数库的动态语言。

alt
出处 https://www.bilibili.com/video/BV1Bt411f7hS?p=10&t=100

  如今才能看到 Python 几乎一统天下,大部分常用的 DCC 软件都支持它了。

alt
出处 https://www.bilibili.com/video/BV1Kt411D7ca?p=1&t=180

  在目前的大环境下, Mel 语言因为其面向过程,功能单一 等多种缘由,已经越来越少被使用,如今的新插件亦或是 Autodesk 官方推出的新功能都已 Python 为主。

alt
出处 https://www.bilibili.com/video/BV1dJ411571P?p=3&165

  Python 支持 subprocess socket 等功能,这些都是 Mel 所不具备的,如果没有 Python 就只能通过 C++ 来扩展,会非常麻烦。
  除了有过去的 Mel 代码需要维护之外,已经很少还有人编写 Mel 代码了,意外地想起 … 普天同庆 病毒是 Mel 代码编写的 (:з」∠)


  虽然说 Mel 已经很少人使用,但是了解 Mel 依然是学习 Maya 编程很重要的一部分,这也是为什么我上一篇要写 Pymel 来调用 Mel 的文章 链接
  升级 Python 之后 Maya 将 Mel 相关的调用以 Python 函数的形式封装到了 cmds 库
  这也是为什么 Mel 和 cmds 有大量的命令都是相通的,而绝大部分不相通的 Mel 命令,很大一部分都是 Mel Procedural 所以无法相通到 Python (当然解决方案就是利用 Pymel 的封装来 Pythonic 调用 Mel,参考上一篇文章)

whatIs
出处 http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/Commands/index.html

  如果用 whatIs 命令查询到的 Mel 命令为 Command 那就是来自于 Maya C++ 底层,可以在 Mel 和 Python 之间互通(就只有 whatIs 这个奇葩不互通)
  那么 Maya 上层 和 底层是怎样的呢?

Maya 架构

alt
出处 https://www.bilibili.com/video/BV1dJ411571P?p=2&t=170
文档地址 https://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_API_Introduction_htm

  从上图可以看到,最上层的调用有 Mel Python 以及 C++&C# 扩展
  中间层则有 Maya commands 和 Maya API
  最底层是 Maya 的 内核

alt
出处 https://www.bilibili.com/video/BV19t411c7Vj?p=2&t=1578

  从上面的 Autodesk 官方教程可以看到 Maya Core 下面还对接 OS 层。
  从文档的描述可以确定 Maya Commands 就是 Mel 文档里面的每个指令,可以利用 C++ 对 command 进行扩展,如上图的 Custom Maya Command

alt
出处 https://www.bilibili.com/video/BV1dJ411571P?p=2&t=233

  上面提及利用 command 可以用来编写 Maya 界面, command 是 Mel 的核心。

alt
出处 https://www.bilibili.com/video/BV1dJ411571P?p=2&t=240

  Maya API 用来开发 Maya 的 C++ 插件,同时可以通过 Python API 1.0 和 2.0 (即 OpenMaya) 进行调用。

alt
出处 https://www.bilibili.com/video/BV1Bt411f7hS?p=8&t=200

  另一个教程对 Mel 和 Python 以及 C++ 的各种优劣都有较为详细的介绍。
  其中 Mel 在 Python 面前毫无优势可言,而 C++ 则有效率高的优点,就是使用起来很繁琐。

alt
出处 https://www.bilibili.com/video/BV164411f758?p=3&t=300

  上面是 C++ 和 Python 开发两者的比较。
  我个人的开发经验是先用 Python OpenMaya1.0 开发,如果实属高性能环境。
  就将 OpenMaya1.0 代码转换为 C++ 代码,由于两者调用很相近,转换过去相对简单。

alt
出处 https://www.bilibili.com/video/BV1Bt411f7hS?p=8&t=326

  上图则比较了 Maya 内置的三个模块的 区别。
  pymel 是兼顾 cmds 和 OpenMaya 的模块,缺点就是效率有限制,我会在下一篇文章提供一些低效的解决方案。


  Python 横跨 Mel 命令和 C++ 扩展的功能,而且动态使用无需编译,简直全能得离谱。

alt

OpenMaya 介绍

  OpenMaya下有两个不同的版本,分别为 OpenMaya 1.0 和 OpenMaya 2.0

附注:mottosso 大神在做 3.0 了 https://github.com/mottosso/cmdc
官方不给力,只能靠社区才能把东西做好

  OpenMaya 1.0 是通过 工具 将 C++ API 自动转换到 Python 下调用。

文档 https://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_GUID_EC669155_DD0C_4072_A0DC_D24C7FB2AB2D_htm

  因此 OpenMaya 库的文档是直接看 C++ 对应文档的 文档链接
  我使用过的命令里面 C++ 支持的 OpenMaya 都支持,文档里面提及有少量 C++ 命令不支持 Python 调用,但是实属极个别情况,目前我还没有遇到过。

OpenMaya 1.0 的问题

alt
出处 https://www.bilibili.com/video/BV19t411c7Vj?p=10&t=637

  上述截图只是 OpenMaya 1.0 部分问题,视频里面还有代码案例作为介绍,,非清晰看到问题所在。
  OpenMaya 1.0 调用非常繁琐,特别不符合 Python 开发者的使用习惯,某些函数需要让 Python 来传递指针。
  为此 Maya C++ 提供了 MScriptUtil ,虽然能够调用起来,但是使用体验会很差。
  并且 1.0 的自动转换没有性能优化,导致无谓的性能开销。

  基于上述的问题, OpenMaya 2.0 诞生了。

2.0 文档 https://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__py_ref_classes_html

alt
出处 https://www.bilibili.com/video/BV19t411c7Vj?p=10&t=1963

  API 2.0 打算纯手工重写 Python 的调用来提高性能,并且让调用更符合 Python 的习惯。
  然而这个 API 从 Maya 2012 引入,到 Maya 2019 都还没有完善好(:з」∠)

alt
出处 https://www.bilibili.com/video/BV19t411c7Vj?p=10&t=2443

  然而 OpenMaya 2.0 不会做 面向对象 的绑定。
  用法依然和 1.0 大差不差的,只是传参各方面会好很多。

1
2
3
4
5
6
from maya import cmds
# OpenMaya 1.0
from maya import OpenMaya
# OpenMaya 2.0
from maya.api import OpenMaya as om
# 官方推荐用 缩写 om 来区分 1.0 和 2.0

  关于 OpenMaya 和 C++ 开发可以去下载 Maya 的 Maya devkit 网址

  下载解压之后 devkitBase\devkit\plug-ins\scripted 路径有很多 基于 OpenMaya 1.0 和 2.0 的官方脚本。
  有时候看代码比看教程学得还快。
  如果是 C++ 的案例的话则看这个路径 devkitBase\devkit\plug-ins
  Maya 开发比较小众,但是官方提供的案例还是非常靠谱的。

pymel 永远滴神

  在 2008 年, Maya 刚引入 Python 没多久之后。
  由于 cmds 和 OpenMaya 都没有提供 对象 属性 等的面向对象编程能力。
  于是 Luma Pictures 公司的开发团队开发了 pymel 来简化开发。

  何为面向对象编程能力呢? 具体可以学习 pymel 文档 Why Pymel

  长话对说就是, Maya Mel 命令下执行的指令,一切的物体对象都是以字符串走的。

1
2
3
4
5
6
7
8
9
from maya import cmds
# NOTE 获取第一个相机
cam = cmds.ls(type='camera')[0]
# Result: u'frontShape' #
# NTOE 获取相机的 transform 节点
transform = cmds.listRelatives(cam,p=1)[0]
# Result: u'front' #
tx = cmds.getAttr(transform + ".tx")
# Result: 0.0 #

  如果使用 cmds 的方式,所有的调用都得拆开,因为和 Mel 语言一样 面向过程,需要不断调用相应的 函数 来触发功能。

1
2
3
4
5
import pymel.core as pm
cam = cmds.ls(type='camera')[0]
transform = cam.getParent()
tx = transform.tx.get()
# Result: 0.0 #

  面向对象开发,所有的函数调用都会后置,代码更加易读,理解更方便。

alt
出处 https://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/PyMel/design.html

  pymel 的开发哲学就是修复 cmds 的 BUG
  不会再数组返回时返回 None
  给 节点 和 属性 创建对应的面向对象类。
  这些新建的类可以接入 OpenMaya 的功能,让 pymel 的功能远超 cmds

1
2
3
4
5
import pymel.core as pm
cam = pm.PyNode("persp")
print(type(cam))
# Result: <class 'pymel.core.nodetypes.Transform'> #
print(dir(cam))

  而这个 Transform 节点包含下列众多方法。

alt

  由于 Maya 所有的调用都是字符串, pymel 还包含了字符串的处理命令,还有诸如 longName objExists 等命令。
  调用起来更加清晰。

  其次还有 __apimdagpath__ 等等的方法可以直接获取到节点对应 OpenMaya 1.0 的对象
  以此来调用 OpenMaya 进行更高效的处理。

1
2
3
4
5
6
7
8
import pymel.core as pm
from maya import OpenMaya
sphere, = pm.polyCube(ch=0)
shape = sphere.getShape()
mesh = shape.__apimfn__()
# Result: <maya.OpenMaya.MFnMesh; proxy of <Swig Object of type 'MFnMesh *' at 0x0000020E80DC2480> > #
dag = shape.__apimdagpath__()
# Result: <maya.OpenMaya.MDagPath; proxy of <Swig Object of type 'MDagPath *' at 0x0000020E8131CF00> > #

  pymel 做好了 cmds 到 OpenMaya 之间的转换,这样可以很方便在两个 API 之间横跳。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import time
from functools import wraps
import pymel.core as pm
from maya import OpenMaya

def log_time(func):
@wraps(func)
def wrapper(*args,**kwargs):
t0 = time.time()
res = func(*args,**kwargs)
print("[%s] elapsed time : %s" % (func.__name__,time.time() - t0))
return res
return wrapper

@log_time
def pymel_itr():
sphere, = pm.polySphere(ch=0)
pos_list = []
for v in sphere.verts:
pos_list.append(v.getPosition())

@log_time
def openmaya_itr():
sphere, = pm.polySphere(ch=0)
shape = sphere.getShape()
dag = shape.__apimdagpath__()
itr = OpenMaya.MItMeshVertex(dag)
pos_list = []
while not itr.isDone():
pos_list.append(list(itr.position()))
itr.next()

pymel_itr()
openmaya_itr()

# [pymel_itr] elapsed time : 0.132999897003
# [openmaya_itr] elapsed time : 0.00699996948242

  上述的例子可以很直观看到 pymel 在遍历数据的劣势。
  究其原因是 pymel 需要给每个顶点对象生成相应实例,如果直接 遍历 物体 所有的点,甚至会比 cmds 还要慢。

补充: pymel 获取顶点的时候,背后会拼装 mel 的字符串,顶点为例 pSphere1.vtx[315] ,在通过这个字符串调用 OpenMaya 的命令进行信息获取,所以在这个过程中会多次使用 OpenMaya 对多个顶点字符串进行解析查询,然而这个无谓的字符串解析查询其实很低效的。就好比查询数据库的时候一次拉取大量的数据下来,还是一条条数据逐个查询,显然如果拆得很碎的话,每个查询都要进行 http 请求,显然就会慢得不可思议,不过 mel 本质也是解析字符串,所以 mel 语言也是

  但是如果获取 mfnmesh 的 OpenMaya 对象,再用相应的 MIt 命令遍历,那就快了很多。
  所以说 pymel 运用妥当,效率并不低,只要不去罗列大量的物体或者组件比如说顶点。

  pymel 是在 08 09 年的时候开发的,那个时候还没有 Python API 2.0
  所以 pymel 底层都是用 Python API 1.0 实现的,也提供了很多方法直接调用底层的 API 比如 isReferenced 等方法。
  可以节省大量的代码。

界面开发 PySide Vs cmds & mel

  我上一篇文章有提到 Maya 内置了一大堆 Mel 脚本 {Maya安装目录}/scripts
  这些 Mel 脚本其实有很大的部分是用来构建窗口的,很多老窗口比如 蒙皮设置窗口 等都是用 Mel 命令构建的。
  同样可以用 cmds 库,利用 Python 的方式来构建。
  甚至可以在这个基础上做面向对象的封装。
  界面开发应该用 PySide2 还是用 cmds 呢?

https://blog.l0v0.com/posts/2c06616b.html#MEL-amp-cmds-VS-PySide-%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2%E5%BC%80%E5%8F%91

  上面是我博客 Python结合Qt系列开发教程 的对比。
  PySide2 能做到的 cmds 也可以实现。

  然而事实上如今用 cmds 开发界面的人也越来越少了。
文档出处 https://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_GUID_D6567F97_012D_4F45_B252_C3112EBAE859_htm
  这主要是 Maya2011 之后接入了 Qt 图形框架。
  过去利用 Mel 或者 cmds 组装界面背后是调用了 各自 平台的图形框架。
  需要付出极大的人力物力去维护不同平台上的 BUG。
  Qt 框架支持跨平台统一,越来越多 DCC 软件都选择了 Qt 框架作为图形底层。
  并且 Python 生态有 PySide 和 PyQt 两个库,实现 Python 调用。
  只要学会 Qt 的 API ,就可以在各个不同软件上开发界面,一套技术解决不同软件的问题。

  于是在开发上,直接使用 Python Qt 框架有两个无与伦比的好处。

  1. 可以跨软件兼容界面
  2. 可以利用 QtDesigner 快速开发 (当然 cmds 也有 loadUi 功能)

  重点还是在第一点上,开发一个界面就可以在不同软件上通用,这可是非常节省开发成本的。
  成熟的流程软件注入 shotgun 都是用这个套路。
  我在 Unreal 开发界面也直接用 Qt 界面省去我研究 Unreal 界面的开发成本,同时可以让不同软件的体验保持一致。

  关于 Python Qt 框架在 Maya 的使用可以参阅 Maya 文档 链接
  上述文档有很详细的 对比,也列出了相应的代码案例来演示如何在 Maya 创建 Qt 的 UI 界面。

Maya 编程学习资源

CGcircuit Maya Python API
CGCircuit Introduction to the Maya API
Autodesk - Maya API Training Webcasts
Python For Maya - Artist Friendly Programming
python for maya with Justin Israel
3DMotive - Introduction to Scripting in Maya

Digital-Tutors Transferring Animation with MEL
Digital-Tutors Artist’s Guide to MEL in Maya
Digital-Tutors Getting Started with MEL in Maya
Digital Tutors Enhancing Your Maya Toolset with MEL

  这里列出的大部分教程都看过了,Chris Zurbrigg 系列后来才拿到的反而没怎么看,工作了就没有时间去看教程了o(╥﹏╥)o
  缅怀一下自己读大学,真就把教程当动漫看的时光。

扩展 Maya 第三方包

  1. 绑定

AdvanceSkeleton
mGear
ngSkinTool

  1. 动画

MGtools
animBot
springMagic
studiolibrary

  1. 模型

instalLOD
QuadRemsher

  1. 日常

Cosmos
siShelf

  1. 编程

Charcoal Editor
Coder2
mpdb

代码扩展库

https://github.com/mottosso/cmdx
https://github.com/mottosso/cmdc
https://github.com/mottosso/qargparse.py
https://github.com/theodox/minq
https://github.com/robertjoosten/maya-orm

  maya 一些底层库, mottosso 大佬独占鳌头。

总结

  总算是经过千辛万苦,把文章更新完了。
  下期抽空剖析一下 Pymel 的底层原理。