見出し画像

Mayaの頂点カラー表示のトグル切り替え


はじめに

Mayaを使っていると色々な便利な機能があるなぁとは思う反面ホットキーが設定できない、もしくはコマンド名がわからん!ということがちらほら出てきます。

今回は個人的によく使うけど機能にたどり着くまでの階層が深かったりして地味に手間がかかる部分を解消すべくスクリプトを用意しました。

自分の復習も兼ねてちょっと丁寧に書いてみます。
また初めてのスクリプトを自分で書いてみたい!というデザイナーにもわかりやすく言葉を砕きながら説明できればなと思います。
何かと自身でスクリプトがかけたりすると細かい効率化につながるので興味がある方は是非チャレンジしてみてください。
TAさんやプログラマーさんの様な本職の方が作るものと比べて大分シンプルなものなのでとっつきやすいかなとは思います。
本職の方はなにかありましたらつっこみよろしくお願いします…!!


頂点カラーの切り替え

個人製作等でただモデリングするだけではあまり気にかけることはないとは思いますがゲーム開発のモデルだとまぁ使う頂点カラー。
詳しいことはここでは割愛しますがテクスチャでいうマスク同様に頂点に色を持たせてその色の強度や範囲でマスクをかけるといったイメージですね。
RGBAと4つの情報を持つことができます。

なので確認や調整のためによく頂点カラーの表示の切り替えを行うことがあります。

簡単にスフィアを1つ用意して赤い頂点カラーを設定しました。
デフォルトではモデリングメニュー→メッシュ表示→表示カラーアトリビュートの切り替えで頂点カラーの表示非表示を行うことができます。
ただ見てわかる通り階層が深く探すのに手間がかかります。
ちょろっと確認したいだけなのにモデリングメニュー以外にしてあるとわざわざ切り替えて~とこれもまためんどいポイントですね。

ホットキーエディタなどでも探してみたのですがこのコマンドのトグル切り替えが見つからなかったのでスクリプトを用意しました!
ちなみに自分はスクリプトを書く際には基本的にpythonで書いています。
Melも便利なのですがMaya専用のスクリプト言語なのでMaya以外での汎用性がありません。
なのでpythonに慣れればほかのことにも応用できないかなぁぐらいのすけべ心で使っています。(Substance Painter Designer, Blender, Houdini, Marvelous Designerなどでもpythonが利用されているようです)
ただMelにはMayaに特化しているだけあって便利なコマンドがたくさんあるのでそのコマンドをpythonで呼び出して使うことは多々あります。
話が逸れてしまいましたので本線に…

アトリビュートエディターを覗いてみるとshape内のメッシュコンポーネントディスプレイ→カラー表示で表示の切り替えが制御されているようです(なのでわざわざメニューに潜らなくてもここで表示の切り替えもできます!)

スクリプトを見てみよう

ではここのチェックボックスのオンオフをスクリプトで対応できるようにしてみましょう!
けどどうやって制御するんじゃい!という話なんですがまずはスクリプトエディタのヒストリを確認してみましょう。

ヒストリ→ヒストリの出力→両方とも表示にさせておく

そして先ほどのカラー表示のチェックボックスを何度かクリックするとどのような処理をしたかのヒストリが表示されます。

setAttr "pSphereShape1.displayColors" 0;
setAttr "pSphereShape1.displayColors" 1;

このような内容がヒストリに記述されています。
このふたつのスクリプトを見ると末尾の数字のみしか違いがないので0と1で表示を切り替えているようなのがなんとなくわかります。
これはBool型といい0と1をTrue(真)とFalse(偽)として返します。
ザックリいうと0でオフ、1でオンのような感じで中間がなかったり2つの選択肢を切り替えたい時によく見られます。
では前から順に内容を確認していていくと

setAttrは「アトリビュートに設定を入れますよ」というコマンド。
pSphereShape1は今回サンプルで用意したスフィア(球)の名前。ここは選択したオブジェクトによって変わる。
.displayColorsは上記からの消去法的に頂点カラーをつかさどるコマンドというのがわかりますね。
末尾の0,1の数字は.displayColorsで表示オンオフの切り替え

とこのように紐解いていくとやっている内容としては非常にシンプルです。
お気づきの方もいるかもしれませんが「じゃあ「pSphereShape1」を選択したオブジェクトの名前に書き換えればいいんじゃないの?」と思うかもしれませんがその通りです。
ここを書き換えれば単純にコピペしてスクリプトを走らせれば表示を切り替えることもできます。
ただ都度手動で書き換えるのは手間的に現実的ではないのでそこを選択したオブジェクトに自動で適応させるのが今回の狙いです。


pythonで書いてみよう

基本的にスクリプトエディタの履歴に残るコマンドはMelで残ります。
前述したとおりMelはMaya専用のスクリプト言語です。
Melの詳しい内容はこちらで確認できます。

ざっくりと判断するには行の一番最後に「;」がついているとMelです。
自分はpythonで書きたいのでpythonベースで説明しますがシンプルに済ませたいよ、なるべくヒストリからコピペして使いたいよという方はMelメインで作業しても全然いいと思います。(記述の仕方に違いがあるのでMelで書きたい人にはこの記事はあまり参考にならないかもしれませんが)
どちらも一長一短あるので本来なら適宜使い分けられるといいかもしれないのですが自分にはそこまでの知識がないのでpythonで進めさせていただきます。
こちらでmaya pythonについて詳しく説明がされているのでわかりやすいかもです。


内容を整理しよう

まずはやりたい内容を整理します。

  • 選択したオブジェクトの頂点カラーを1クリックで切り替えたい。

  • 表示がオンの時にクリックするとオフに。表示がオフの時にクリックするとオンに。(トグル式)

  • 選択するオブジェクトは複数の事もある。

これらをクリアするようなものを作っていきたいと思います。

では早速pythonで書いていきましょう。
結論から書くと最終的にはこの様になります。
setAttrの前についているcmds.import maya.cmds as cmdsという記述に関してはさきほどのリンク先に説明があるので内容を確認してみてください。

import maya.cmds as cmds

sl_nodes = cmds.ls(selection=True)
for sl_node in sl_nodes:
    if cmds.getAttr(sl_node + ".displayColors") == False:
        cmds.setAttr(sl_node + ".displayColors", 1)
    elif cmds.getAttr(sl_node + ".displayColors") == True:
        cmds.setAttr(sl_node + ".displayColors", 0)

このぐらいの行数でできてしまいます!なんかできそうな気がしますよね。
まずはMelで書いてある

setAttr "pSphereShape1.displayColors" 0;

をpythonに書き換えてみましょう。
そうすると

cmds.setAttr("pSphereShape1" +".displayColors", 0)

このような書き方になります。
多少の違いはあれど使われている言葉(コマンド)は概ねMelと一緒ですね。
このスクリプトがちゃんと動くかどうかはスクリプトエディタ内で確認することができます。

+を押すとタブを追加できます。

ここのpythonのタブ内に上記のスクリプトを記述してctrl+aでスクリプトを全選択してctrl+enterで処理を走らせることができます。
全選択した後enterキーだけでも処理は走るのですがスクリプトがすべて消えてしまうので基本的にctrl+enterで確認する方がおすすめです。

確認ができたところで次はどのオブジェクトを選んでもこの処理が適応されるようにしましょう。
それには変数というものを使います。

Python スクリプトにおいて計算結果の数値や文字列などを覚えさせておいて、 後で使いたいときには変数というものを使います。

例えば、ある同じ数値をスクリプト中の複数の箇所で使いたい場合には、変数に数値を覚えさせておいて、数値の代わりに使うと便利です。

変数には代入という操作で数値や文字列を入れておくことができます。

http://www.not-enough.org/abe/manual/maya-python-aa07/variable.html

なにか難しく感じるかもしれませんが内容はいたってシンプルです。
今回のケースでいうと選択したオブジェクトを変数に覚えさせて、pSphereShape1にあたる部分をその変数に置き換えてしまえばいいのです。
選択したオブジェクトを変数に覚えさせるにはlsコマンドというものを使います。()内のselection=Trueは選択したオブジェクトが記録(list)されます。

import maya.cmds as cmds

sl_node = cmds.ls(selection=True)
cmds.setAttr(sl_node + ".displayColors", 0)

sl_nodeという箱(変数)にcmds.lsで選択したオブジェクトを覚えさせてそのsl_nodeに対して頂点カラーの表示を0(オフ)にさせる。という処理です。
なんだ~!簡単じゃん!と思うかもしれませんが実はこれをコピペして処理を走らせるとエラーがでてしまいます。



ややこしいルール

ここはちょっとややこしいのですが、
pythonのルール的にcmds.setAttr(sl_node + ".displayColors", 0)の+".displayColors"はlistの物としか合体できませんよということです。
listってなに?という話なんですが簡単に言えば箱(変数)の中身です。
今回はlistとしか合体できないものに対してsl_nodeという箱そのもの(str型)を渡そうとしているので受け取れませんよ、箱の中身(list)をくださいといったイメージです。
なので箱の中身を取り出して渡してあげれば処理が通ります。
listの取り出し方は何番目かを直接指定して取り出す方法と順繰りに取り出す方法があります。

オブジェクトを1つしか選んでいないのであれば一番最初のlistを指定してあげればいいのです。

(listの概要は上記のリンクがわかりやすいです。)

listを取り出してみよう

import maya.cmds as cmds

sl_node = cmds.ls(selection=True)
cmds.setAttr(sl_node[0] + ".displayColors", 0)

setAttr内のsl_nodeのお尻に[0]を追記しました。
基本的にプログラムでは0から数えるので1番最初の物は0,2番目の物は1…といった風に記述します。
これでsl_node[0]と記述するとsl_nodeという箱(変数)に記憶されている1番目の物(list)を取り出しますよ。ということになります。
ではこれで処理を走らせてみましょう。おそらくオブジェクトの頂点カラー表示がオフになったかと思います。

新しく別のオブジェクトを作成して処理を走らせても問題なく頂点カラー表示がオフになると思います。

複数選択の処理

ただここで問題になるのが複数のオブジェクトを選択して処理を走らせた際に最初に選択したものにしか適応されないことです。
なぜならsl_node[0]という指定の仕方だとlistの一番最初の物を指定しますよという書き方だからです。
常に1つしか選択しないという事であればこれでも問題ないのですが複数のオブジェクト、それは1つなのか、2つなのか、もしくはそれ以上なのかといくつオブジェクトを選択するかは基本的には状況によりけりなので定まっていないと思います。

for文を使おう

そこでもうひとつのlistの取り出し方for文を利用します。
これは順繰りにlistを呼び出してくというものです。

スクリプトを書く上でfor文if文というのはよくでてきます。なにか聞いたことあるかも…という人もいるかもしれませんね。

import maya.cmds as cmds

sl_nodes = cmds.ls(selection=True)
for sl_node in sl_nodes:
    cmds.setAttr(sl_node + ".displayColors", 0)

書き方はこのようになります。
sl_node[0]という書き方に比べると大分複雑に見えますね…
ただこれで何個オブジェクトが選択されていようが問題なくなるのです!

まず注意点としていくつか記述を変えたので気を付けてください。
変数であるsl_nodesl_nodesに書き換えました。
正直変数は分かりやすいものであればなんでもいいです。あくまで箱の名前なので。
自分はselectしたnode(選択したノード)という意味合いで今回sl_nodeという名前にしましたがsel_obj(selectしたobject)の意味で付けることもあったりするので割とその場の感覚でつけています。ただ見返した時に「これなんの変数だ?」ってならない方がいいかなとは思うのである程度分かりやすい名前がいいと思います。

話を戻して、複数選択するのでsl_nodeという名前を複数形のsl_nodesにしただけという事です。

for文を見てみると
for sl_node in sl_nodes:
という記述がされています。
これはsl_nodes内の物をいったんsl_nodeという小さい箱に小分けにしてから処理を走らせるということです。
つまり今回で言うと

for sl_node in sl_nodes:
    cmds.setAttr(sl_node + ".displayColors", 0)

というのはsl_nodes内に入っているlist(今回はスフィアとキューブ)からまずは1番最初のスフィアを取り出し、sl_nodeに入れてsetAttrの処理を行います、
それが終わったら2番目のキューブを取り出しsl_nodeに入れてまたsetAttrの処理を行います。といったイメージです。
これはlistにある分だけ行われます。

これで複数オブジェクトを選択してもちゃんと頂点カラーの表示がオフになったかと思います。



ちょっとしたpythonのルール

import maya.cmds as cmds

sl_nodes = cmds.ls(selection=True)
for sl_node in sl_nodes:
    if cmds.getAttr(sl_node + ".displayColors") == False:
        cmds.setAttr(sl_node + ".displayColors", 1)
    elif cmds.getAttr(sl_node + ".displayColors") == True:
        cmds.setAttr(sl_node + ".displayColors", 0)

この記述を見てわかる通り、for文やif文の処理を適応させたい箇所に関しては1段書き始めがズレていると思います。
これはインデントといい空白を開けてここからif文はじめますよ~みたいなルールだと思ってください。
空白さえ空いていればスペース1つ分でも2つ分でもなんでもいいのですが、スペース1個の場合や2個の場合などが入り乱れているとエラーがでてしまいます。なので空白の数は統一させておく必要があります。
また可読性を高めるためにもある程度空白をあけておく事が推奨されています。
自分はtabキー1つ分で統一しています。
またif文for文を書く際に条件を記述した行の終わりにはコロン「:」を記述することもわすれないでください。
これがなくてもエラーになってしまいます。




表示オンオフの判断

これで大分完成に近づいてきましたね。
ただ現状では頂点カラーの表示をオフにするだけしかできません。
".displayColors", 0".displayColors", 1に変えれば表示をオンにすることもできますがこれではオン用、オフ用と2つのスクリプトが必要になってしまうのでできればトグル式で1クリックでそれぞれ切り替えたいですよね。
なので「このオブジェクトは現在頂点カラーの表示がオンなのか?オフなのか?」を判断させる必要があります。

それにはif文getAttrコマンドを使います

if文の概要は上記のサイトで確認してみてください。
一部引用すると

「この条件のときはOK」でも「この条件のときはダメ」

という命令を書きたい時には if文 を使います。

という事です。
今回のケースでは

  • もし、頂点カラーの表示がオンなら表示をオフにさせる

  • もし、頂点カラーの表示がオフなら表示をオンにさせる

という事になります。

import maya.cmds as cmds

sl_nodes = cmds.ls(selection=True)
for sl_node in sl_nodes:
    if cmds.getAttr(sl_node + ".displayColors") == False:
        cmds.setAttr(sl_node + ".displayColors", 1)
    elif cmds.getAttr(sl_node + ".displayColors") == True:
        cmds.setAttr(sl_node + ".displayColors", 0)

完成コードを見てみましょう
if文の箇所を抜き出すと

if cmds.getAttr(sl_node + ".displayColors") == False:

と書いてあります。これはif文の条件文になります。
(文末のコロンを忘れずに)

条件文と聞くと堅苦しく聞こえますが、「もし〇〇なら~」といったイメージです。
この行の意味は「もしsl_nodeの頂点カラーの表示がオフなら」ということになります。
1つづつ紐解いていきましょう。
よく見るとコマンドがsetAttrではなくgetAttrになっています。
アトリビュートに設定を行うにはsetAttrを使用しました。これは意味通りsetする(設定する)ということです。
ではgetAttrは?というとなんとなくお気づきかもしれませんがアトリビュートの情報をgetする(取得する)ということです。これでアトリビュートの情報を取得することができます。
最後の==Falseですが、これはオンオフ(真偽)を判断するBoolです。
なので順を追って説明すると

もし(if)、情報を取得して(getAttr)、sl_nodeの頂点カラー表示(sl_node + ".displayColors")が0ならば(==False)
という風に解釈することができます。
それに続き

cmds.setAttr(sl_node + ".displayColors", 1)

設定します(setAttr)、sl_nodeの頂点カラー表示(sl_node + ".displayColors)をオンに(、1)
という事になります。
すこし複雑に感じるかもしれませんが1文ずつかみ砕くとこのような処理内容になります。
残りの記述にあるelif

    elif cmds.getAttr(sl_node + ".displayColors") == True:
        cmds.setAttr(sl_node + ".displayColors", 0)

条件式 1 が成り立つ場合に Python 文 1 を実行、 成り立たなければ条件式 2 を調べて、成り立つ場合は Python 文 2 を実行、 以下、同様に else if(条件式) が出てくるたびに条件式を調べて 条件式が成り立っていれば Python 文を実行してゆき、 どの条件も成り立っていなければ最後の else の部分の Python 文 n を実行します。
if 条件式1:
  Python 文 1
elif 条件式2:
  Python 文 2
else
  Python文3

つまり今回のケースでは
if cmds.getAttr(sl_node + ".displayColors") == False:
に当てはまらなかった場合にこちらにelifの方を実行してねという事です。

文章で書くと
もしsl_nodeの頂点カラー表示がオンだった場合は
if cmds.getAttr(sl_node + ".displayColors") == False:
に当てはまらないので
elif cmds.getAttr(sl_node + ".displayColors") == True:
こっちの条件で確認してみてね。もし頂点カラー表示オンだったら
cmds.setAttr(sl_node + ".displayColors", 0)
こっちの頂点カラー表示をオフにする処理を実行してね。
ということです。

それを選択したオブジェクトの数分実行したいのでこのif文for文の中に入れて最終的に

import maya.cmds as cmds

sl_nodes = cmds.ls(selection=True)
for sl_node in sl_nodes:
    if cmds.getAttr(sl_node + ".displayColors") == False:
        cmds.setAttr(sl_node + ".displayColors", 1)
    elif cmds.getAttr(sl_node + ".displayColors") == True:
        cmds.setAttr(sl_node + ".displayColors", 0)

この様なスクリプトが完成しました!
スクリプトの内容に比べて大分説明が長くなってしまいました!
自分がスクリプトを勉強した際にif文やfor文の説明を読んでも「で、それは何に応用できるんだろう?」と思ってしまいなかなか身にならなかったので実際にこういうツールを作るさいに役に立つよと実践を交えて説明したほうがわかりやすいかな?と思い今回書き起こしてみました。

実践あるのみだとは思うので普段行っているめんどくさい反復作業などをスクリプト化するのは勉強になりつつ理解を深める近道かなとは思います。
自分自身もまだ複雑なツールは作れないのでより精進していきたいと思います。

最後にシェルフに登録してみよう

最後に作ったスクリプトをシェルフに登録してみましょう。
スクリプトエディタからスクリプトをctrl+aで全選択して中クリックでシェルフにドラッグ&ドロップでおしまいです!


この記事が気に入ったらサポートをしてみませんか?