リファレンスのこまこわ

今年もお誘いを受けて、え、ぶっちゃけ今そんなにネタ無…げふんげふん。

せっかくなのでまたMaya-Pythonアドベントカレンダーに書こうかなと思います。

シーン構築で高確率で使うリファレンスまわりを書きますね( ･ㅂ･)و ̑̑

今回も、pymelとmayaPythonの両方の記述で書いておきます。
　

``` python
import pymel.core as pm
import maya.cmds as cmds
```
　

# リファレンスの作成

まずは、基本的な作成について。

``` python
pm.createReference(refFile, ns=namespace)
```

``` python
cmds.file(refFile, reference=True, ns=namespace)
```

ここで書かれている<font color=orange>refFile</font>という変数はリファレンスするファイルのパス、

<font color=orange>namespace</font>はネームスペース名なので任意の文字列を入れてください。


# リファレンスについて

さて、他のリファレンスのコマンドをみていく前にリファレンスについておさらいしておきます。

リファレンスというのはシーンをインポートするのではなく、

その名の如く参照することになります。

参照先のシーンが更新された場合、

リファレンスしているシーンにも更新が反映されます。

Mayaでのリファレンスはシーン上にリファレンスノードというものを作って参照しています。

**「このリファレンスノードがリファレンスに必要な情報をすべて持っている…」**

あながち間違っていないのですが、こうやって思い込んでいると

どのようにコマンドを叩いていいかわからなくなります。

なので、リファレンス周りのコマンドは特殊だということを念頭に置いておいてください。

cmdsにおいては<font color=orange>file</font>コマンド使ったり<font color=orange>referenceQuery</font>を使ったりとわかりにくかったりするので、

pymel側では<font color=orange>FileReference</font>というクラスでまとめてしまっています。

どういうことかはこの後のコマンドの紹介で実際に見ていってもらえればと思います。



ということで、リファレンス周りのコマンドはcmdsとpymelでけっこう扱いが違います。

とはいえ、cmdsのコマンドの場合はpmで置き換えるだけでも使えます。


ちなみに、先ほどのリファレンス作成時のコマンドで

``` python
fileRef = pm.createReference(refFile, namespace=namespace)
```

``` python
fileRef = cmds.file(refFile, reference=True, ns=namespace)
```

とすると、pymelでは<font color=orange>FileReference</font>クラス、cmdsではリファレンスパス文字列、

と違ったものを取得できます。


# リファレンスノードの取得

さて、まずはリファレンスノードの取得を見ていきましょう。

``` python
refNodes = pm.ls(rf=True)
```

``` python
refNodes = cmds.ls(rf=True)
```

基本はこれですべてのリファレンスノードが取得できます。

特定のリファレンスノードを取得したい場合は、

``` python
refNodes = pm.ls(refname, rf=True)
```

``` python
refNodes = cmds.ls(refname, rf=True)
```

とします。<font color=orange>refname</font>はノード名です。

ただし、<font color=orange>ls</font>はリストで返ってくるので注意です。


もう一つの取得方法として、リファレンスパスから取得するというものがあります。

パスだけがわかっていて、シーン内のリファレンスノードが特定できないときに使えます。

``` python
refNode = pm.FileReference(refFile).refNode
```

``` python
refNode = cmds.referenceQuery(refFile, rfn=True)
```

<font color=orange>refFile</font>はリファレンスするファイルのパスです。

これは<font color=orange>ls</font>と違って単体で返ってきます。


# リファレンスパスの取得

先ほどとは逆にリファレンスノードからリファレンス先のパスを取得します。

``` python
pm.FileReference(refNode).path
```

``` python
cmds.referenceQuery(refNode, f=True)
```


# リファレンスパスの変更

次は、リファレンスパスの変更です。

通常であれば、リファレンス先のファイルが移動するということは好ましくありませんが、

何かしらの止むに止まれぬ事情によってパスが変更されるというのはよくあります。

``` python
pm.FileReference(refNode).replaceWith(newPath)
```

``` python
cmds.file(newPath, lr=refNode)
```

<font color=orange>refNode</font>がリファレンスノードの変数、<font color=orange>newPath</font>が新しいパスになります。

リファレンスのパスを特定のパスからパスに置換するときは

リファレンスノードよりパスを取得して、そのパスに文字列置換を行い、

この変更のコマンドでパスを置換します。

cmdsの場合はパスの方をメインの引数に書くのでちょっとわかりにくいですよね。


# リファレンスのインポート

レンダリング時などシーンのファイル処理を埋め込んでしまって最適化するときに使います。

``` python
pm.FileReference(refNode).importContents()
```

``` python
cmds.file(rfn=refNode, ir=True)
```


# リファレンスに含まれているノードの取得

シーン内においてどのノードがリファレンスされているのかを知ることができます。

ネームスペースで取得することも可能だとは思いますが、

ノードからの関連でとりたいときはこっちがいいでしょう。

``` python
pm.FileReference(refNode).nodes()
```

``` python
cmds.referenceQuery(refNode, n=True)
```


# <font color=orange>referenceEdit</font>をきれいにする

そもそも<font color=orange>referenceEdit</font>ってなんなのかってところからいきましょう。

これがかなり重要でやっかいです。

リファレンスしているオブジェクトは参照元で値をオーバーライドすることができます。

例えば、参照元でvisibilityをオフにすることができるのはこのオーバーライドの機能のおかげです。

このオーバーライドの情報が<font color=orange>referenceEdit</font>と呼ばれるものです。

この情報は、値を変更した直後にすぐに格納されるという性質があります。

オブジェクトのvisibilityをオン・オフするように、同じアトリビュートだけオーバーライドするなら、

最後のものが適用されます。

しかし、translateを変えてないつもりで間違えて同じ値をsetAttrした場合、その情報も残ります。

parentもオーバーライドできるのでこの情報も残ります。

というわけで、何がやっかいかという本題です。

実はシーンを読み込むとき、リファレンスをロードした後にオーバーライドの情報を適用します。

これが積もり積もっているとオーバーライド適用に時間がかかってしまい

シーンのオープンに時間がかかってしまうのです。

また、この情報があるほどそもそものシーンファイルのサイズも大きくなります。

**「いや、そんなにオーバーライド情報増えるわけ…」**

って思われるかもしれませんが、

アーティストがシーンをいじっていると予期せぬ操作というのは絶対起こります。

ちなみに、これはアーティストが悪いという訳ではありません。

そもそもオーバーライドの性質上ごみ情報がたまりやすいのが問題なのです。

まだシーンのオープンが遅いだけならいい方なのですが、

**「シーン開くのおそいなーやだなーこわいなー」**

と思ってそのまま放置していると、

場合によっては<font color=red>**シーンがぶっ壊れます**</font>。シーン自体が開けなくなるのです。

こわいですねー。

ファイル復旧できればいいですけど、

そもそもぶっ壊れるまでゴミがアレした状態のシーンを、

正直、直せるとは思えません。

手っ取り早いのはシーンを構築し直してしまうことなんですが、

そもそもシーンでリファレンス以外にやっていた作業を無為にしてしまうのは残念なわけです。

そこで、リファレンスの<font color=orange>referenceEdit</font>をきれいにすることで

シーンの安全を保つというわけです。


ただ、注意しないといけないのは無闇に<font color=orange>referenceEdit</font>を消すと、シーンは壊れないものの、

シーンの状態が予期せぬ状態になることがあるということです。

なので慎重に行う必要があります。


前置きが長くなってしまいましたけど次から実際に見ていきましょう。


## <font color=orange>referenceEdit</font>の確認

何はともあれ実際に<font color=orange>referenceEdit</font>を視覚的に見てみましょう。

リファレンスしているシーンを開いて、Reference Editorを開きます。

確認したいリファレンスを選択して、右クリック

File > ListReference Edits...　を実行します。
![advcale_mayapy_2017_02.png](https://qiita-image-store.s3.amazonaws.com/0/153835/365359fb-81af-7a2b-bd52-bfb3af41a9c9.png)

すると、Reference Editsウィンドウが開きます。

そこで、現在の<font color=orange>referenceEdit</font>が確認できます。
![advcale_mayapy_2017_03.png](https://qiita-image-store.s3.amazonaws.com/0/153835/08da6025-871a-af71-dcdd-cf20c5ae04e1.png)


ちなみに、対象の行を選択して、右下のRemove Selected Editsで消すこともできます。

コマンドでは、

``` python
refEdits = pm.FileReference(refNode).getReferenceEdits()
```

``` python
refEdits = cmds.referenceQuery(refNode, es=True)
```

いずれも<font color=orange>referenceEdit</font>を丸々文字列としてとってこれます。

ただ、そのままだとわかりにくいのでパースする必要はあるでしょうね。


## 特定の<font color=orange>referenceEdit</font>を消す

<font color=orange>referenceEdit</font>というのは先ほど述べたように特定のオーバーライド情報のことです。

これらを指定して消すというコマンドです。

``` python
pm.referenceEdit(nodeAttr, ec=commandName, r=True, fld=True, scs=True)
```

``` python
cmds.referenceEdit(nodeAttr, ec=commandName, r=True, fld=True, scs=True)
```

nodeAttrにはリファレンスしている特定のノードのアトリビュート(sphere1.txみたいな感じのやつ)

もしくはオブジェクト名が入ります。

オブジェクト名の場合は、そのオブジェクトの<font color=orange>referenceEdit</font>している

アトリビュートすべてに対して適用します。

commandNameには、

'<font color=orange>setAttr</font>'
'<font color=orange>connectAttr</font>'
'<font color=orange>disconnectAttr</font>'
'<font color=orange>addAttr</font>'
'<font color=orange>parent</font>'

いずれかの文字列が入ります。

ここから、オーバーライドの情報は上記5つのコマンドのみに限られているということがわかります。

あとはこの中から不必要だと思われる要素を削除していきます。

　
　
　
　
　
　


# まとめ
これ以外にもリファレンスまわりのコマンドはありますが、とりあえずこの辺で。

気になった方は調べてみましょう。


というわけで、リファレンスはきれいにしておきましょうというお話でした。

ちなみにこのオーバーライド情報の性質上、

リファレンス先がリファレンスしているという、

いわゆる二重リファレンスはシーンが非常に壊れやすくなります。

リファレンスは便利なものですが、このような性質を理解せず無闇に使うと

シーンが壊れて収集がつかなくなるので注意です。

個人的には、そもそも二重リファレンスはしない方がいいと思ってます。

経験上、問題が起きなかった試しがないからですね。

おお、こわいこわい。


参照といわないまでも、お仕事も無闇にいろんなところに手を出すと、

ストレスやらなんやらごみがたまって収集がつかなくなりますからね。

何事もシンプルにいきたいものですよね。


あ、でもお仕事的なリファレンス元募集してます。よろしくお願いします。

　
　
　


以上、「リファレンスのコマンド＆こわいネタ」

略して「リファレンスのこまこわ」でした。

ではでは(:3_　)ﾍ
　
　
　
　
　
　
タイトル苦しいな…