【MAYA】マテリアルのフェイスアサイン時のノード構成を理解する#1

MAYAのインスタンスの振る舞いに続いて、(世の中で嫌われていそうな)マテリアルのフェイスアサインについてPymelを使ったプログラム視点で調査したのでメモを残してみます。
以下は、MAYA2018 + MacOS Sierraで動作確認しています。ソース中`pm`となっているのは`import pymel.core as pm`という形でインポートされた PymelCoreオブジェクトです。ソース中、`出力:`と書いてある部分は、結果表示を自分がわかりやすくコメントの形で書き写した部分で、実際のMAYA上の画面とは異なります。

## フィイスアサイン前後の差異

<img width="270" alt=" 2018-01-08 22.10.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/0e737ec3-ea8e-25aa-e142-00fe9af78bfb.png">
マテリアルのフェイスアサイン前の状態です。shapeの`instObjGroups[0]`からShadingGroupの`dagSetMembers[0]`にコネクトされています。つまり、キューブのデフォルトインスタンスがマテリアル`phong1`に紐づいています。普通です。

<img width="325" alt=" 2018-01-08 22.11.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/a5aa6242-5f0c-6963-42ff-eba5059a7e80.png">
フェイス１面に別のマテリアルである`phong2`をアサインしてみた状態がこれです。逆向きにコネクションができたり、コネクションの出力元が増えたり、急に複雑になりました。変更点をそれぞれ見てみます。

### メンバーワイヤーフレームカラー

SGそれぞれからShapeに逆向きに伸びるコネクションです。見やすくビューのノードの左右位置を入れ替えて詳しく見てみます。
<img width="218" alt=" 2018-01-08 22.47.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/d3a8e956-87c0-0957-ef45-cc11b7044128.png">
メンバーワイヤフィレームカラーからオブジェクトグループのカラーという所へコネクションが伸びています。詳しい値をscriptで表示してみます。

```python
sg1 = pm.PyNode('phong1SG')
print(sg1.memberWireframeColor.get())
#出力：-1

print(sg1.memberWireframeColor.listConnections(plugs=True))
#出力：[Attribute(u'pCubeShape1.instObjGroups[0].objectGroups[0].objectGrpColor')]

sg2 = pm.PyNode('phong2SG')
print(sg2.memberWireframeColor.get())
#出力：-1

print(sg2.memberWireframeColor.listConnections(plugs=True))
#出力：[Attribute(u'pCubeShape1.instObjGroups[0].objectGroups[1].objectGrpColor')]
```
sg1、 sg2共にほぼ同じ状態で、異なるのはobjectGroupsのインデックス番号だけです。このobjectGroupsの番号がズバリ、フェイスのグループ番号を示すようです。青いフェイス部分がグループ０で、黄色い部分がグループ１という事ですね。

`pCubeShape1.instObjGroups[0].objectGroups[0]`は、つまり、`pCubeShape1`のデフォルトインスタンスのフェイスグループ0という意味でしょう。

そして、`memberWireframeColor`というのはどうも作業画面上のワイヤフレームのカラー表示設定のようで、ユーザーが自由にカスタマイズして使うことができるもののようです。値の-1というのはデフォルト値で設定なし、です。と、するとこのカラー番号を指定するUIがMAYAのSGのインスペクタ上に存在しそうなものですが見つかりません。。
見つからないのですが、重要そうなノード接続ではなさそうなので、深く考えずに先に進みます。

### インスタンスオブジェクトグループのオブジェクトグループ

<img width="296" alt=" 2018-01-08 23.23.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/d27b818c-79b1-a117-0086-db1ee14a928e.png">
フェイスアサイン前後を改めて確認します。

```python
shape = pm.PyNode('pCubeShape1')
print(shape.instObjGroups[0].listConnections(plugs=True))
#出力:[Attribute(u'phong1SG.dagSetMembers[0]')]
```
`instObjGroups[0]`から`phong1SG.dagSetMembers[0]`に接続が伸びています。

<img width="231" alt=" 2018-01-08 23.31.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/2a64e889-2b63-7d2f-6e3b-f27c21f459fb.png">
フェイスアサイン後はこのようになりました。まず、選択されて黄色くなっているコネクションを確認します。

```python
sg1 = pm.PyNode('phong1SG')
print(sg1.dagSetMembers[0].listConnections(plugs=True, source=True))
#出力：[Attribute(u'pCubeShape1.instObjGroups[0].objectGroups[0]')]

sg2 = pm.PyNode('phong2SG')
print(sg2.dagSetMembers[0].listConnections(plugs=True, source=True))
#出力：[Attribute(u'pCubeShape1.instObjGroups[0].objectGroups[1]')]
```
SGそれぞれ２つの`dagSetMembers[0]`から逆方向にコネクションを出力させてみましたが、フェイスアサイン前の`instObjGroups[0]`ではなく、`instObjGroups[0].objectGroups[0]`と`instObjGroups[0].objectGroups[1]`に接続先が変わりました。フェイスごとにSGに接続が伸びるようになったという事ですね。

さらに、objectGroup(=フェイスのグループ)について詳しく内容を表示してみます。

```python
shape = pm.PyNode('pCubeShape1')
objectGroup = shape.instObjGroups[0].objectGroups[0]
attr_names = pm.listAttr(objectGroup)
for attr_name in attr_names:
    print('{}'.format(attr_name))
#以下出力:
#instObjGroups[0].objectGroups[0]
#instObjGroups[0].objectGroups[0].objectGrpCompList
#instObjGroups[0].objectGroups[0].objectGroupId
#instObjGroups[0].objectGroups[0].objectGrpColor
```
どのような属性があるのか羅列してみました。
`objectGroups`そのものと、`objectGrpCompList`、`objectGroupId`、`objectGrpColor`が存在するようです。
それぞれの型と値を表示してみます。

```python
objectGroup = shape.instObjGroups[0].objectGroups[0]
attrs = (objectGroup, objectGroup.objectGrpCompList,
  objectGroup.objectGroupId, objectGroup.objectGrpColor)
for a in attrs:
    print('{}({}) : {}'.format(a, a.type(), a.get()))
#以下出力:
#pCubeShape1.instObjGroups[0].objectGroups[0](TdataCompound) : [[u'f[1:5]'], 20, -1]
#pCubeShape1.instObjGroups[0].objectGroups[0].objectGrpCompList(componentList) : [u'f[1:5]']
#pCubeShape1.instObjGroups[0].objectGroups[0].objectGroupId(long) : 20
#pCubeShape1.instObjGroups[0].objectGroups[0].objectGrpColor(short) : -1
```
* `objectGroups[0]`は`TdataCompound`という型で、この場合は他の情報をひとまとめにした型という事のようです。値の表示も他の３つのデータがそのままリストで並んでいます。
* `objectGrpCompList`は`componentList`という型で、u`f[1:5]`という文字列の内容から察するにフェイス番号1から5の集合という意味ではないでしょうか。
* `objectGroupId`は`long`の数値型でフェイスグループのID番号のようです。
* `objectGrpColor`は`short`の数値型で前述したように枠線表示などに利用される色番号のようです。

別フェイスグループの方も見てみます。

```python
objectGroup = shape.instObjGroups[0].objectGroups[1]
attrs = (objectGroup, objectGroup.objectGrpCompList,
  objectGroup.objectGroupId, objectGroup.objectGrpColor)
for a in attrs:
    print('{}({}) : {}'.format(a, a.type(), a.get()))
#以下出力:
#pCubeShape1.instObjGroups[0].objectGroups[1](TdataCompound) : [[u'f[0]'], 22, -1]
#pCubeShape1.instObjGroups[0].objectGroups[1].objectGrpCompList(componentList) : [u'f[0]']
#pCubeShape1.instObjGroups[0].objectGroups[1].objectGroupId(long) : 22
#pCubeShape1.instObjGroups[0].objectGroups[1].objectGrpColor(short) : -1
```
おおよそ予想通りの内容が表示されました。フェイス番号0が一つ設定されています。キューブの場合全フェース数は６個なので、辻褄が合います。

なお、このようなフェイスアサインにした場合、
<img width="171" alt=" 2018-01-09 0.04.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/1fb607d8-b9b0-45c3-ec59-b52719ef2d18.png">
`objectGroups[0].objectGrpCompList`の値は、`[u'f[0]', u'f[2:3]', u'f[5]']`と、なりました。飛び飛びのフェイス番号が登録されています。

ここまでは、問題なく理解できました。

### コンポーネントのインスタンスオブジェクトグループ
マテリアルのフェイスアサインにすると変化するコネクションの最後の１つ、一番厄介そうな奴を見ていきます。

<img width="260" alt=" 2018-01-09 0.09.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/4c3451bf-cd2d-4964-3fd8-61d614b30c9e.png">
コネクションで黄色くなっている部分に注目します。フェイスアサイン前には伸びていなかったコネクションで、片方のマテリアルにだけ伸びています。最初にオブジェクト全体に適用されていたマテリアル側に伸びるようです。

<img width="329" alt=" 2018-01-09 0.14.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/0eb9bb00-1b24-650b-947a-9f2d27435b5c.png">
元々の青いマテリアル(SG1)を赤いマテリアル(SG3)をフェイスアサインする事で無くしてみましたが、まだコンポーネントのインスタンスオブジェクトグループからSG1へのコネクションは維持されています。

<img width="314" alt=" 2018-01-09 0.14.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/6f9f9e71-9254-c536-85be-bc670cfa87a3.png">
オブジェクト自体にSG3をアサインし直してみました。コンポーネントのインスタンスオブジェクトグループからSG1へのコネクションがなくなりました。

<img width="311" alt=" 2018-01-09 0.15.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/8302/590ebf12-1b39-cc52-8567-4deb62e51eca.png">
改めて黄色いSG2をフェイスアサインしてみました。今度はインスタンスオブジェクトグループからSG3へコネクションが貼られています。

フェイスアサインがなかった場合のデフォルトのマテリアル設定を保持している、と考えれば良いでしょうか。よくわからないので、詳細の調査に進みます。

```python
shape = pm.PyNode('pCubeShape1')
compInstObjGroups = shape.compInstObjGroups[0]
attr_names = pm.listAttr(compInstObjGroups)
for attr_name in attr_names:
    print('{}'.format(attr_name))
#以下出力:
#compInstObjGroups[0]
#compInstObjGroups[0].compObjectGroups
#compInstObjGroups[0].compObjectGroups.compObjectGrpCompList
#compInstObjGroups[0].compObjectGroups.compObjectGroupId

compObjectGroups = compInstObjGroups.compObjectGroups[0] # ここで[0]にしないとMAYAが落ちる
attrs = (compInstObjGroups,
    compObjectGroups,
    compObjectGroups.compObjectGrpCompList,
    compObjectGroups.compObjectGroupId
)
for attr in attrs:
    print('{}({}) {}'.format(attr, attr.type(), attr.get()))
#以下出力:
#pCubeShape1.compInstObjGroups[0](TdataCompound) (0.0,)
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0](TdataCompound) [None, 2]
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0].compObjectGrpCompList(None) None
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0].compObjectGroupId(long) 2
```
ここで、`compInstObjGroups[0]`と`compObjectGroups`は`TdataCompound`型で、`compObjectGrpCompList`には`None`が、`compObjectGroupId`には`2`が設定されています。
さて、値がNoneとは。。？

ちなみにこの値はマテリアルのフェイスアサイン前にも存在はしていて、

```python
#pCubeShape1.compInstObjGroups[0](TdataCompound) (0.0,)
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0](TdataCompound) [None, 0]
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0].compObjectGrpCompList(None) None
#pCubeShape1.compInstObjGroups[0].compObjectGroups[0].compObjectGroupId(long) 0
```
のような状態になりました。
さらによくわかりませんが、#2に続きます。#2ではシェイプのインスタンスを作成して異なるフェイスアサインをしてみます。