LoginSignup
35
20

More than 5 years have passed since last update.

HoudiniのParameter Interfaceで使えるPython ①

Last updated at Posted at 2018-12-15

この記事はHoudini Advent Calender 2018の15日目の記事です

今回は、HoudiniのParameter Interfaceで使えるPythonの話を書こうと思います
基本的には自分の経験上で実装する時にちょっと調べた事普段良く使う構文などのTips が中心で
できること全体をカバーする内容ではありません。
どうやってやるか調べた事あるけど、わからなくて挫折したネタが見つかるかも。という程度でみて下さい。

Menu Script

Menu Scriptを使って inputに応じてMenuの内容を変える方法 を実装してみたいと思います

まず、Menu Scriptは
Orderd Menu Type であれば必ずONになっていますが
String Typeでも use Menuにチェックを入れると使えるようになります

ここに書く内容として必ず守らなければいけないのが
かならず 文字列の配列を返す ということです。
その処理を書き忘れるとWarningが出まくって面倒な事になります・・

厳密には配列には TokenLabel の2つの要素を 交互にいれる必要があります
TokenとLabelとは、Menu Itemsを直接指定する時に使うこの時の組み合わせのことです

また、もう1つ注意しなければならないのが、返す配列の中身は

[ [token, label], [token, label] ]

では無くて

[ token, label, token, label ]

のようにただ交互に繰り返して並べるだけという事です。
tokenとlabelはペアだと思って直感的に前者の書き方をしそうになりますが違います。


Menu Script Sample

実際にどんな書き方をして要素を作ると簡単かサンプルをあげてみます。
例えば Tokenを数字にしてLabelを入れる場合 は
enumrateとかを併用すると便利です

labels = ["box","sphere","grid"] # 使いたいメニューの項目
result = []
for (i,v) in enumerate(labels): # iに数字が入る(0スタート)
  result.extend([i,v])
return result

または TokenとLabelを同じにしたい 場合は
zipとかsumを使って

labels=["P","N","Cd"] #使いたいメニューの項目、attributeとかはtoken同じにする場合が多い
return sum(zip(labels, labels),())

とかやるとまとまります。

以上を踏まえて、冒頭にあったinputに応じてMenuの内容を変えるサンプルを作ってみます

入力ノードのPointAttribute一覧をMenuに表示するサンプル

以下のように書きます。

inputs = hou.pwd().inputs() 
result = []
if len(inputs):
    node = inputs[0] # inputは複数あるのでとりあえず1番目の例
    attrs = [ x.name() for x in node.geometry().pointAttribs()]
    result = sum(zip(attrs, attrs),())
return result

かなりよく使う仕組みだと思うので、やってる人も多そうですが紹介してみました。

ちなみに、小ネタですがString TypeのパラメーターをMulti Lineにして
Language を VEXとかPythonにしてMenu Scriptを仕込むと
Wrangleノードでお馴染みの オレオレSnippet が作れます

Action Button Script

続いて ActionButton にスクリプトを書いてみたいと思います
ActionButtonとはMenuの横でよく見かけるこういうやつです

Action Buttonの設定はParameter Descriptionの一番右のタブにあります
とりあえず何か書けばボタンを押した時にそのスクリプトが実行されます
(何も書かれていないとボタンが出ません)

たとえば、ActionButtonを押した時にParameterの値を変化させたい場合は

node = kwargs["node"]
node.parm("変えたいパラメーターの名前").set("変えたい値")

という感じで書けば大丈夫です。

ちなみに、先ほどMenuScriptではノードの取得に hou.pwd()を使っていましたが
ここでは kwargs["node"] というのを使っています。
どちらも自分のノードオブジェクト( hou.Node )を返してくれるのですが、どっち使えばいいの?と
おそらく混乱する人も居るのではないかと思います

基本的にHoudiniでPythonを扱う時、ParameterInterface関連でイベントが発生する時は
kwargsに値が格納されてます。イベントによって中身がちょっと違うのですが、何が入っているかはこちらに書かれています
http://www.sidefx.com/ja/docs/houdini/hom/locations.html
もしくは print kwargs と書いておけば何が入ってるかわかりますね。大体、自分のNodeとか押したParmeterの名前とかです。

実は、さっきのMenuScriptもkwargsが使えます
ただ、Menu Scriptは hou.pwd()でもkwargs["node"]でも自分のノードが返ってくるのですが、
Action Buttonは hou.pwd()をしてもルート("/")のノードが返ってきます。
まぁ、なんとなくActionButtonのイベントは評価されている場所が違うのだろうというのがわかると思いますが

誤解を恐れず言ってしまうと
何かのアクションに対して実行するときはkwargs["node"]
常時発動系はhou.pwd()
という感じで覚えておけば大体通ると思います。例外あったらすみません。

hou.pwd → Expressionとか
kwargs → Shelfとかボタンとか

そんなかんじですね
脱線しましたので、話を戻して

ActionButtonでViewPortから頂点を選択してパラメーターに反映する

というのを作ってみましょう。

Viewportからオブジェクトを選択するには hou.SceneViewer Class
http://www.sidefx.com/docs/houdini/hom/hou/SceneViewer.html
の関数を使えば良いのですが、まずViewportのアクセスにはtoolutilsモジュールが便利なのでそれを使いましょう

とりあえず書くとこんな感じです

import toolutils
node=kwargs["node"]
selection = toolutils.sceneViewer().selectGeometry(geometry_types=(hou.geometryType.Points,))
node.parm("parm").set(selection.mergedSelectionString())

はい、引数が多いので補足します

hou.sceneViewerのselectGeometry()はドキュメントに選択するタイプを指定するにはどうしたらいいのか? が書かれていません。
ただ、良く読んでよく調べるとgeometry_typesという引数にhou.geometryType*の値を与えてあげれば良いことがわかります
hou.geometryType
http://www.sidefx.com/docs/houdini/hom/hou/geometryType.html

ただ.hou.geometryTypeを1つ指定すれば良いのかというとそうではなくて
Tupleで、かつ(hou.geometryType.Points , ) としなくてはいけないので知らないとハマります
geometry_types= (hou.geometryType.Points) ではダメです。

また、hou.SceneViewerの selectGeometry()で頂点や面を選択しても
返ってくるデータはhou.GeometrySelection なので
http://www.sidefx.com/docs/houdini/hom/hou/GeometrySelection.html
mergedSelectionString()を使うと、空白とか含めて最適な文字列を返してくれます。

pythonに慣れてる人は大丈夫だと思いますが、
引数や戻り値の型が見えづらいので、気をつけながら書く必要がありますね。

ただ、頂点とかオブジェクトを選択してParameterに入れるのも比較的よく使うネタだと思うので紹介してみました。

続いて

HDA で使えるPython の書き方

を見ていこうと思います
これが一番需要がありそうですが、同時に人によってやり方が違いそうな気がします。
あくまで私の個人的なやり方になりますので、参考程度でお願いします。

HDAにScriptを埋め込みには、TypePropertiesのScript欄を使います

まず左側でEvent handlerを定義する必要があります
先ほどのkwargsのリンクと同じページに書いてあるので、順に読んで頂いた方は気づいたかもしれませんが
もう一度リンクを貼っておきます
http://www.sidefx.com/ja/docs/houdini/hom/locations.html#asset_events

おそらくよく使われるのは
OnCreated (HDAを作成した時に実行される)
OnInputChanged (Inputが変わった時に実行される)
PythonModule (汎用モジュール)
あたりだと思います

それぞれに役割を持たせてクラスや関数を書くことが出来ます
個人的にはPythonModuleにまとめて書くことが多いので
そういった使い方を説明します。

さっそくサンプルとして

入力したオブジェクトのタイプに応じて色を変える

というHDAを作ってみましょう。
HDA自体の作成方法は省略して、Scriptの登録から説明します。

まず色を変える関数を作っておきましょう

def changeColor(node, rgb):
    node.setColor(hou.Color(rgb))

名前は何でも良いですが関数としてPythonModuleに書いておきます

次に入力に応じて切り替えて見ようと思いますが
Polygonなら赤
PackedGeometryなら緑
VDBなら青
にしてみましょうか
*便宜上 1つ目のPrimで判断することとします

OnInputChangedイベントを作り、その中に以下のように書きます

node = kwargs["node"]
inputs = node.inputs()

if inputs:
    geo = inputs[0].geometry()
    type = geo.prims()[0].type()

    if type == hou.primType.Polygon:
        node.hdaModule().changeColor(node,(1,0,0))
    elif type == hou.primType.PackedGeometry:
        node.hdaModule().changeColor(node,(0,1,0))
    elif type == hou.primType.VDB:
        node.hdaModule().changeColor(node,(0,0,1))

こんな感じのHDAになります(TypeColorというのがサンプルで作ったHDA)

繰り返しになりますが、自分はHDAで使う関数は大体PythonModuleにまとめてしまいます。
この場合、OnInputChangedのイベントからPythonModuleの中にある関数を呼ぶのに
node. hdaModule() というのを使ってますが
このようにPythonModuleにある関数は他のイベントや、イベント外から呼べるのに対して
OnCreateなど特定のイベント内に書いた関数は他から呼び出せない為
結果的にPythonModuleに全て書いたほうが汎用性が高くなるからです

例として、押したら色が黒になる、というボタンを作ってみましょう
ついでに、CallbackScriptというのを説明します

CallbackScriptからPythonModuleを使う

CallbackScriptはParameterDescriptionのところにあります

HscriptとPython両方が使えるので、右端でPythonにしておきます
Pythonはこんな感じに書いてますが

hou.phm().changeColor(kwargs["node"],(0,0,0))

先ほど使ったnode.hdaModule()でPythonModuleを呼び出すのを省略して
hou.phm()というのが使えます
こういう良く使う構文の省略形を用意してくれてるのがSideFXの良いところですね

結果的に、ボタンを押した時やインプットを切り替えた時に、それぞれ同じ関数を呼び出して使うことが出来ました。
CallbackScriptは1行で済む処理ならそのまま書いちゃうことも出来ますが
処理を変更したい時に、OnInputChangedやCallBackScriptに書いた内容をそれぞれ編集するのは面倒ですよね
なので、なるべくpythonModuleに書いて、同じ関数を呼び出すようにしています。
どんな処理をさせるかとか、どんな引数にするかとかでも違ってくると思いますが、一応おすすめの方法でした。

で、また小ネタなんですが・・(多すぎですが)
Subnetでよく見るコレ色を付けるとノードのラインの色が変わるのはご存知の方も多いと思いますが

コレの名前、1 2 3 4なんですよね・・・ この記事書いてて気づきました。

node.item("1")とかやると取ってこれます
先ほどのにitemの色も変えるように書き加えるとこんな感じです

def changeColor(node, rgb):
    node.setColor(hou.Color(rgb))
    node.item("1").setColor(hou.Color(rgb))

はい、カラフルですね
実用性あるかわかりませんが、エラーなどの表示に良いかもしれません。

というわけで、文字が多い割にネタの少ない記事になってしまいましたが
Parameter Interfaceで使えるPython の話でした

実はまだ

レイアウトをPythonで保存して複数のHDAで使い回す方法

オススメのParameter Interface+Sciprtのパーツ集

の話をしようと思ったのですが
この記事の公開が遅くなってしまったのと、アドカレにまだ空きがありそうなので、別の日に書くことにします
(追記: 24日の記事として投稿しました https://qiita.com/yuya_torii/items/6f07a5708c3f45ad632c)

という事で一旦終わります
質問・ご相談などあればこちらに気軽にどうぞ。
toriivfx@gmail.com

35
20
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
35
20