TOOL 02
Tool:PyMEL
Maker:Luma Pictures
PyMELは数々のハリウッド映画のVFXを手がけるVFXスタジオのLumaPicturesにより開発されているMaya用Pythonライブラリです。BSDライセンスのオープンソースソフトウェアで、GitHubにてソースコードが公開されています(github.com/lumapictures/pymel)。Maya 2011からはMayaに標準搭載となり、インストール作業なしにすぐ使えるようになりました。
PyMEL
対応アプリケーション:Autodesk Maya(2011からは標準搭載)
対応OS:対応アプリケーションに準ずる
価格:無料
問い合わせ:Luma Pictures
E-mail:www.lumapictures.com
GitHub.com/LumaPictures/pymel
入手方法
Mayaに標準搭載されているため、2011以降のバージョンをお使いの方はMayaのインストールのみでそのままお使いいただけます。2010以前の方は、前述のGitHubからソースをダウンロードし、Pythonパスを設定する必要があります。
[cmds]
from Maya import cods
for o in cmds.ls(sl=True, type='transform'):
cmds.setAttr('%s.tx' % o, 10)
[PyMEL]
import pymel.core as pm
for o in pm.selected(type='transform'):
o.tx.set(10)
基本機能
PyMELはMaya標準のcmdsと、一部OpenMaya(MayaAPIライブラリ)をラッピングしたライブラリで、よりPythonらしく、かつ簡潔にツールを書くことができます。cmdsとの大きなちがいはオブジェクト単位での処理を行いやすいということです。ここで言うオブジェクトとはMayaのオブジェクトではなく、Pythonのオブジェクトのことです。cmdsではMELと同様に文字列を扱ってオブジェクトを判別したりしますが、PyMELでは各ノードやアトリビュートにクラスが用意されており、オブジェクト指向での記述が可能です。
例えば選択したオブジェクト全てのtranslateXを10にしたい場合、cmdsとPyMELではそれぞれ左のようになります。cmdsでは文字列を使いアトリビュートを判定しているのに対し、PyMELではオブジェクトのプロパティに対して処理を行なっています。cmdsではsetAttrという関数を実行する際に、どのオブジェクトに対して実行するのかという情報をいちいち与えないといけません。これに対しPyMELでは、ループで与えられたオブジェクトに対してtxをセットするだけです。PyMELはcmdsのラッパーなので、実際には内部でsetAttrが動いているのですが、ユーザーが細かいことを気にする必要はありません。
この程度の例では大きなちがいは感じられないかもしれませんが、もう少し大きなツールを書く際にはこれがじわじわと効いてきます。ただし、filterExpandなど一部のコマンドでは文字列が返ってきたりなどする場合もあるので、慣れるまではどういう情報が返ってきているか適宜確認しながら進めるのが良いでしょう。
活用例
試しに何か簡単なツールを書いてみたいと思います。筆者が書くスクリプトはほとんどがUIなしの簡単なスクリプトで、さらにその大半がそのショットを作成する際に使用するのみの捨てスクリプトです。そういう場合によく登場するのが、大量オブジェクトに対してのループ処理です。例えば任意のオブジェクトの階層以下にある特定のネーミングルールを満たしたオブジェクトに対して何らかの値をセットする、または削除する、setsにまとめるなど、手作業で行うには面倒な上にエラーも介在しそうなケースや、ただひたすら数が多い場合など、ループ処理はとにかく頻繁に使用します。
とは言えただのループ処理を書くだけでは面白くないので、エクスプレッションを自動で設定するスクリプトを書いてみます。これはsetAttrをくり返すのではなく、エクスプレッションとなる文字列を作ってexpression関数で一気に設定するものです。
このシーンは赤青のボックスとロケータがあるだけのシンプルなシーンです。これをロケータの動きに合わせてボックスが回転するようにエクスプレッションを組んでいきます。ただし、完全にロケータの動きに沿ってしまうと単調な動きにも見えてしまいそうなので、少しだけ動きに幅をもたせるために、各種オフセットのパラメータを各ボックスに設定したいと思います。下がそのコードです。
expressionコマンドでエクスプレッションを作成するための文字列を作成するのと同時に、各ボックスでオフセットなどのパラメータをいじることができるようにするためのアトリビュートを追加しています。また、そのアトリビュートに対してランダムな値を入れることで、動きに多少のバラつきをもたせています。これを全てのボックスに対して手動で行おうと思うと大変面倒です。サンプルでは30個程度のボックスに対して適用したので最悪手動でも設定できそうですが、これが1,000、2,000という数字だったらもはや現実的ではありません。
PyMELを使うことで、cmdsで文字列を駆使するのに比べるとどのオブジェクトに対しての処理なのかがわかりやすくなるため、今後の使い回しや軽い調整などの際にも有効だと考えられます。これはあくまでも個人的な感想ではあるのですが、PyMELに慣れてしまった今となっては、たったこれだけの長さのスクリプトでもcmdsのように文字列を切り貼りするスタイルでスクリプトを書くのはなかなか辛いものがあります。まして言語の限界というわけではないと思うとなおさらです。ぜひ一度実際に試してみてその快適さを体感していただきたいです。もちろんcmdsの方が楽だった場合にはcmdsに戻るのもありだと思います。
import pymel.core as pm
from random import uniform
expr_init_tmpl = '''
float $p, $_loc, $w, $pp, $np;
float $loc = {ctl}.tx;
float $max_angle = {angle};
'''
expr_tmpl = '''
$p = {obj}.tx;
$_loc = $loc + {obj}.offset;
$w = {ctl}.sx * {obj}.influenceWidthRatio * 0.5;
$pp = $p + $w;
$np = $p - $w;
{obj}.ry = linstep($np, $pp, $_loc) * $max_angle;
'''
ctl = pm.PyNode('ctrl_box1')
expr_str = expr_init_tmpl.format(ctl=ctl.name(), angle=90.0)
for o in pm.listRelatives('box1_grp', c=True, pa=True, type='transform'):
try:
o.addAttr('influenceWidthRatio', sn='ir', at='double', dv=1.0)
o.addAttr('offset', sn='off', at='double', dv=0)
except Exception, e:
print e
o.ir.set(uniform(0.5, 1.2), k=True)
o.off.set(uniform(-2.0, 2.0), k=True)
expr_str += expr_tmpl.format(obj=o.name(), ctl=ctl.name())
pm.expression(s=expr_str, alwaysEvaluate=True, unitConversion='all')
print('# Done')
セットアップ前のシンプルなシーン。赤青にマテリアルが設定された縦長のキューブのグループとロケータがあるだけ
ロケータを左から右に動かした例。上段は回転幅を狭くしてランダムを強くしたもの、下段が回転幅を広くしてランダムを弱くしたもの。エクスプレッション側で回転の幅とロケータのスケールが接続されており、直感的にグラデーションの幅を決めることができます
エクスプレッションをアサインすると同時にアトリビュートを追加し、乱数で値を設定。influenceWidthRatioはロケータの動きに対する回転幅です。これを下げると小さな幅で一気に回転します。offsetはロケータの位置との差に対するオフセット。本来回転が始まる場所に到達する前に回転する、またはその逆ができるため、回転開始のバラつきを作成することができます。こういったパラメータを用いてエクスプレッションの動きにワンクッション置くことでバラつきが個々で制御可能になり、ほしい結果がつくりやすくなると考えて追加しました