29
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Maya-PythonAdvent Calendar 2016

Day 15

Mayaのマテリアルのこねこね

Last updated at Posted at 2016-12-14

お誘いを受け、せっかくなので私もMaya-Pythonアドベントカレンダーに書こうかなと思います。

pythonメインのお話が多いようなのでmayaメインのお話でも。

中でも、意外とやることが多いマテリアルまわりの小ネタを書いていこうかなと思います。

念のため、pymelとmayaPythonの両方の記述で書いておきますね。
 

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

 

マテリアルまたはシェーダーの取得

まずは、何事においても始めとなるマテリアルの取得について。

【全material取得】

pm.ls(mat=True)
cmds.ls(mat=True)

シーン内のすべてのマテリアルを取得できます。
 
 

特定のマテリアルをとりたいときはtypフラグを使います。

例:lambertのみを取得

pm.ls(typ='lambert')
cmds.ls(typ='lambert')

 
 

##【Maya上で使用できるシェーダーリストの取得】

前項で特定のマテリアルを取得というのをやりました。

しかし、環境によっては存在しないマテリアルのタイプを取得しようとすると

「このマテリアルうちのMayaに入ってないからだめやんけ!」

と、ワーニングを返されてしまいます。
 
 

そんなスクリプトでワーニングすら返されたくないという人に役立つのがこれです。

matTypList = pm.listNodeTypes('shader')
matTypList = cmds.listNodeTypes('shader')

これを実行すると、自分の環境で入っているすべてのマテリアルタイプを取得できます。

厳密にいうと、pluginで読まれて使用できる状態になっているものを取得できます。

例えば、vrayをインストールしていても、シーン的にpluginがオンになっていないと、

vrayのマテリアルタイプが存在しないことになります。

つまるところ、チェックしようとしているマテリアルタイプが

しっかりと読まれているかを確認するときに使えるんですよね。

単純に使用できるマテリアルリストアップとしても使えるので覚えておくとお得です。
 
 

【オブジェクトからマテリアルを取得】

あるオブジェクトからそれにアサインされているマテリアルやシェーディンググループを取得します。

lsよりもこっちの方が何気によく使います。

というのも、特定のオブジェクトのマテリアルに対しての処理というのをやりたい時が多いからなんですよね。
 
 

SG = pm.listConnections(objs, s=False, d=True, t='shadingEngine')
mat = pm.ls(pm.listConnections(SG, s=True, d=False), mat=True)
SG = cmds.listConnections(objs, s=False, d=True, t='shadingEngine')
mat = cmds.ls(cmds.listConnections(SG, s=True, d=False), mat=True)

 
ここで書かれているobjsという変数は特定のオブジェクトが含まれているリスト変数になります。
 

選択しているオブジェクトに上記を使うなら例えば、

objs = pm.selected()
objs = cmds.ls(sl=True)

と事前に、objs変数を指定しておきます。
これのいいところは、複数指定した状態でも返ってくるところですね。

pymelでシェーディンググループを取得するなら

SG  = obj.shadingGroups()

とも書けるんですが、objに入るものは単体でなければなりません。

これを複数のものに適用するなら、for文で回して…とかめんどいことになります。
 
 
 
 

ちょっと話は脱線しますが、ここで使用したlistConnectionsはめちゃくちゃ便利です。

上記同様な感じで、マテリアルからオブジェクトにアサインされたものをとることもできますし、

jointからbind skinされたオブジェクトをとることもできます。
 
 
 

基本maya上での作業ってポリゴンやjoint選択(指定)から引数代わりとして指定することが多いので

何かのツールを作るときは大体の確率で使用します。

逆にいえばここら辺は関数にしとくと楽ですよね~。
 
 
 
 
 

ファイルノードの取得

ファイルノード、いわゆるテクスチャパスが入っているノードを取得します。

シーン内の全部というのであれば最初に紹介した、lsコマンドを使用して、typをfileと指定してあげればできます。

ただ、特定のマテリアルにつながっているファイルノードのみを取得したいというのが頻繁にあります。
 

コマンドを使って簡単に取得

fileNodes = pm.ls(pm.listHistory(mat), type='file')
fileNodes = cmds.ls(cmds.listHistory(mat), type='file')

mat変数には特定のマテリアルが入ります。

この書き方をするとマテリアルにマテリアルがつながっていても、

その先につながっているファイルノードを取得することができます。

結果的にそのマテリアルに関連するファイルノードをすべて取得できるんですね。
 

ところがどっこい。このlistHistory注意して欲しいんですが、

特定のアトリビュートにコネクトされているものは引っ張ってこれないという弱点があります。

advcale_mayapy_01.png

こういったノードがあったとき、

pm.select(pm.listHistory('lambert2'))

これを実行してみます。

するとこんな感じで

advcale_mayapy_02.png

file15, 18, 24, 27 が取得できていないということがわかります。

とはいえ、この取得できない特定のアトリビュートというのはそうそう繋ぐこともないので

問題ないといえば問題ないんですよね。
 
 
 
 

完璧に取得

「もしかして、必要ノードを取り逃がすかもしれない…」

というような完璧主義者さんには、whileで回すという方法もあります。

ここはpymelだけ載せてますが、基本pm.cmds.にすればmayaPythonでも動くはずです。

limitNum = 20
##---------------------------------------------------------
## get connected file node
##---------------------------------------------------------
## @param <obj/List>nodes : nodes
## @return <obj/List>fileNode : file node
## @return <obj/List>othNode  : other node
def getConnectedFileNode(nodes):

    connNode = pm.listConnections(nodes, s=True, d=False)
    fileNode = pm.ls(connNode, typ='file')
    othNode  = list(set(connNode) - set(fileNode))

    return fileNode, othNode
##---------------------------------------------------------
## get file node from history
##---------------------------------------------------------
## @param <obj/List>materials : target material
## @return <obj/List>fileNode : file node
def getFileNodeFromHistory(materials):

    global limitnum

    count   = 0
    fileNode, othNode = getConnectedFileNode(materials)
    while othNode:
        tmpFNode, othNode = getConnectedFileNode(othNode)
        if tmpFNode:
            fileNode += tmpFNode
        count += 1
        if count >  limitNum:
            break

    return fileNode
getFileNodeFromHistory(mat)

で実行できます。matにはリストで渡しても、単体で渡しても問題ないです。

さっき例に挙げたマテリアルネットワークで試してみます。

advcale_mayapy_01.png

pm.select(getFileNodeFromHistory('lambert2'))

advcale_mayapy_03.png

このように漏れなく取得できるようになります。
 

limitNumというglobalの変数は階層の深さを指定しているものです。

指定しなければどのような階層の数でもいけるのですが、

while文は無限ループが引き起こる要因になりますので、

念のための保険として入れてます。

流石に20階層以上のシェーディングネットワークは無いと思いますので…

万が一あったとしてもlimitNumの数値を上げておけば済みます。
 
 

ちなみに、言わずもがなかもしれませんが、処理速度はgetFileNodeFromHistoryの関数を使うよりも圧倒的にlistHistoryコマンド使った方が速いです。

「一度このwhile文を使ってみたかったんだ!助かる!」

というスクリプト初心者にはテストケースとして勉強になるかもしれません。
 

はい。私のことですね。

べ、べつにlistHistory知らなくて、作ったわけじゃないんだからね!
 
 
 
 
 
 

おわり

というわけで、マテリアル周りのお話でした~。

誰かの役に立てばいいですね~。

これで、コネクションまわりはlistConnections使っておけばなんとでもなるというのが勉強できると思います。
 
 
 

お仕事のコネもlistConnectionsできればいいんですけどね…

以上、「マテリアルのコネクション小ネタ」

略して「マテリアルのこねこね」でした。

ではでは(:3ノシ )ヘ

29
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?