今年もお誘いを受けて、え、ぶっちゃけ今そんなにネタ無…げふんげふん。
せっかくなのでまたMaya-Pythonアドベントカレンダーに書こうかなと思います。
シーン構築で高確率で使うリファレンスまわりを書きますね( ・ㅂ・)و ̑̑
今回も、pymelとmayaPythonの両方の記述で書いておきます。
import pymel.core as pm
import maya.cmds as cmds
リファレンスの作成
まずは、基本的な作成について。
pm.createReference(refFile, ns=namespace)
cmds.file(refFile, reference=True, ns=namespace)
ここで書かれているrefFileという変数はリファレンスするファイルのパス、
namespaceはネームスペース名なので任意の文字列を入れてください。
リファレンスについて
さて、他のリファレンスのコマンドをみていく前にリファレンスについておさらいしておきます。
リファレンスというのはシーンをインポートするのではなく、
その名の如く参照することになります。
参照先のシーンが更新された場合、
リファレンスしているシーンにも更新が反映されます。
Mayaでのリファレンスはシーン上にリファレンスノードというものを作って参照しています。
「このリファレンスノードがリファレンスに必要な情報をすべて持っている…」
あながち間違っていないのですが、こうやって思い込んでいると
どのようにコマンドを叩いていいかわからなくなります。
なので、リファレンス周りのコマンドは特殊だということを念頭に置いておいてください。
cmdsにおいてはfileコマンド使ったりreferenceQueryを使ったりとわかりにくかったりするので、
pymel側ではFileReferenceというクラスでまとめてしまっています。
どういうことかはこの後のコマンドの紹介で実際に見ていってもらえればと思います。
ということで、リファレンス周りのコマンドはcmdsとpymelでけっこう扱いが違います。
とはいえ、cmdsのコマンドの場合はpmで置き換えるだけでも使えます。
ちなみに、先ほどのリファレンス作成時のコマンドで
fileRef = pm.createReference(refFile, namespace=namespace)
fileRef = cmds.file(refFile, reference=True, ns=namespace)
とすると、pymelではFileReferenceクラス、cmdsではリファレンスパス文字列、
と違ったものを取得できます。
リファレンスノードの取得
さて、まずはリファレンスノードの取得を見ていきましょう。
refNodes = pm.ls(rf=True)
refNodes = cmds.ls(rf=True)
基本はこれですべてのリファレンスノードが取得できます。
特定のリファレンスノードを取得したい場合は、
refNodes = pm.ls(refname, rf=True)
refNodes = cmds.ls(refname, rf=True)
とします。refnameはノード名です。
ただし、lsはリストで返ってくるので注意です。
もう一つの取得方法として、リファレンスパスから取得するというものがあります。
パスだけがわかっていて、シーン内のリファレンスノードが特定できないときに使えます。
refNode = pm.FileReference(refFile).refNode
refNode = cmds.referenceQuery(refFile, rfn=True)
refFileはリファレンスするファイルのパスです。
これはlsと違って単体で返ってきます。
リファレンスパスの取得
先ほどとは逆にリファレンスノードからリファレンス先のパスを取得します。
pm.FileReference(refNode).path
cmds.referenceQuery(refNode, f=True)
リファレンスパスの変更
次は、リファレンスパスの変更です。
通常であれば、リファレンス先のファイルが移動するということは好ましくありませんが、
何かしらの止むに止まれぬ事情によってパスが変更されるというのはよくあります。
pm.FileReference(refNode).replaceWith(newPath)
cmds.file(newPath, lr=refNode)
refNodeがリファレンスノードの変数、newPathが新しいパスになります。
リファレンスのパスを特定のパスからパスに置換するときは
リファレンスノードよりパスを取得して、そのパスに文字列置換を行い、
この変更のコマンドでパスを置換します。
cmdsの場合はパスの方をメインの引数に書くのでちょっとわかりにくいですよね。
リファレンスのインポート
レンダリング時などシーンのファイル処理を埋め込んでしまって最適化するときに使います。
pm.FileReference(refNode).importContents()
cmds.file(rfn=refNode, ir=True)
リファレンスに含まれているノードの取得
シーン内においてどのノードがリファレンスされているのかを知ることができます。
ネームスペースで取得することも可能だとは思いますが、
ノードからの関連でとりたいときはこっちがいいでしょう。
pm.FileReference(refNode).nodes()
cmds.referenceQuery(refNode, n=True)
referenceEditをきれいにする
そもそもreferenceEditってなんなのかってところからいきましょう。
これがかなり重要でやっかいです。
リファレンスしているオブジェクトは参照元で値をオーバーライドすることができます。
例えば、参照元でvisibilityをオフにすることができるのはこのオーバーライドの機能のおかげです。
このオーバーライドの情報がreferenceEditと呼ばれるものです。
この情報は、値を変更した直後にすぐに格納されるという性質があります。
オブジェクトのvisibilityをオン・オフするように、同じアトリビュートだけオーバーライドするなら、
最後のものが適用されます。
しかし、translateを変えてないつもりで間違えて同じ値をsetAttrした場合、その情報も残ります。
parentもオーバーライドできるのでこの情報も残ります。
というわけで、何がやっかいかという本題です。
実はシーンを読み込むとき、リファレンスをロードした後にオーバーライドの情報を適用します。
これが積もり積もっているとオーバーライド適用に時間がかかってしまい
シーンのオープンに時間がかかってしまうのです。
また、この情報があるほどそもそものシーンファイルのサイズも大きくなります。
「いや、そんなにオーバーライド情報増えるわけ…」
って思われるかもしれませんが、
アーティストがシーンをいじっていると予期せぬ操作というのは絶対起こります。
ちなみに、これはアーティストが悪いという訳ではありません。
そもそもオーバーライドの性質上ごみ情報がたまりやすいのが問題なのです。
まだシーンのオープンが遅いだけならいい方なのですが、
「シーン開くのおそいなーやだなーこわいなー」
と思ってそのまま放置していると、
場合によってはシーンがぶっ壊れます。シーン自体が開けなくなるのです。
こわいですねー。
ファイル復旧できればいいですけど、
そもそもぶっ壊れるまでゴミがアレした状態のシーンを、
正直、直せるとは思えません。
手っ取り早いのはシーンを構築し直してしまうことなんですが、
そもそもシーンでリファレンス以外にやっていた作業を無為にしてしまうのは残念なわけです。
そこで、リファレンスのreferenceEditをきれいにすることで
シーンの安全を保つというわけです。
ただ、注意しないといけないのは無闇にreferenceEditを消すと、シーンは壊れないものの、
シーンの状態が予期せぬ状態になることがあるということです。
なので慎重に行う必要があります。
前置きが長くなってしまいましたけど次から実際に見ていきましょう。
referenceEditの確認
何はともあれ実際にreferenceEditを視覚的に見てみましょう。
リファレンスしているシーンを開いて、Reference Editorを開きます。
確認したいリファレンスを選択して、右クリック
File > ListReference Edits... を実行します。
すると、Reference Editsウィンドウが開きます。
ちなみに、対象の行を選択して、右下のRemove Selected Editsで消すこともできます。
コマンドでは、
refEdits = pm.FileReference(refNode).getReferenceEdits()
refEdits = cmds.referenceQuery(refNode, es=True)
いずれもreferenceEditを丸々文字列としてとってこれます。
ただ、そのままだとわかりにくいのでパースする必要はあるでしょうね。
特定のreferenceEditを消す
referenceEditというのは先ほど述べたように特定のオーバーライド情報のことです。
これらを指定して消すというコマンドです。
pm.referenceEdit(nodeAttr, ec=commandName, r=True, fld=True, scs=True)
cmds.referenceEdit(nodeAttr, ec=commandName, r=True, fld=True, scs=True)
nodeAttrにはリファレンスしている特定のノードのアトリビュート(sphere1.txみたいな感じのやつ)
もしくはオブジェクト名が入ります。
オブジェクト名の場合は、そのオブジェクトのreferenceEditしている
アトリビュートすべてに対して適用します。
commandNameには、
'setAttr'
'connectAttr'
'disconnectAttr'
'addAttr'
'parent'
いずれかの文字列が入ります。
ここから、オーバーライドの情報は上記5つのコマンドのみに限られているということがわかります。
あとはこの中から不必要だと思われる要素を削除していきます。
まとめ
これ以外にもリファレンスまわりのコマンドはありますが、とりあえずこの辺で。
気になった方は調べてみましょう。
というわけで、リファレンスはきれいにしておきましょうというお話でした。
ちなみにこのオーバーライド情報の性質上、
リファレンス先がリファレンスしているという、
いわゆる二重リファレンスはシーンが非常に壊れやすくなります。
リファレンスは便利なものですが、このような性質を理解せず無闇に使うと
シーンが壊れて収集がつかなくなるので注意です。
個人的には、そもそも二重リファレンスはしない方がいいと思ってます。
経験上、問題が起きなかった試しがないからですね。
おお、こわいこわい。
参照といわないまでも、お仕事も無闇にいろんなところに手を出すと、
ストレスやらなんやらごみがたまって収集がつかなくなりますからね。
何事もシンプルにいきたいものですよね。
あ、でもお仕事的なリファレンス元募集してます。よろしくお願いします。
以上、「リファレンスのコマンド&こわいネタ」
略して「リファレンスのこまこわ」でした。
ではでは(:3_ )ヘ
タイトル苦しいな…