>   >  回・小松 泰のオススメ! SOuPほか、テクニカルアーティスト向けツール3選
回・小松 泰のオススメ! SOuPほか、テクニカルアーティスト向けツール3選

回・小松 泰のオススメ! SOuPほか、テクニカルアーティスト向けツール3選

COLUMN PyMELの速度問題とOpenMaya

前述の通りPyMELはcmdsやOpenMayaのラッパーであり、使いやすさを実現するために裏であれこれ様々な処理を行なっています。そのため、速度的には不満なケースも多々あります。筆者が行なった、簡単なシュリンクラップのテストでは、PyMELとOpenMayaで50倍以上の速度差が出ました。頂点の移動など、大量のデータを扱う際にはOpenMayaの使用も検討してみると良いかもしれません。

OpenMayaはMayaのAPIライブラリで、もともとC++でのみ使用可能なライブラリでしたが、MayaがPythonに対応した際にPythonからも実行可能になり、C++でプラグインを作成せずとも通常のスクリプトからAPIを叩くことが可能になりました。OpenMayaの詳細に関しては今回は割愛しますが、速度比較の際に使用したスクリプトを例に簡単に解説したいと思います。右がその構文です。

PyMELのみ使用する関数と、OpenMayaを併用する関数の2つを用意しました。今回はOpenMaya版に関してのみ解説を行いたいと思います。この関数の肝はMFnMeshというクラスにあります。これは任意のメッシュの情報を取得したり設定したりするもので、様々なメソッドをもっています。今回は貼り付ける側(球)と貼り付けられる側(キューブ)の2種類があるので2つのMFnMeshのインスタンス(dmfn、tmfn)が必要になります。この2つのインスタンスを作成する際に、MDagPathを引数として渡しています。これはMayaの内部データを表すもののひとつで、特に階層やトランスフォームの情報が必要な場合に使用します。これをMFnMeshに渡すことによって、ワールド座標でのポイントの取得が可能になります。

今度は貼り付ける側の球のポイント全てを取得します。ここで取得できる情報はMPointArrayというポイントの配列用のデータです。ここで取得したポイント全てに対し、forループを使い、getClosestPointで貼り付けられる側のキューブの最近点を取得します。取得したポイントでMPointArrayの情報を更新し、全ての頂点に対して処理が終わったらsetPointsを行い、完了となります。PyMELと比較すると多少煩わしいと感じられるかもしれませんが、プラグインを書くまでもなく、メッシュなどMayaの様々な情報に高速でアクセスできるという点はやはり大きな魅力です。

Arrayや返り値の扱いにおいて、現状のOpenMayaモジュールではPythonらしくない記述が散見されますが、これはMaya 2016で拡充されたPython API 2.0で大幅に改善されているとのことです。今後のさらなる発展に期待が寄せられます。

from pymel.core import *
from time import time
from maya import OpenMaya

def calc_time(f):
 def fn(*args, **kwargs):
  s = time()
  r = f(*args, **kwargs)
  e = time()
  print('[TIME] %f sec' % (e - s))
  return r
 return fn

# [TIME] 17.135000 sec
@calc_time
def stick_pts_pymel(dst, target):
 d = PyNode(dst).getShape()
 t = PyNode(target).getShape()
 s = 'world'
 pts = d.getPoints(space=s)
 for i, p in enumerate(pts):
  pts[i] = t.getClosestPoint(p, space=s)[0]
 d.setPoints(pts, space=s)
stick_pts_pymel('pSphere1', 'pCube1')

# [TIME] 0.319000 sec
# 53.7 times faster than pymel version!!
@calc_time
def stick_pts_openmaya(dst, target):
 d = api.toMDagPath(dst)
 t = api.toMDagPath(target)
 d.extendToShape()
 t.extendToShape()
 dmfn = OpenMaya.MFnMesh(d)
 tmfn = OpenMaya.MFnMesh(t)
 s = OpenMaya.MSpace.kWorld
 pts = OpenMaya.MPointArray()
 dmfn.getPoints(pts, s)
 p = OpenMaya.MPoint()
 for i in xrange(pts.length()):
  tmfn.getClosestPoint(pts[i], p, s)
  pts.set(p, i)
 dmfn.setPoints(pts)
stick_pts_openmaya('pSphere1', 'pCube1')

次ページ:
TOOL03 Atom(GitHub)

特集