株式会社デジタル・フロンティア-Digital Frontier

Header

Main

  • TOP
  • DF TALK
  • MayaPythonScriptを比較してみる

MayaPythonScriptを比較してみる

2014/4/21

Tag: ,,,

こんにちは。TDチームの石田です。
今回はMayaのスクリプト(主にpython)に関して書いていこうと思います。

— Mayaで使えるスクリプトについて —

Maya標準で使用できるスクリプトを挙げますと maya.cmds、OpenMaya、API2.0、pymel、mel といろいろありますよね。
個人的なのですがここ最近の使用頻度は OpenMaya == maya.cmds >>>> mel > pymel > API2.0(使ってないw)
となってきました。
今回はPySide,PyQtは置いときます。ちなみにUI作成はPySideがほとんどです。

最近スクリプトの勉強を始めた方、もしくは始めようとしてる方はどれから始めたらいいの?と迷っているかと思います。
正直言うと、コレだ!って言えないのですよね。
それぞれいいとこもあれば微妙なところもある。今回は比べてみたので参考にしてみてください。

ただmelとpythonで分けると断然pythonです。
pythonは使用できるモジュールも多くMayaだけに縛られずいろいろな方面で使用されています。
多機能だけど文法はシンプル。個人的にも出来る事が増えました。

— 比べてみよう —

上記であげたmayaで使用できるpythonのサンプルを書いて比べてみました。melは省きます。

内容

    ● maya.cmds、OpenMaya、API2.0、pymel(maya.cmds)、pymel(OpenMaya)、mel(処理速度だけ)、c++ を比べる。
    ● ポリゴンスフィア:9902vtxの全ポジションの値を取るscriptをそれぞれ用意。
    ● 書き方の違い、処理速度の違いを比較

それでは先に、以下サンプルドン!!

import maya.cmds as cmds
import maya.OpenMaya as om
import maya.api.OpenMaya as om2
import pymel.core as pm
#---------------------------------------------------------------------
# それぞれの処理内容は全てのバーテックスのワールドポジションのリストを返します。
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# 速度計算処理
# デコレータ定義
#---------------------------------------------------------------------
import time
def testTime(func):
    def start(*args):
        print '--'*14
        print args[1] + ' >> ' + args[0]
        st = time.time()
        posAry = func(args[0])
        ed = time.time() - st
        print 'total : ' + str(ed)
        print '--'*14
        return posAry
    return start

#---------------------------------------------------------------------
# maya.cmds
#---------------------------------------------------------------------
@testTime
def maya_cmds(obj):
    posAry = []
    for vtx in cmds.ls(obj+'.vtx[*]',fl=1):
        posAry.append(cmds.xform(vtx,q=1,t=1,ws=1))
    return posAry

#---------------------------------------------------------------------
# maya.OpenMaya
# OpenMayaではイテレータが使用できますが、API2.0で使用出来ないためMFnMeshを使いました。
#---------------------------------------------------------------------
@testTime
def maya_OpenMaya(obj):
    posAry = []
    sel = om.MSelectionList()
    sel.add( obj )
    dagPath = om.MDagPath()
    sel.getDagPath( 0, dagPath )
    meshFn = om.MFnMesh(dagPath)

    pts = om.MFloatPointArray()
    meshFn.getPoints(pts,om.MSpace.kWorld)
    for id in xrange(pts.length()):
        posAry.append([pts[id].x,pts[id].y,pts[id].z])
    return posAry

#---------------------------------------------------------------------
# maya.OpenMaya
# イテレータも試してみました。
#---------------------------------------------------------------------
@testTime
def maya_OpenMayaIter(obj):
    posAry = []
    sel = om.MSelectionList()
    sel.add( obj )
    dagPath = om.MDagPath()
    sel.getDagPath( 0, dagPath )
    meshIter = om.MItMeshVertex(dagPath)

    while not meshIter.isDone():
        pos = meshIter.position(om.MSpace.kWorld)
        posAry.append([pos.x,pos.y,pos.z])
        meshIter.next()
    return posAry

#---------------------------------------------------------------------
# maya.api.OpenMaya
# maya.OpenMayaをAPI2.0に書き換えると下記になります。
#---------------------------------------------------------------------
@testTime
def maya_OpenMaya2(obj):
    posAry = []
    sel = om2.MSelectionList()
    sel.add( obj )
    dagPath = sel.getDagPath(0)
    meshFn = om2.MFnMesh(dagPath)

    pts = meshFn.getPoints(om2.MSpace.kWorld)
    for pos in pts:
        posAry.append([pos.x,pos.y,pos.z])
    return posAry

#---------------------------------------------------------------------
# pymel.core
# maya.cmdsのwrapper
#---------------------------------------------------------------------
@testTime
def maya_pymel(obj):
    posAry = []
    for vtx in pm.ls(obj+'.vtx[*]',fl=1):
        pos = pm.xform(vtx,q=1,t=1,ws=1)
        posAry.append(pos)
    return posAry

#---------------------------------------------------------------------
# pymel.core
# maya.OpenMayaのwrapper
#---------------------------------------------------------------------
@testTime
def maya_pymel_om(obj):
    posAry = []
    mesh = pm.PyNode(obj)
    for vtx in mesh.vtx:
        pos = vtx.getPosition()
        posAry.append([pos.x,pos.y,pos.x])
    return posAry

#---------------------------------------------------------------------
# 実行
# ポリゴンスフィア:9902vtx
#---------------------------------------------------------------------
obj = 'pSphere1'
maya_cmds(obj,'maya.cmds')
posAry = maya_OpenMaya(obj,'OpenMaya')
posAry = maya_OpenMayaIter(obj,'OpenMaya_Iter')
posAry = maya_OpenMaya2(obj,'API2.0')
posAry = maya_pymel(obj,'pymel_cmds')
posAry = maya_pymel_om(obj,'pymel_OpenMaya')

Mayaでポリゴンスフィアを用意してサンプルコードを実行するとそれぞれの処理速度が出ますので試してみてください。
ついでにc++のdoIt関数も載せて置きます。

MStatus mayaPluginCmd::doIt( const MArgList& args )
{
    MStatus status;

    MSelectionList selection;
    MGlobal::getActiveSelectionList( selection );
    MDagPath dagPath;

    selection.getDagPath( 0, dagPath);
    MFnMesh meshFn(dagPath);

    MFloatPointArray pts;
    meshFn.getPoints(pts , MSpace::kWorld);
    unsigned int size = pts.length();
    MDoubleArray values(size * 3);
    for(unsigned int i=0 ; i < size ; i++)
    {
        values[i * 3 ] = pts[i].x;
        values[i * 3 + 1] = pts[i].y;
        values[i * 3 + 2] = pts[i].z;
    }
    clearResult();
    setResult(values);
    return status;
}

それでは上から順番に説明していきます。
 

— 処理速度を計るスクリプト —


上記13行目からの処理
処理速度を計る。説明そのまんまです。

見慣れないものがあるかと思いますがpythonのデコレータというものを使っています。
Mayaとは関係ないですがこんな書き方もあるよってことで紹介します。

def deco(func):
    def start():
        func()#ここでtest()が実行される。
    return start

@deco
def test():
    print 'decodeco'

test()#実行
>> 'decodeco'

@decoをdef test():の前に書くと下記と同じ書き方をしているのと同じになります。

a = deco(test)
a()#実行
>> 'decodeco'

デコレータを使用せずともtestTime(なにかの関数)を入れるとその関数の処理速度を計ってくれます。
 

— maya.cmds —


上記29行目からの処理
基本的な書き方なので説明はコメントのみで。

def maya_cmds(obj):
    posAry = []
    #全vtxを取得してfor分で回す
    for vtx in cmds.ls(obj+'.vtx[*]',fl=1):
        #vtxのワールドポジションをposAryに格納
        posAry.append(cmds.xform(vtx,q=1,t=1,ws=1))
    return posAry

 

— maya.OpenMaya —


上記29行目からの処理

OpenMayaはc++みたく型を作ってあげないといけないためPythonに慣れている人はちょっと取っ付きにくいかと思います。
例えばこんな書き方になります。
pts = om.MFloatPointArray()
meshFn.getPoints(pts,om.MSpace.kWorld)

本当であれば
pts = meshFn.getPoints(om.MSpace.kWorld)
という書き方がpythonらしい書き方なのですが、OpenMayaでは出来ません。
でもAPI2.0が解決してくれてます。
説明は後ほど。

def maya_OpenMaya(obj):
    posAry = []
    sel = om.MSelectionList()
    sel.add( obj )
    dagPath = om.MDagPath()

    #selの0番目からdagPathを取得
    sel.getDagPath( 0, dagPath )

    #MFnMeshの関数セットを作成
    meshFn = om.MFnMesh(dagPath)

    #ワールドポジションを入れる為にMFloatPointArrayを作成
    pts = om.MFloatPointArray()

    #ptsにvtxの全ポジション(MFloatPoint)を入れる
    meshFn.getPoints(pts,om.MSpace.kWorld)

    #MFloatPointを[float,float,float]に変換してposAryに格納する
    for id in xrange(pts.length()):
        posAry.append([pts[id].x,pts[id].y,pts[id].z])
    return posAry

 

— maya.OpenMaya イテレータ —


上記59行目からの処理

ここではイテレータの処理部分だけ説明します。
イテレータは繰り返し処理をするための関数です。
MItと頭に付いているものがイテレータを扱えます。
ここではMItMeshVertexを使用しています。
慣れるとイテレータ便利です。

while not meshIter.isDone():#全て処理し終わったらループから出る。
    #現在のmeshIter.vtxからポジションを取得。
    pos = meshIter.position(om.MSpace.kWorld)
    #次のバーテックスへ移る。
    meshIter.next()

 

— maya.api.OpenMaya API2.0 —


上記78行目からの処理

API2.0ドキュメント

OpenMayaとの違いは
● OpenMayaでは面倒だった戻り値の取得がPythonの様に扱える。
● MScriptUtilも要らない。※MScriptUtilは説明が面倒なので省きます。
● OpenMayaより速い、らしい。
● イテレータが使えない…、まだいろいろ足りない(ここでかい)

サンプルを見てもらうとあっさりしてると思います。これから充実して行くのですかね?
 

— pymel.core maya.cmdsのwrapper と OpenMayaのwrapper —


上記95行目からの処理 と 上記107行目からの処理
pymelはまとめて説明しちゃいます。
二つ用意したのには理由があり、pymelはmaya.cmdsとOpenMayaから成り立っています。
なのでmaya.cmdsとOpenMayaの両方のように、むしろより簡単に書けます。
ただ問題は処理速度が遅いこと。繰り返し処理の時にその遅さが顕著に現れます。
pymelはとても便利なのですが遅さとバグ、仕様変更がたまにあるため個人的には避けています。
ただ繰り返し処理をしない、バグ仕様変更に対応出来る、のであればお勧めします。
Mayaでpythonを勉強する上で一番いい資料だと思います。

C:\Program Files\Autodesk\Maya20XX\Python\Lib\site-packages\pymel
にソースがあるので眺めてみると意外と勉強になります。お試しください。
 

— 速度比較結果 —


コンマ数秒しか変わりませんが何回か実行して一番早い結果で出しています。
比べるとこんな結果に。

API2.0 > c++ > OpenMaya > OpenMaya Iter > mel > maya.cmds > pymel_OpenMaya > pymel_cmds

API2.0は段違いですね。
コンパイルしなくてもいいし簡単に速度出るしAPI2.0使っていこうかな~w

c++のほうがちょっと遅い…。

API2.0とpymel_cmdsの差がなんとも…。

----------------------------
maya.cmds >> pSphere1
total : 0.591000080109
----------------------------
OpenMaya >> pSphere1
total : 0.055999994278
----------------------------
OpenMaya_Iter >> pSphere1
total : 0.0590000152588
----------------------------
API2.0 >> pSphere1
total : 0.00699996948242
----------------------------
pymel_cmds >> pSphere1
total : 14.9739999771
----------------------------
pymel_OpenMaya >> pSphere1
total : 5.72500014305
----------------------------
mel >> pSphere1
total : 0.468
----------------------------
c++ >> pSphere1
total : 0.00999999046326
----------------------------

同じ処理をしていても書き方が結構違いますよね。
今回とは違う書き方もあるのでもしかすると処理速度が変わるかもしれませんがそれほど違いは無いと思います。たぶん。

最初はOpenMayaの基本的な書き方や、OpenMayaとmaya.cmdsを使用してpymelっぽく書く小規模モジュールの書き方の方法を
紹介しようと思ったのですがボリュームが大きくなってしまったので次の機会に書こうと思います。
もしかしたらリグの記事にするかもしれませんがこの記事の反応見て決めますw

※免責事項※
本記事内で公開している全ての手法の有用性、安全性について、当方は一切の保証を与えるものではありません。
これらの手法を使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご活用ください

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

CAPTCHA


 

*