今年も色々なHDAを作りました。本記事では1年の振り返りを行いつつ、案件ベースの特殊なHDAではなく、僕自身汎用的に使っているものを作り方と合わせてご紹介していきます。
本記事はHoudiniアドベントカレンダー2020 5日目の記事です。
1年の振り返り
今年もいくつかの勉強会を開催しました。with コロナ時代ということもあり、オンラインでの開催となりました。本当はオフラインで交流を深めたいところですが、オフラインのメリットも最大限活用しつつ、今後も開催できればと思っています。
Houdiniゆるゆる会
Houdiniで作成したシーンを持ち寄り、ゆるく意見交換するお茶会をスタートしました。
内容としては毎回1つのお題を決めて、それに沿ったシーンを各々事前に作成していただき、当日お披露目と意見交換をします。 こんなところ工夫したよ!とか、こういうの作りたいけどどうやるの?といった話をします。
参加者が持ち寄ったシーンファイルはこちらにて公開しています。
隔月で開催予定なので、お時間がある方はぜひご参加ください。今年最後のゆるゆる会を企画しています。12/26(土)、お時間がありましたらぜひご参加ください。
下記はアーカイブになります。
- Houdiniゆるゆる会グループページ
- 第1回 Houdiniゆるゆる会
- Houdini: 第1回Houdiniゆるゆる会を開催しました!
- 第2回 Houdiniゆるゆる会
- Houdini: 第2回Houdiniゆるゆる会を開催しました!
- 第3回 Houdiniゆるゆる会 NEW!!!
Houdiniもくもく会
各人Houdiniの作業を通じてともに学び合うもくもく会です。
Houdiniを使っている人、学びたいと思っている人が集まって、勉強できる場として立ち上げました。 身近にHoudinistがいない。作業をする習慣をつけたい。質問をしたい等々、Houdiniに関することなら何でもOKです。 参加者同士で教えあったり、議論したり、もくもくしたり、わいわいしましょう。という感じの活動となります。
こちらは不定期開催ですが、ざっくり隔月開催できればいいな、くらいなノリでやっています。
HDAのご紹介
閑話休題、こちらが本編です。作成手法ごとにカテゴリ分けし、制作意図と合わせ説明していきます。
ちなみにHDAの一連の作成方法はもっとシンプルな方法があるかもしれません。アドバイス等いただけましたら随時更新していきます。
執筆時の環境
- Windows10 Pro
- Houdini 18.0.532
まだ現行最新版の18.5に上げていないのですでに不要になっているHDAもあるかもしれませんが、作り方・考え方の参考ということで
データ配布
解説用シーン、作成済みHDAはこちらからダウンロードできます。
Clone
Min/Max/AverageSOPを題材にご紹介します。ビルトインのノードを改変し、新しいHDAを作る方法です。
作成の背景
実際にサンプルのネットワークを組みながら見ていきましょう。
まずはattribwrangle_distノード
で第1インプットの各ポイントと第2インプットのSphere(Primitive Type: primitiveのため中心にポイントがひとつとなります)との距離をdist
アトリビュートに格納します。
@dist = distance(@P, point(1, "P", 0));
distの最大値と最小値を取得するためにMin/Max/AverageSOPを使用しますが、オリジナルのノードではAnalysis Methodを追加した際Prefixを自身で入力しなければなりません。これは不便。というわけでAnalysis Methodを指定するだけでそれに紐付いたPrefixを付与するノードを作成しました。使用法は下記の通り。
Labs Min Max Averageノード、ぼくは割とよく使うんだけどPrefixが自動で変わらないの嫌だなーと思って改造した。
— めんたいこ (@kickbase) September 27, 2020
ラッパーノードも増えてきたのであとで自前のHDA群に置き換えしよ。#Houdini pic.twitter.com/6ZEO8BxGqQ
作成方法
オリジナルのMin/Max/AverageSOPを改造し、自身のHDAとして登録します。本ノードはかなりシンプルな作りなので初めての改造にオススメだったりします。
Min/Max/AverageSOPの作成
まずはSideFX LabsのMin/Max/AverageSOPを作成します。
ノードを変更可能状態に
右クリックからAllow Editing of Contentsを実行し、内部のノードを変更可能状態にします。
内部のノードを読み解く
今回は"../prefix"
というパラメータを参照しているところを見つけましょう。
attribpromote5ノード
New Nameチャンネルのエクスプレッション
`chs("../prefix" +(detail("../meta", "iteration", 0)+1)) + chs("../attribute")`
attribpromote6ノード
Original Nameチャンネルのエクスプレッション
`chs("../prefix" +(detail("../meta", "iteration", 0)+1)) + chs("../attribute")`
ここあたりが怪しいですね!
該当箇所の書き換え
こんな感じにしたらprefix
パラメータを取り除くことができそうです。
attribpromote5ノード
New Nameチャンネルのエクスプレッション
`chs("../method" + (detail("../meta", "iteration", 0)+1)) + "_" + chs("../attribute")`
attribpromote6ノード
Original Nameチャンネルのエクスプレッション
`chs("../method" + (detail("../meta", "iteration", 0)+1)) + "_" + chs("../attribute")`
Subnetworkの作成
最終的にこのSubnetworkをHDA化します。
Subnetworkに移植
作成したSubnetwork内にMin/Max/AverageSOPの中身をコピペします。
UIの整備
右クリックからEdit Parameter Interface...を実行し、UIを作成していきます。
まずはinput #1~#4を全選択してInvisibleにしておきましょう。 *1
続いてFrom NodesタブからMin/Max/AverageSOPのUIを移植します。ただしそのままドラドロするとエラーが出てしまいます。
そんなときはサブネットの外側からパラメータを移植できるようForbid Linking Parameters from Outside this Subnetのチェックを外しましょう。
今度はドラドロできましたね。
最後に不要になったPrefix(prefix#)
を削除してAcceptしましょう。
HDA化
あとはお好みの設定でHDA化してあげればOKですが、ポイントは以下のとおりです。
Operator Nameをkickbase::min_max_average
とする。名称のプリフィックスであるkickbase::
の部分はネームスペースを表し、これを指定することでオペレーター名の衝突を避けることができます。
今後修正の可能性が多ければバージョン管理をしても良いでしょう。ここらへんのお話は本記事最後の参考文献にリンクを掲載しています。
古いMin/Max/AverageSOPの呼び出し停止
$HH下にOPcustomizeというファイル(拡張子なし)を作成し、下記設定を記述します。
ophide Sop labs::min_max_average
これでlabsネームスペースのMin/Max/AverageSOPはタブメニューから表示されないようになります。
Decoratorパターン
Attribute NoiseSOPを題材にご紹介します。
作成の背景
シーンとしてはPyroシミュレーションの下準備を想定してください。PigheadにPyroSourceをつなぎ、その後temperatureやdensityにノイズをかけるというのはよくやると思います。その際、僕がいつもやっていた間違いが「temperatureをタイポする」というものでした。
temperature、打ちにくくないですか!?
というわけで既存のアトリビュートをテキストボックスに候補として表示するUIをつけました。ここではデザインパターンで言うところのDecoratorパターンを採用して実現しています。
Attribute Noise改造した。「既存のAttributeをダイアログで選べるようにした」ってただそれだけなんだけど、ホント便利。
— めんたいこ (@kickbase) October 5, 2020
これで毎回 ”temperature” をタイポする地獄から開放されたぜ。#Houdini pic.twitter.com/4JY5dzG4Ic
作成方法
先程の例でHDA化などの細かい話はしたので、ここでは構造にフィーチャーしてお話します。
Subnetworkの作成
これがHDAになる入れ物となります。そこにNull、Attribute Noise、Outputを作成し接続します。
このSubnetworkの中にAttribute Noiseを丸々入れて、そこに機能を増やしていく方法はプログラムの世界ではコンポジションと呼ばれ、継承と同じくOOPの基本となる考え方です。
機能 | 意味 |
---|---|
継承 | A is a B. |
コンポジション | A has a B. |
継承が一子相伝で進化を続ける北斗神拳だとしたら、コンポジションは一つの村にサウザーとトキを集めてそれぞれの技を使ってもらう感じというとわかりやすいかもしれません。*2
Nullに機能を追加
get_point_attributes_in_menuと名前をつけたNullノードに既存のアトリビュートを取得し補完する機能を付与します。
Nullノードにはパラメータ名attribs、LabelにAttributesとしたStringを作成します。(最終的にSubnetworkのattribsを参照させます)
そしてMenuタブに移り下記設定を行います。
- use Menuにチェック
- use MenuをToggle(Field + Multiple Selection Menu)に設定
- Menu Scriptに下記コードを記載
inputs = hou.pwd().inputs() result = [] if len(inputs): node = inputs[0] attrs = [x.name() for x in node.geometry().pointAttribs()] result = sum(zip(attrs, attrs), ()) return result
コードの解説は割愛しますが、ジオハンドル0に入ってきた情報に対してポイントアトリビュートを取得し、TokenとLabelのセットを設定してくれるということになります。
use MenuをToggle(Field + Multiple Selection Menu)にしたことによりテキストエリアの右側にプルダウンメニューボタンが追加されるわけですね。
Attribute NoiseSOPのパラメータ設定
ほぼすべてのパラメータをSubnetworkに露出させますが、attribsパラメータのみ以下のようにNullを参照します。
`chs("../get_point_attributes_in_menu/attribs")`
HDA化
Operator Nameをkickbase::attribnoise
とします。
そして重要なポイントとして、NodeタブのDescriptive Parmにattribs
を設定することです。これによりノードの下側にDescriptive Textバッジ*3が表示されるようになります。
古いAttributes NoiseSOPの呼び出し停止
OPcustomizeに下記設定を記述します。
ophide Sop attribnoise
スクラッチ
Switch by FrameSOPを題材にご紹介します。
作成の背景
これはSwitchSOPで代用ができますが、タイムラインを汚さない(キーフレームに依存しない)Switchがほしいということで作ってみました。
ビルトインのSwitchSOPでは下図のようになります。タイムラインにキーフレームが打たれ、他のアニメーションと同期させて切り替えるにはつどタイムラインを調整する必要があります。
そして下図が作成したSwitch by FrameSOPです。キーフレームに依存せず、他のパラメータとのリンクも簡単に行なえます。
フレーム指定のSwitch作ってみた。便利なんかどうなのかわからんが楽しかったからヨシ! #Houdini pic.twitter.com/f45m0EN37H
— めんたいこ (@kickbase) October 16, 2020
既存のSwitchSOPを利用するかと思いきやスクラッチでの実装としたのでその過程をご紹介します。
作成方法
Subnetworkを作成
これがHDAになる入れ物となります。
Attribute Wrangleを作成
calculate_frame_rangeという名前にし、Run OverをDetail(only onece)
にします。
そして下記コードを実装します。
int max = chi("../frames"); i[]@frames = {}; for (int i = 0; i < max; ++i){ int frame = chi("../frame" + itoa(i+1)); append(@frames, frame); } @frames = sort(@frames);
内容はシンプルで、SubnetworkのUI上で増やしたframeの総数をローカル変数max
として取得し、forループで回してframesアトリビュート(配列)
にframeパラメータを格納していきます。
最後にsort
をしてあげることでユーザーがframeパラメータの順序を意識しなくて良い設計にしています。
Attribute Wrangleを作成
calculate_current_rangeという名前にし、こちらもRun OverをDetail(only onece)
にします。
そして下記コードを実装します。
int frames[] = detail(0, "frames"); int max = len(frames); i@current_id = 0; for (int i = 0; i < max; ++i){ if(frames[i] < @Frame){ @current_id = i+1; } }
先ほど作成したframeアトリビュートをローカル変数frames配列
に代入します。再度forループを回し、現在のフレームがどのフレームレンジにいるのかを判定します。これをcurrent_idアトリビュート
に格納します。
current_idアトリビュート
がわかれば表示したいジオメトリがわかるので、ObjectMergeSOPのオブジェクトパスに下記エクスプレッションを記載します。
opinputpath("..", detail("../OBJ_ID", "current_id",0))
これでSwitchSOPの機能を使わず目的のジオメトリを取得することができました。SwitchSOPはAllow Editing Contentsを使うことができないのと、コンポジションで実装しようとしても可変長入力を切り替える仕組みを作るのは骨が折れそうなためこのような仕組みにしました。
SwitchSOPを利用する方向にこだわりすぎることなく、様々な方法を選択できるようになりたいものです。
HDAをマルチインプット入力可にする
参考URLにあるように、Edit Operator Type PropertiesウィンドウでMaximum Inputを999などの大きな値にするとマルチインプットの入力形状になってくれます。
ただし入力順を変更したり、接続を削除したりするとうまく更新されないことがあり、強制的にインプットの情報をリフレッシュさせる方法があるかどうか調査中です。ご存じの方は教えていただけると幸いです。
安全にHDA化するには
すでに解説していますが、OPcustomizeとネームスペース機能を利用することによってビルトインのノードを編集することなく同名のノードを作成・呼び出しすることが可能になります。
オリジナルのノードが必要になったらHscript Texportを使っていつでも呼び出せるので安心ですね。その方法については次の項目で解説します。
オリジナルのHDAを呼び出す方法
オリジナルのMin/Max/AverageSOPを呼び出す方法について解説をします。
操作 | 内容 |
---|---|
Alt+Shift+T(メインメニュー > Window > Hscript Texportでも可) | Hscript Texportウィンドウを開く |
$ cd obj/WORK |
obj > WORKノード内にカレントディレクトリを移動 |
$ opadd labs::min_max_average |
labsネームスペースのmin_max_averageノードを作成する |
ちなみに上記$マークはコマンドラインの先頭という意味を表すので、実際にHscript Texportに入力する際は無視してください
オリジナルのノードはいつでも呼び出せるため安心ですね。
参考文献
- デジタルアセットのバージョンとネームスペース
- OPcustomizeを使って、タブメニューのノード表示を設定する
- Mergeノードのような大量INPUTのHoudini Digital Assetを作る
- HoudiniのParameter Interfaceで使えるPython ①
まとめ
思ったより長い記事になってしまいましたが、複数のアプローチのご紹介を行いました。みなさんの作業効率向上にお役立ていただければ幸いです。