Houdini: FontSOP機能追加 - はじめてのPython
もうアドベントカレンダーの季節とか嘘でしょう…?
気を取り直して皆さんいかがお過ごしでしょう。僕は今年もHoudini三昧の毎日でした。
今回はFontSOPに機能追加を施す方法についてお話をしたいと思います。
本記事はHoudini Apprentice アドベントカレンダー2024 1日目の記事です。Apprenticeということでできるだけシンプルな題材を取り上げ、コードについても手厚めに解説しています。
ちなみに今回から技術ブログはZennに移行しました!よろしくお願いします。
データ配布
ツール制作の背景とその特徴
FontSOPはテストジオメトリの作成や表現そのものにもよく用いられますが、フォントを選ぶ際、メニューリストから毎回選択しなければならないことがUIとして不便に思っていました。
そこで任意の文字列からプレビュー用の文字列ジオメトリを一括で生成し、使用したい文字列をクリックすることで好みのフォントがセットされたFontSOPに切り替わるツールを作成しました。
こちらは続くHoudiniアドベントカレンダー19日目の記事でご紹介する予定なのですが、もっとシンプルに次のフォント・前のフォントを選べるボタンUIを作るだけでも高いUXになるのではと思い作成してみました。
振り返ると、本ツールはHoudini Python初心者向けに良い題材だと思われたので、Apprenticeのアドベントカレンダーでご紹介しようと思った次第です。
動作は下記動画をご参考ください。
HoudiniとPython
HoudiniではPythonを様々な用途で使用可能です。シェルフツールを作って作業を効率化したり、ノードを生成したり、もちろんアトリビュートへのアクセスも可能です。(VEXを用いた並列処理で行うかどうかを考慮する必要はありますが)
今回はノードのパラメータを操作するという比較的シンプルでわかりやすい使用方法をご紹介しましょう。
ツール構成の説明
ここから実際のネットワークを見ながらツール構成を順にご説明します。本記事は初心者向けにPythonコードの説明や手順をメインに行いますが、HDAの作成方法には触れません。
パラメータ
最初に最終的なパラメータを確認しておきましょう。FontSOPと比較していただければわかりますが、単純に<ボタン
と>ボタン
を追加しただけです。
これから僕らが作るツールではボタンを押すたびにメニューリストから「次へ」「前へ」と順にフォントを切り替える機能を追加すればいいというわけですね。
ツールのネットワーク
まずはAllow Editing of Contents
を実行せずHDAの中にダイブしてみましょう。FontSOP
とOutputSOP
しかありませんね。OutputSOP
は何もしないノード(出力されるジオメトリを決定する機能だけを有しているノード)ですので、このHDAの中身は実質的にFontSOP
だけしか入っていないということになります。
FontSOPのパラメータをすべて露出
皆さんがこの手順を実際にやってみたい場合はHDA作成後、Edit Operator Type PropertiesウィンドウのParametersタブ
でFontSOP
を丸ごとExisting Parameters
にドラッグ・アンド・ドロップしてあげましょう。
これでFontSOP
のパラメータが全てHDAで露出されることとなります。
前へ、次へボタンの作成
font
パラメータの前と後ろにprev
パラメータとnext
パラメータを作成します。パラメータの設定は下記スクリーンショットをご参考下さい。
ここで重要となるのがCallback Script
の項目です。下記のように設定されていることを確認して下さい。
パラメータ | Callback Script |
---|---|
prev | hou.phm().change(kwargs, -1) |
next | hou.phm().change(kwargs, 1) |
そもそもCallback Script
とはなにかという簡単な説明をすると、ボタンが押されたときに実行されるプログラムのことで、hou.phm()
の部分で「HDAに埋め込まれたスクリプト」を指し示し、change
で「change関数を実行してね」とHoudiniに伝えています。
そして今回の一番のポイントが引数であるkwargs, -1
とkwargs, 1
の部分です。ここが異なっているおかげでchange関数
の挙動をコントロールできる(前に、次にを切り替えられる)というわけですね。
change関数のコード解説
Edit Operator Type PropertiesウィンドウのScriptタブ
にPythonModule
を作成し、ここにコールバック関数であるchange
を定義しています。
HDA に Python モジュールを登録する手順については下記BornDigitalさんの記事にまとまっていたためご参考下さい。
続けてコードを見ていきましょう。説明のためコメントを多く入れていますが、コメントを外すととても短いことに驚くかと思います。はじめてPythonを書くのであれば、チャレンジしやすい分量ですね。
change関数の実際のコード
def change(kwargs, action):
"""
FontSOPのfileパラメータのメニュー項目を切り替える関数
Args:
kwargs (dict): ノード情報を含む辞書
action (int): メニュー項目のインデックスを変更する値
"""
# ノードのfileパラメータを取得
parm_file = kwargs["node"].parm("file")
# 現在選択されているフォントを取得
current_label = parm_file.eval()
# メニュー項目から重複とセパレータを除外してソート
labels = sorted({l for l in parm_file.menuLabels() if l != "_separator_"})
# 循環的にインデックスを更新してフォントを切り替え
next_index = (labels.index(current_label) + action) % len(labels)
parm_file.set(labels[next_index])
change関数からコメントを外したコード
def change(kwargs, action):
parm_file = kwargs["node"].parm("file")
current_label = parm_file.eval()
labels = sorted({l for l in parm_file.menuLabels() if l != "_separator_"})
next_index = (labels.index(current_label) + action) % len(labels)
parm_file.set(labels[next_index])
なんと6行ですね。では解説を続けていきましょう。
def change(kwargs, action):
ではchange
関数の定義をしています。引数にkwargs
とaction
をとる関数だよということですね。
parm_file = kwargs["node"].parm("file")
の部分ではノードのfileパラメータを取得しています。ボタンを押されたときにkwargs
という情報がこの関数に渡ってくるのですが、これは辞書型のデータで、インタラクション発生時の多くの情報がここでわかるようになっています。
kwargs["node"]
がHDAそのもの(ノード)を表していて、そこからパラメータを探すことができるわけですね。(今回はfile
パラメータを取得しました)
current_label = parm_file.eval()
では現在選択されているフォントを取得しています。eval
というメソッドを使用するとパラメータが持つデータを取得することができます。
labels = sorted({l for l in parm_file.menuLabels() if l != "_separator_"})
の部分ですが、少々ややこしいですね。Pythonに慣れていないと入れ子構造のワンライナーには最初戸惑いますが、ここで行っている処理は下記をまとめたものです。
-
file
パラメータのメニューリストから_separator_
を取り除く - メニューリストから重複を取り除く
- メニューリストをアルファベット順に並び替える
これでフォントを順番に切り替える下地ができました。ちなみに上記では「メニューリスト」と簡単に記載していますが、正確にはデータ型を変更する処理が入っています。興味がある方は調べてみて下さい。
next_index = (labels.index(current_label) + action) % len(labels)
の部分でメニューリストにアクセスするインデックスを取得しています。ポイントは+ action
のところで、押されたボタンによって(つまり引数action
の値が+1
なのか-1
なのかによって)インクリメントされるのかデクリメントされるのかが決定されます。
また% len(labels)
の処理も気をつけたいポイントです。これを行わないとメニューリストを循環的にアクセスできません。どんどん>ボタン
を押し続けていくとメニューリストの最後を超えてアクセスしてしまうという問題が起こるわけです。
最後にparm_file.set(labels[next_index])
の部分でパラメータを更新しています。
こんな短いコードでもひとつひとつ見ていくと大変かもしれませんが、printデバッグ
などを利用しながら現在どんな処理が行われているかを順を追って確認すれば、決して理解できないものではないでしょう。
まとめ
最後まで読んでくださった方、ありがとうございます。ビルトインノードであっても「不便だな」と思ったところは改善していくと、より理解が深まったり多角的な視点を持つ事ができるようになったりします。
もう一歩難易度の高いツールに関しては12/19にご紹介するとします。
ではでは、来年も素敵なHoudiniライフを!
開発環境
- Windows10
- Houdini 20.0.688
Discussion