【MAYA】インスタンスについてのまとめメモ＃２

 #１からの続きです。MAYA2018 ＋ MacOS Sierra 環境で調べています。

#PymelでDagノードのインスタンスを扱う

すごく雑に以下のコードでインスタンスぽい挙動をする命令を一括実行してみます。

```python

s1 = pm.PyNode('pCube1|pCubeShape1')
shapes = [s1]
for s in shapes:
    print('')
    info = dir(s)
    succces_retval = ['']
    for mem in info:
        if 'instance' in str(mem).lower() and not mem.startswith('_'):
            method = s.__getattribute__(mem)
            is_error = False
            try:
                retval = method()
            except Exception as ex:
                is_error = True
                print(ex.message)
            if not is_error:
                retval = ('{}.{}() -> {}'.format(s.name(), mem , retval))
                succces_retval.append(retval)

    for retval in succces_retval:
        print (retval)
```


```python
isColorSetPerInstance() takes exactly 2 arguments (1 given)
isInstanceOf() takes exactly 2 arguments (1 given)
isInstancedAttribute() takes exactly 2 arguments (1 given)
isUVSetPerInstance() takes exactly 2 arguments (1 given)
setInstanceable() takes exactly 2 arguments (1 given)
```
これら、エラーになったものは引数が必要なので個別に対応します。`1 given = self` なので、全て引数を１つ取るようです。

```python
# インスタンスが存在しない際の出力
pCubeShape1.getInstances() -> [nt.Mesh(u'pCubeShape1')]
pCubeShape1.getOtherInstances() -> []
pCubeShape1.instanceCount() -> 1
pCubeShape1.instanceNumber() -> 0
pCubeShape1.isInstanceable() -> True
pCubeShape1.isInstanced() -> False

# インスタンスが存在する際の出力
pCube1|pCubeShape1.getInstances() -> [nt.Mesh(u'pCube1|pCubeShape1'), nt.Mesh(u'pCube2|pCubeShape1')]
pCube1|pCubeShape1.getOtherInstances() -> [nt.Mesh(u'pCube2|pCubeShape1')]
pCube1|pCubeShape1.instanceCount() -> 2
pCube1|pCubeShape1.instanceNumber() -> 0
pCube1|pCubeShape1.isInstanceable() -> True
pCube1|pCubeShape1.isInstanced() -> True

# インスタンス側の出力
pCube2|pCubeShape1.getInstances() -> [nt.Mesh(u'pCube1|pCubeShape1'), nt.Mesh(u'pCube2|pCubeShape1')]
pCube2|pCubeShape1.getOtherInstances() -> [nt.Mesh(u'pCube1|pCubeShape1')]
pCube2|pCubeShape1.instanceCount() -> 2
pCube2|pCubeShape1.instanceNumber() -> 1
pCube2|pCubeShape1.isInstanceable() -> True
pCube2|pCubeShape1.isInstanced() -> True
```

上記の結果を踏まえるに、

* getInstances ： 自分を含めたインスタンス（のpymelオブジェクトの)一覧を返す。インスタンスがなくても自身がインスタンスの0番目である扱い。
* getOtherInstances ： 自分を含めないインスタンス一覧を返す。
* instanceCount ： 関連インスタンス数を返す。自身も含める。
* instanceNumber ： インスタンス番号を返す。これが0なら親という事でいいだろうか。
* isInstanceable ： インスタンス化可能かを示す？？
* isInstanced ： 別インスタンスが存在するか

という事のようだ。getInstances系のメソッドは、実行時に別のpymelオブジェクトをどかっと作成してしまうかもしれないので、パフォーマンスに注意したい。
また、親となるノードを消した後に同じ調査を行うと、

```python
# pCube1(親)を削除した際の出力
pCubeShape1.getInstances() -> [nt.Mesh(u'pCubeShape1')]
pCubeShape1.getOtherInstances() -> []
pCubeShape1.instanceCount() -> 1
pCubeShape1.instanceNumber() -> 0
pCubeShape1.isInstanceable() -> True
pCubeShape1.isInstanced() -> False
```
このようになった。インスタンス番号が０で、別インスタンスは存在しない、つまり親になった状態。…これをよく考えると、つまり `インスタンス間において親も子もなく、インデックス番号が異なるだけ` という事ではないだろうか。（作業者の意識としては親がこれ、というのはあるだろうが。）明確に親シンボルがあるAdobe系ツールとは異なりますね。

* setInstanceable インスタンス化可否を設定する

```python
n = pm.PyNode('pCubeShape1')
n.setInstanceable(False)
pm.instance(n)

# 警告: '|pCube1|pCubeShape1' にはインスタンス化できない設定があるためインスタンス化できません。 # 
# エラー: RuntimeError: file /Applications/Autodesk/maya2018/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python2.7/site-packages/pymel/internal/pmcmds.py line 134: インスタンス化するオブジェクトがありません # 
```
False設定にするとインスタンス化できなくなった、予想通りだが、UIにこれの設定項目が見当たらない。プログラムonly?

*isInstanceOf それぞれがインスタンス関係にあるかどうか調べる

```python
n1 = pm.PyNode('pCube1|pCubeShape1')
n2 = pm.PyNode('pCube2|pCubeShape1') #n1から作成したインスタンス

print(n1.isInstanceOf(n2))
print(n2.isInstanceOf(n1))

n3 = pm.PyNode('pSphereShape1') #関係ないmesh
print(n1.isInstanceOf(n3))
print(n3.isInstanceOf(n1))

#True
#True
#False
#False
```

通常のプログラムでは `instanceOf` みたいなメソッドは片方がクラス設定で片方がインスタンスなわけですが、MAYAのインスタンスには親子関係がないとすると、インスタンス通しでこのメソッドを実行した返り値が共に Trueである事が理解できます。

残りはよくわからないので、概要を検索して調べるだけとする。

* isColorSetPerInstance(name) カラーセットというものを扱う際に使うメソッドらしい。引数は文字列。実例検索しても全然引っかからないのであまり使われてないのだろう。

>Return true if this color set is per-instance, and false if it is shared across all instances. The name provided may be an individual set name or a set family name.

カラーセットがインスタンスごとに設定されていればTrueを返し、インスタンス間でシェアされている場合はFalseを返す、引数はセット名でもセットのファミリー名でも良い、との事。

* isUVSetPerInstance(name)

>Return true if this set is per-instance, and false if it is shared across all instances. The name provided may be an individual set name or a set family name.

ColorSetと同じ感じのUVSet版のようだ。

* isInstancedAttribute(attribute) 

引数に`pymel.core.general.Attribute`を取る。

```python
n1 = pm.PyNode('pCube1|pCubeShape1')
n2 = pm.PyNode('pCube2|pCubeShape1')
print(n1.isInstancedAttribute(n2.worldMatrix))
print(n1.isInstancedAttribute(n2.displayBorders))
# 出力：True
# 出力：False
```
とか出る。

>Returns true if the specified attribute is instanced. An instanced attribute is an array attribute whose index depends upon which instance of the node is being referred to.

属性がインスタンス化されていれば Trueを返す、その場合属性は配列となる、とのこと。複数のノードと関連が着いているかどうか確認するやつだろうか。

#その他 関連クラス・メソッド考察
参照先は Pymel Version 1.0.9のドキュメント。

[DagNode](http://help.autodesk.com/cloudhelp/2016/CHS/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html)

* getInstances
* getOtherInstances
* instanceCount
* instanceNumber
* isInstanceOf
* isInstanceable
* isInstanced

は、DagNodeに実装されている。つまりトランスフォームでもシェイプでもDagNodeを継承したノードであればインスタンスになり得るという事。

* getConnectedSetsAndMembers(instanceNumber, renderableSetsOnly)

というメソッドも存在した。インスタンスに関連したセットとメンバを調べるもののようだ。

[Shape](http://help.autodesk.com/cloudhelp/2018/CHS/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Shape.html)

* isInstancedAttribute
* setInstanceable

は、Shapeに実装されている。

[Mesh](http://help.autodesk.com/cloudhelp/2017/JPN/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html)

* isColorSetPerInstance
* isUVSetPerInstance

は、Meshに実装されている。他にもインスタンスがらみの引数を取るメソッドがそこそこある。

[DependNode](https://help.autodesk.com/cloudhelp/2017/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html#pymel.core.nodetypes.DependNode)
duplicateメソッドがインスタンスを作るかどうかの引数を持ってる。

なお、（中間は端折るが）ノード系の継承順は以下である。

`DependNode` <- `DagNode` <- `Shape` <- `Mesh`

インスタンス化の可否をコントロールできるのが Shape以下なのはなんでだろう。 

[pymel.core.general.instance](https://help.autodesk.com/cloudhelp/2017/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.core.general/pymel.core.general.instance.html)
インスタンスを作るメソッド。こっちの[melの解説](http://help.autodesk.com/cloudhelp/2018/JPN/Maya-Tech-Docs/CommandsPython/instance.html)を見た方がほうがわかりやすい。
末端以外はコピーにする、という事ができるようだ。これは、[mel コマンド duplicate](http://help.autodesk.com/cloudhelp/2018/JPN/Maya-Tech-Docs/CommandsPython/duplicate.html) でinstanceLeafフラグを立てた時と同じ挙動かもしれない。位置をずらしてインスタンス作成という機能も存在する。（メニューから実行できる `トランスフォームして複製`と同じものかも。）

[pymel.core.general.instanceable()](https://help.autodesk.com/cloudhelp/2017/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.core.general/pymel.core.general.instanceable.html#pymel.core.general.instanceable)
子孫ノードまでまとめてインスタンス化の可否を設定できる。

#まとめ
インスタンスの兄弟を探して色々処理するには、getOtherInstances、instanceCount、instanceNumber、isInstancedあたりを使っていつもの処理をするだけで事足りそうです。
ところでシングルトンクラスを作る際にgetInstance()とかいうメソッドを作っちゃってるとMAYAのインスタンスがらみの機能と混乱を招きそうだ。pythonだとモジュールとして作ればいいのかもしれないけど。