10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MayaAdvent Calendar 2021

Day 3

maya pythonの4つの書き方(pymel, maya.cmds, API 1.0, API 2.0)による頂点処理の比較

Last updated at Posted at 2021-09-12

はじめに

mayaでpythonを使う時に、色んなモジュールの選択肢がありますね。

主によく使うのがこの4つの方法

  • pymel.core (pymel)
  • maya.cmds (mc)
  • maya.OpenMaya (maya python API 1.0)
  • maya.api.OpenMaya (maya python API 2.0)

それぞれの方法についての説明は割愛しますが、mayaの公式サイトなどですでに詳しいドキュメントが書かれています。

maya python APIについては公式サイトに書いてあります
https://help.autodesk.com/view/MAYAUL/2022/JPN/?guid=Maya_SDK_Maya_Python_API_html

mayaでのpythonのAPIは1.0と2.0がありますが、その違いについてはこちら https://help.autodesk.com/view/MAYAUL/2022/JPN/?guid=Maya_SDK_Maya_Python_API_PythonAPI1VsPythonAPI2_html

maya.cmdsの公式ドキュメントは
https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/CommandsPython/index.html

pymelは
https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/PyMel/index.html

pymelについてこの本もとても参考になります https://www.amazon.co.jp/gp/product/B079Z121TB/

書き方の比較

まずは書き方の違いを見せるために例を挙げたいと思います。

ここで使うのはmaya2022でpython3ですが、maya2021以前でのpython2とはあまり変わらないはずです。

例えばこんな5面ピラミッドの頂点の位置を取得したい場合です。

書き方を比べてみます。

pymel

まずは一番書きやすいpymelです。全部の頂点の位置を取得したい時に.getPoints()というメソッドがあるので書き方は簡単です。

import pymel.core as pm

mesh = pm.ls(sl=1)[0]
x = mesh.getPoints()
print(x)

結果はdt.Pointというxyzの位置を収めるクラスのオブジェクトのリストです。頂点が6なので、dt.Pointが6つ。

[dt.Point([1.0514626502990723, -1.051462173461914, -3.2360682487487793]), dt.Point([-2.7527637481689453, -1.051462173461914, -2.000000238418579]), dt.Point([-2.7527639865875244, -1.051462173461914, 1.9999998807907104]), dt.Point([1.051462173461914, -1.051462173461914, 3.2360680103302]), dt.Point([3.4026031494140625, -1.051462173461914, 0.0]), dt.Point([0.0, 1.051462173461914, 0.0])]

maya.cmds

次にmaya.cmdsはxformという関数があります。照会モードで使うと頂点や面やエッジの位置は簡単に取得できます。
詳しくは https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/CommandsPython/xform.html

import maya.cmds as mc

mesh = mc.ls(sl=1)[0]
x = mc.xform(mesh+'.vtx[*]',q=1,t=1)
print(x)

結果は6の頂点のxyzの位置を収めるリスト。長さは6×3=18

[1.0514626502990723, -1.051462173461914, -3.2360682487487793, -2.7527637481689453, -1.051462173461914, -2.000000238418579, -2.7527639865875244, -1.051462173461914, 1.9999998807907104, 1.051462173461914, -1.051462173461914, 3.2360680103302, 3.4026031494140625, -1.051462173461914, 0.0, 0.0, 1.051462173461914, 0.0]

API 1.0

そして一番古くて面倒くさい書き方であるAPI 1.0です。

import maya.OpenMaya as om1

sll = om1.MSelectionList()
om1.MGlobal.getActiveSelectionList(sll)
dagpath = om1.MDagPath()
sll.getDagPath(0,dagpath)
mesh = om1.MFnMesh(dagpath)
x = om1.MPointArray()
mesh.getPoints(x)
print(x)

結果はMPointArrayオブジェクト。

<maya.OpenMaya.MPointArray; proxy of <Swig Object of type 'MPointArray *' at 0x000002A1B1CD5960> >

ただしこのMPointArrayの中には頂点の位置を収めていますが、全体printしてみても中の値が表示されません。一つずつprintしないと見られません。

print(x[0][0]) # 1.0514626502990723
print(x[0][1]) # -1.051462173461914
print(x[0][2]) # -3.2360682487487793

API 2.0

API 2.0はAPI 1.0から改善されたもので、書き方は似ていますが、大分楽になります。

import maya.api.OpenMaya as om2

sll = om2.MGlobal.getActiveSelectionList()
mesh = om2.MFnMesh(sll.getDagPath(0))
x = mesh.getPoints()
print(x)

結果はAPI 1.0と同じくMPointArrayオブジェクトですが、printするとすぐ中の値が見えて便利です。

[maya.api.OpenMaya.MPoint(1.0514626502990722656, -1.0514621734619140625, -3.2360682487487792969, 1), maya.api.OpenMaya.MPoint(-2.7527637481689453125, -1.0514621734619140625, -2.0000002384185791016, 1), maya.api.OpenMaya.MPoint(-2.7527639865875244141, -1.0514621734619140625, 1.9999998807907104492, 1), maya.api.OpenMaya.MPoint(1.0514621734619140625, -1.0514621734619140625, 3.2360680103302001953, 1), maya.api.OpenMaya.MPoint(3.4026031494140625, -1.0514621734619140625, 0, 1), maya.api.OpenMaya.MPoint(0, 1.0514621734619140625, 0, 1)]

できた結果の形式はそれぞれ違うのですが、結果として頂点の位置を示す6×3=18の数字が得られます。

概観

書きやすさから見るとこうなるでしょう。
pymel > maya.cmds > API 2.0 >> API 1.0

ただし、速度の方が一般的に言うとこうなります。
API 2.0 > API 1.0 >> maya.cmds > pymel

同じことをする時に、pymelとmaya.cmdsの方は書きやすいのですが、速度はAPI 2.0とAPI 1.0の方が数十倍や百倍速いです。

API 1.0は古い方法で書き方が冗長で面倒くさくてあまりpythonらしくないです。

それに比べてAPI 2.0は一番新しいもので、API 1.0よりpythonらしい書き方で扱いやすくなるだけでなく、速度も随分速くなります。

頂点の位置を取得する時の速度の比較

次は実際に各方法で実行して時間を計ってみます。

速度を比較するために、今回はこのような無駄にハイポリな螺旋で試します。

mc.polyHelix(c=6,h=4,w=4,r=0.1,sa=12,sco=120)

頂点の数は8652です。どう見てもやりすぎですね。

ここで全部の頂点の位置を100回くらい繰り返して取得するという無駄な行動を実行してかかる時間を計ります。

import time
import pymel.core as pm
import maya.cmds as mc
import maya.OpenMaya as om1
import maya.api.OpenMaya as om2

n = 100 # 百回繰り返す

t0 = time.time()
for i in range(n):
    poly = pm.ls(sl=1)[0]
    x = poly.getPoints()
print('pymel: %.5f秒'%(time.time()-t0))

t0 = time.time()
for i in range(n):
    poly = mc.ls(sl=1)[0]
    x = mc.xform(poly+'.vtx[*]',q=1,t=1)
print('maya.cmds: %.5f秒'%(time.time()-t0))

t0 = time.time()
for i in range(n):
    sll = om1.MSelectionList()
    om1.MGlobal.getActiveSelectionList(sll)
    dagpath = om1.MDagPath()
    sll.getDagPath(0,dagpath)
    mesh = om1.MFnMesh(dagpath)
    x = om1.MPointArray()
    mesh.getPoints(x)
print('API 1.0: %.5f秒'%(time.time()-t0))

t0 = time.time()
for i in range(n):
    sll = om2.MGlobal.getActiveSelectionList()
    mesh = om2.MFnMesh(sll.getDagPath(0))
    x = mesh.getPoints()
print('API 2.0: %.5f秒'%(time.time()-t0))

結果

pymel: 6.56777
maya.cmds: 1.60982
API 1.0: 0.01965
API 2.0: 0.00201

こうやって見ると、時間の違いは圧倒的です。

何回やっても速度の差は同じくらいで明白です。

ポリゴンの頂点をそれぞれ移動する処理の速度の比較

次はもっと実用的な例を挙げます。

例えばポリゴン球を弄ってこんなものを作ってみたいと思います。

方法は一つずつの頂点をその元の位置によって移動させて形を作るのです。

この4の方法で作成して時間を比較します。

import math,time
import pymel.core as pm
import maya.cmds as mc
import maya.api.OpenMaya as om2
import maya.OpenMaya as om1

pm.polySphere(r=10,sx=180,sy=30)
t0 = time.time()
mesh = pm.ls(sl=1)[0]
xyz = mesh.getPoints()
for i,t in enumerate(xyz):
    w = 1+0.2*math.cos(math.atan2(t[0],t[2])*8)
    xyz[i] = t[0]*w,t[1]*(100-t[1]**2)/100,t[2]*w
mesh.setPoints(xyz)
print('pymel: %.5f秒'%(time.time()-t0))

mc.polySphere(r=10,sx=180,sy=30)
t0 = time.time()
mesh = mc.ls(sl=1)[0]
xyz = mc.xform(mesh+'.vtx[*]',q=1,t=1)
for i in range(int(len(xyz)/3)):
    t = xyz[i*3:(i+1)*3]
    w = 1+0.2*math.cos(math.atan2(t[0],t[2])*8)
    mc.scale(w,(100-t[1]**2)/100,w,'.vtx[%d]'%i)
print('maya.cmds: %.5f秒'%(time.time()-t0))

mc.polySphere(r=10,sx=180,sy=30)
t0 = time.time()
sll = om1.MSelectionList()
om1.MGlobal.getActiveSelectionList(sll)
dagpath = om1.MDagPath()
sll.getDagPath(0,dagpath)
mesh = om1.MFnMesh(dagpath)
xyz = om1.MPointArray()
mesh.getPoints(xyz)
for i in range(xyz.length()):
    t = xyz[i]
    w = 1+0.2*math.cos(math.atan2(t[0],t[2])*8)
    xyz.set(om1.MPoint(t[0]*w,t[1]*(100-t[1]**2)/100,t[2]*w),i)
mesh.setPoints(xyz)
print('API 1.0: %.5f秒'%(time.time()-t0))

mc.polySphere(r=10,sx=180,sy=30)
t0 = time.time()
sll = om2.MGlobal.getActiveSelectionList()
mesh = om2.MFnMesh(sll.getDagPath(0))
xyz = mesh.getPoints()
for i,t in enumerate(xyz):
    w = 1+0.2*math.cos(math.atan2(t[0],t[2])*8)
    xyz[i] = om2.MPoint([t[0]*w,t[1]*(100-t[1]**2)/100,t[2]*w])
mesh.setPoints(xyz)
print('API 2.0: %.5f秒'%(time.time()-t0))

結果

pymel: 0.09283
maya.cmds: 0.49966
API 1.0: 0.01959
API 2.0: 0.01200

この場合でも相変わらずAPI 2.0が一番速いです。

API 2.0の書き方はpymelとはほとんど変わらないのに、速度は8倍くらいの差です。

今回maya.cmdsが一番遅いのは、.setPoints()みたいに全ての頂点を一括として位置を決める関数がないからです。

もしmaya.cmdsと同じような書き方をpymelで書いたら、pymelの方が遅いです。

つまりpymelとmaya.cmdsはどちらも書きやすいが、高速とは言えません。一般に言うとpymelの方がmaya.cmdsより遅いようですが、やりたいことによってmaya.cmdsより速いという場合もあります。

##numpyと一緒に使ってみたら

おまけに、maya2022ではpipでnumpy, pandas, sklearn, cv2など便利なモジュールを簡単にインストールできるので、mayaに使うのも便利です

例えば上述の例はAPI 2.0をnumpyと一緒に使ってみます。

import time
import maya.cmds as mc
import maya.api.OpenMaya as om2
import numpy as np

mc.polySphere(r=10,sx=180,sy=30)
t0 = time.time()
sll = om2.MGlobal.getActiveSelectionList()
mesh = om2.MFnMesh(sll.getDagPath(0))
xyz = mesh.getPoints()
xyz = np.array(xyz).T
w = 1+0.2*np.cos(np.arctan2(xyz[0],xyz[2])*8)
xyz = np.stack([xyz[0]*w,xyz[1]*(100-xyz[1]**2)/100,xyz[2]*w]).T
xyz = om2.MPointArray(xyz)
mesh.setPoints(xyz)
print('API 2.0 + numpy: %.5f秒'%(time.time()-t0))

API 2.0のMPointArrayは色々numpyのndarrayと似ていますが、numpyのndarrayみたいに直接計算に使ったり転置したりすることはできないのでまだちょっと不便です。

numpyの配列に変換するとforを使わずに計算することができて便利です。

ただし変換に時間もかかるので、全体見ると速度はやっぱりAPI 2.0だけ使う時と比べて少し遅くなります。

まとめ

結果から見れば、同じことをする時に、何をしようとしてもAPI 2.0が一番速いです。なのでできるだけAPI 2.0を使った方がいいようです。

pymelとmaya.cmdsは便利な関数が整って書きやすいので、時間に気にする必要がない場合pymelとmaya.cmdsを使った方いいですが、速度はAPI 1.0とAPI 2.0の方が圧倒的に速いです。

API 1.0は古いもので書きにくい上に、速度もAPI 2.0より遅いので、今のところあまり使う理由がないかもしれません。ただしAPI 1.0のできることはAPI 2.0より多いようです。

なのでAPI 2.0のできないことはまだAPI 1.0を使う必要がありますが、API 2.0のできることなら今更API 1.0を使ってもあまりメリットがないでしょう。

以上説明した4の方法の他にも、まだ色々できる手段があるようです。例えばcymel, cmdx, metan 詳しくはこの記事 https://qiita.com/ryusas/items/eba6ab1b5af0700799e6

ただしどれもまだあまり普及というほどではないようです。速度もやはりpymelより速くても、API 2.0には敵いません。

違う書き方の比較は、他にもこの記事など https://qiita.com/ktmmilk/items/c88041b654f84b8498ca

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?