Houdiniでパラメータを作る話

# この記事は？
**巷のパラメータ記事、上級者向けすぎ。**

ちゃうねん。もっと一歩手前、上級者さんたちが問題にしている一歩手前のところをとりあえず知りたいねん。そんな気持ちで記事を読んでいたので、簡単でよく使うところのみをさっくりとまとめてみたのがこの記事です。

# パラメータを作ろう
## Edit Parameter InterfaceとType Properties
各種ノードのここから編集できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/3af14fb0-d0ed-c227-e2f4-848dfb961d28.png)

Edit Parameter Interfaceは、インスタンスされたノードのパラメータを編集します。おなじタイプのノードを2つ作って、片方を編集しても、もう片方に反映されません。そのときどきで、個別にパラメータを持たせたいときはこちらを使用します。

Type Propertiesはその名の通り、とあるタイプのノードのプロパティを編集するもので、編集結果が同じタイプの、ほかのインスタンスノードにも反映されます。HDAを作るときはこちらを編集します。

注意するべきなのが、Type Propatiesで編集した内容は、Edit Parameter Interfaceでは編集できないことです。どちらもパラメータ編集時のUIのぱっと見がとても似ているので、初心者の頃にとても混乱しました。（「あれ？なぜかこのパラメータだけグレーアウトして消せない！」とか。。。）

## 使い方
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/4538d271-8774-910b-26dd-757a65af6443.png)

画面は、null1ノードのEdit Parameter Interfaceです。

UIは縦に3分割されていて、
左のカラムには「追加できるパラメータ」
真ん中のカラムには「今選択しているノードのパラメータ」
右のカラムには「今選択しているパラメータの設定詳細」
が表示されています。

左のカラムから追加したいパラメータを選択→真ん中のカラムへ追加されることで、パラメータが作成され、

真ん中のカラムから設定を行いたいパラメータを選択→右のカラムで設定を編集することでパラメータの詳細を設定することができます。

Discardボタンでそれまでの編集を削除、Applyでそれまでの編集をノードへ反映することができます。

##作成できるパラメータ
左のカラムから選択したパラメータを追加できますが、どのようなパラメータを追加できるのかざくりと紹介します。

**①By Typeタブ**

いろんな型のパラメータを追加できます。プレーンな状態で追加されるので基本的にここから選ぶことになるとおもいます。

**②Render Propaties、Node Propaties**

各種プロパティをノード上で編集数るために追加できます。

**③From Node**

既存のノードのパラメーターをそのまま選択中のノードにもってくることができます。これをした場合、既存のノード側のパラメータには、選択中のノードのパラメータを参照するためのエクスプレッションが自動で入ります。

##デフォルトの値を設定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/d3538297-f434-5b8a-0f82-c5c1d0639b38.png)

デフォルトの値は、右カラムのChannelsタブから設定できます。Defaultsのところに好きな数字を入れます。ノード編集時にこの値以外の値が入ると、太字で表示されます。

##レンジを決める

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/566018bc-91a8-ff3f-60b0-eef44f97aa8d.png)

floatのパラメータを作成した直後だと、ノードのスライダー上では「0～1」の値しか入れることができず、パラメータに直接数字を打ち込んではじめて負の数や1より大きい値を入れることができます。

このようなレンジも、Edit Parameter Interface上で編集できます。

鍵のマークをクリックすると、Min/Max外の値も許容するかどうかを設定できます。

####表示設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/0aa102e5-6475-f978-60cd-f804b51e6c37.png)

- **Invisible**：オンにすると、ノードのパラメータに表示されなくなります。真ん中のカラムでも非表示になってしまいますが、その場合は「Show Invisible Parameter」で一時的に表示することができます。

- **Disable When、Hide When**：特定の条件下でDisableはパラメータをグレーアウト、Hideはパラメータを非表示にすることができます。条件は右のフィールドにスクリプトを記述して設定します。

例えば、name=toggle_parmというトグルパラメータがオフの時グレーアウトさせたい場合は下記のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/a3ea5492-4244-8762-0093-7a7f4c830fe0.png)


#Pythonでパラメータをつくろう
##パラメータ追加の基本～コード編～
小難しい説明をする前に…こちらのコードをご覧ください。

```python:add_code
#ノードを取得
node=hou.pwd()

#パラメータをつくる
parm_float = hou.FloatParmTemplate("test_parm","Test",1)

#取得したノードの、パラメータを入れるためのグループを取得 
group = node.parmTemplateGroup()

#作ったパラメータをグループに追加
group.append(parm_float)

#ノードのインターフェースへ反映する
node.setParmTemplateGroup(group)
```

ついでに、削除するコードもおいておきます。

```python:remove_code
#ノードを取得
node=hou.pwd()

#取得したノードの、パラメータを入れるためのグループを取得
group = node.parmTemplateGroup()

#パラメータをグループから取得
parm_float=group.find("test_parm")

#パラメータをグループから削除する
group.remove(parm_float)

#ノードのインターフェースへ反映する
node.setParmTemplateGroup(group)
```

##hou.Parmとhou.ParmTemplate
houでパラメータを作成する際に、最初につまずきやすいポイントがあります。
それは、パラメータに関するオブジェクトが「hou.Parm」オブジェクトと「hou.ParmTemplate」オブジェクトの２種類あることです。

この二つの違いは、公式ドキュメント（日本語）によると以下のようになります。

###hou.Parm

[https://www.sidefx.com/ja/docs/houdini17.5/hom/hou/Parm.html]

> ノードのパラメータ。各パラメータは、そのノード内で固有の名前を持ち、パラメータタプル内に存在します。

[https://www.sidefx.com/ja/docs/houdini17.5/hom/hou/ParmTuple.html]

>タプル内の各<b>パラメータは値を保存します。</b> 異なるノード内の異なるparmタプルのインスタンスは、独自のパラメータ値のセットを保存します。

つまり、hou.Parmはhou.ParmTupleのエレメントとしてノード内に実際に存在し、それぞれのhou.Parmが固有の名前と値を持っています。

>パラメータタプルは、<b>そのノードで保存された特定の値を保存します</b>。 しかし、パラメータタプルの記述は、hou.ParmTemplateで表現されます。 パラメータテンプレートは、パラメータタプルのタイプ、デフォルト値、範囲などを記述します。

hou.ParmTupleは「特定の値を保存」し、hou.ParmTemplateはそのhou.Parmが「どんなタイプでどんなデータを保持できるのか？を定義している」ということです。
ちょっとまわりくどくてわかりにくいですね。

###hou.ParmTemplate

[https://www.sidefx.com/ja/docs/houdini17.5/hom/hou/ParmTemplate.html]


> ParmTemplateは、それが保持するパラメータとデータのタイプを記述しますが、パラメータの現行値を保存しません。 hou.ParmTupleオブジェクト内のhou.Parmオブジェクトのみが実際にパラメータ値を保存します。<b> hou.ParmTupleをParmTemplateのインスタンスとして考えることができます。</b>

<b> hou.ParmTupleをParmTemplateのインスタンスとして考えることができます。</b>
だいじなことなので2回言いました。

>ParmTemplateオブジェクトが単なるデータコンテナで、それらのオブジェクトは<b>Houdini内のオブジェクトと何も関係性がない</b>ことに注意してください。

「何も関係性がない」というのは、hou.Parmは実際にノード上に存在している一方、hou.ParmTemplateはノード上に情報として持たれてはいるが存在していません。つまり、hou.ParmTemplateをいくら編集しても、（その結果を反映させる処理を走らせない限りは）ノードやその他hou.Parmに影響を与えられません。

つまり…どういうことだってばよ…？というのは簡単なコードを見ていけば、理解の助けになるかと思います。

##パラメータ追加の基本～解説編～
これらを踏まえたうえで上記コードを一つずつ見ていきましょう。
### ①ノードを取得
```python
node=hou.pwd()
```
hou.pwd()によって、このコードをもつノード自身のhou.Nodeオブジェクトを取得します。

###②パラメータをつくる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/545e6048-b857-ed49-905a-d75d4e284fcf.png)

これから追加したいParmTemplateオブジェクトを作成します。

```python
parm_float = hou.FloatParmTemplate("test_parm","Test",1)
```

上記で「ParmTemplateは保持するパラメータとデータタイプをもつ」と説明しましたが、ここで実際に記述しています。
もうすこし簡単な言葉にすると「どのようなパラメータか」の情報を持っていますが、「今このパラメータにどのような値がはいっているか」の情報は持っていません。後者の情報は、この後でhou.Parmオブジェクトになった後にもつことができるようになり、そこではじめてhou.Parmオブジェクトを評価し値を得ることができるようになります。

###③取得したノードの、パラメータを入れるためのグループを取得
```python
group = node.parmTemplateGroup()
```

###④作ったパラメータをグループに追加
```python
group.append(parm_float)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/f8ff62c4-72a1-8fcd-e74e-d824c90ec2e5.png)

今回の例ではhou.ParmTemplateオブジェクトが1つしかないのですが、これをなん十個も作るときもあれば、特定のフォルダに入れたり特定の順番にならべたりしたいときもあると思います。なので、hou.ParmTemplateオブジェクトをまとめるためのからっぽのグループをここで定義しておきます。

###⑤ノードのインターフェースへ反映する
```python
node.setParmTemplateGroup(group)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/1c9bcbf1-a62a-fcbc-9849-ff103e5e4b76.png)

作成したParmTemplateGroupを、ノードに反映させます。ノードに反映されてはじめて、設定したものがhou.Parmとなり、値を入れたり評価したりが可能になります。
図では箱をひっくり返しているので、setParmTemplateGroup()したらParmTemplateGroup()がからっぽになるイメージになってしまいますが、実際は空っぽになりません。

つまり、ノードには同じ意味をもったhou.ParmTemplateとhou.Parmが存在することになります。
これは、パラメータを消したいときに実感することができます。

###⑥パラメータを削除する
```python
#パラメータをグループから取得
parm_float=group.find("test_parm")

#パラメータをグループから削除する
group.remove(parm_float)

#ノードのインターフェースへ反映する
node.setParmTemplateGroup(group)
```

既存のグループから消したいhou.ParmTemplateを指定し、hou.ParamTemplateGroupから削除、その結果をインターフェースへ反映させます。
removeした直後ではノードのパラメータエディタ上では削除結果が反映されません。.setParmTemplateGroup()をしてはじめて削除されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/9b3be2d4-05bc-940c-74d2-bff8504ff2bf.png)

ParamTemplateGroupはParmを管理するためのParmTemplateの名簿で、実際に反映されるのはsetParmTemplateGroupと覚えておくと最初のうちはよいかもしれません。

#おまけ
houモジュールを使用したPythonでの記述がわかっても、「そのスクリプトをいつ実行するのか？」が、初心者にとっては地味なつまずきポイントだと思います。
（わたしも、最初はコード書く→勝手に実行される→無意味なテスト用パラメータが無限増殖するということを繰り返していました）

##解決案
わかりやすいのは、ボタンパラメータのコールバックスクリプトで、上記コードを実行することだと思います。
###①Stringパラメータを作成します。
Multi-line Stringにチェックを入れて、languageをPythonに設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/252f06c5-8baa-a3f2-04ed-89445b05da66.png)

###②ボタンパラメータを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434798/c3c5dcf9-f34a-eb3c-6aa0-087f06129c50.png)

Callback Scriptに下記コードを記載します。add_codeの部分はコードが記載してあるStringパラメータのnameにあわせます。

```python
exec(hou.evalParm('add_code'))
```
###なにがうれしいのか
この方法ですとボタンとコードの位置が近くて気軽にポチポチ試すことができます。
ある程度しあがったらStringのパラメータをInvisibleにするなり、HDA化の際にPythonモジュールに移動するなり、その後の対応もまあやりやすいのではないかと思います（たぶん）

HDA作成時は、パラメータをまとめたnullノードで編集→subnet化してそのノードのTypePropatiesを編集という手順になるかと思うのですが、階層をまたいでノードとType Propatiesを行き来するのは地味に面倒くさいんですよね。
良い方法があればぜひ教えていただきたいです。

#おわりに
今回の記事は以上です。
パラメータを作成する方法はいくつかありますが、そのうち（おそらく）ベーシックなものの基礎的な部分をまとめてみました。
記事を読んでくれた方の参考になれば幸いです。指摘やコメントなどいただけると嬉しいです！