Houdiniの中のPython

# Houdiniの中のPython
Houdini Apprentice Advent Calendar 2023が空きまくってるので書いてみました。

HoudiniもめでたくさっくりPython3系に移行出来ましたし
今回はHoudiniの中のどの辺りでPythonが使えるのか見てみましょう。


## シェルフのボタンのPython
画面左上にシェルフがあってボタンがずらっと並んでますよね。
あそこのボタンは実は中身がPythonなんです。
CreateタブのBoxを右クリックしてEdit Tool...を選んでみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/bc52bcde-3833-d9b3-1fde-34d43ee114b1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/9ec3110f-5d3f-7418-812b-51a716aa9b69.png)
こんな風にPythonがボタンに仕込まれています。

自分のシェルフを作りたかったら+ボタンを押してNew Shelf Tabで新しくシェルフタブを追加できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/b4d14993-baa0-1927-82c5-2bd1fcc12ac4.png)
適当に名前を付けてシェルフタブを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/7322dfa4-4249-d762-c5b7-aa9ccbd57391.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/9daae659-f3c9-a491-e9b6-a9c4eedd01dc.png)

このシェルフタブにいろいろ登録出来ます。
例えばこういう手で組むと微妙に時間がかかるけどHDAにするほどでもないしhipとして保存して
Houdiniを同時に開いておいてコピー＆ペーストするのめんどくさいけどそこそこ頻繁に似たようなの組んじゃうなんていう中途半端なノードネットワークがあったとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/f618ff75-b80d-1bf7-9532-4513aac4b1bb.png)


これらのノードをまとめてドラッグすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/be122fd6-d0fe-b585-3fb3-22a526f8f1ce.png)
こんなダイアログが出るのでボタンの名前を決めてAcceptを押すと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/1018e3a8-f95a-bca7-fae2-16cc32d38732.png)

こんなボタンが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/b9ebcfe2-ed93-d030-5c15-bf33b2524556.png)

押すとドラッグしたノードネットワークと同じものが生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/8e78611b-8348-52f3-2c72-614193b833f7.png)

ボタンの中身を見てみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/4c89c841-18f1-f66e-eb01-60e74130fb6c.png)
Pythonが記述されています。
厳密にはHScriptが下の方で文字列として表記してあってそれが実行されている感じなんですけどね。

他にもシェルフ上の何もない所で右クリックして先にボタンだけ作っておいて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/ce5738ac-c491-b3f0-e32f-03d2ee157b7a.png)


MantraのレンダリングのボタンをドラッグするとそのUIを表すPythonが記述されるので
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/d1601a12-202b-c53a-509c-ec1aeba2ec3b.png)

そこに <font color="red">**.pressButton()**</font> を追加しておくと順々に実行してくれるボタンが作れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/9c6304e5-642b-1dee-935f-55cf59a4bc78.png)

## PythonSOP
次はジオメトリコンテキスト内で呼び出せるPythonSOPです。
最初は何を書いたら良いか分からないどういう風に書いたらいいか分からないという方も
いらっしゃると思いますが右上の下向きの三角を押すといくつかのスニペットが用意されていますので
まずはそれを見てどんな風に書けるのか確認してみると良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/f4696d1b-adb8-40ab-3ed3-e2217d03e1e9.png)

例えば Move Points Up ではどんな風に各ポイントや座標にアクセスすればいいかの例が見れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/6400f8c8-410e-9237-4300-19b91a68ede7.png)

wrangleと違って処理の速度はそんなに速くはないですが様々なモジュール群を活用できるので
やれることの幅が広がります。

PythonSOPではわざわざimportしなくても使える **[houモジュールのドキュメント](https://www.sidefx.com/ja/docs/houdini/hom/hou/index.html)** も参考に
いろいろ書いてみると面白いでしょう。
例えばgridSOPのpointのCdアトリビュートを画像として吐き出す様なコードも簡単に書けます。
<pre>import hou
from PIL import Image
node = hou.pwd()

ySize = hou.evalParm('../baseGrid/rows')
xSize = hou.evalParm('../baseGrid/cols')
colorBaseR = hou.evalParm('../initRGB/basecolr')
colorBaseG = hou.evalParm('../initRGB/basecolg')
colorBaseB = hou.evalParm('../initRGB/basecolb')
baseR = int( 255*colorBaseR )
baseG = int( 255*colorBaseG )
baseB = int( 255*colorBaseB )
base = (baseR,baseG,baseB)
img = Image.new("RGB",(xSize,ySize),base)
pixels = img.load()

geo = node.geometry()

for num,point in enumerate( geo.points() ):
    col = point.attribValue("Cd")
    colR = int( col[0]*255 )
    colG = int( col[1]*255 )
    colB = int( col[2]*255 )
    
    pixels[ num%xSize, int(num/xSize) ] = (colR,colG,colB)
rimg = img.rotate(180)
rimg.save(hou.evalParm('./outputimage'))
</pre>
ただPythonSOPでこれ書いちゃうとSOPをクリックしたタイミングで実行されたりするので
HDAのコールバック関数とかで書いた方が無難ですね。

## Pythonでエクスプレッション
よくbboxやcentroidなどパラメータのところに使う関数がありますがあれが
**[エクスプレッション関数](https://www.sidefx.com/ja/docs/houdini/expressions/index.html)** とよばれるやつです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/9bf71d18-bb1b-6e6b-73dc-120715ee65c5.png)
普通はこの緑色のエクスプレッションになります。
(※パラメータの所をクリックすると数値とエクスプレッションの表記がトグルされます。)
これがHScriptエクスプレッションと呼ばれるものです。
ここのエクスプレッションの表記方法をPythonに切り替えることも出来ます。
詳しくは **[ここ](https://www.sidefx.com/ja/docs/houdini/hom/expressions.html)** を見て頂くとして

19.5まではあったコレ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/0beaf44e-331d-9547-4c1a-181581dbce20.png)
が20.0.533ではなくなってますね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/1af8dfe9-d284-44a0-4549-ece9161f4f12.png)
じゃーPythonでエクスプレッション書けないのか？っていったらそんなことはなくてですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/7b649779-269c-d0a9-7ba8-719f42778759.png)
一応これでPythonに切り替えることが出来ます。
エクスプレッションのデフォルト言語を切り替えるっていう概念があまり
うまくいってなかったからボタンを取っ払ったんじゃないかなーなどと想像しちゃいますが
デフォルトはHScriptで部分的にPythonで書きたきゃ書けば？みたいなスタンスなんじゃないかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/e2417eda-3466-d666-9fc3-1705a7cc0c09.png)
Pythonにエクスプレッションを切り替えるとパラメータの色がこんな風に紫色になります。
「え？こんなの初めて見た！気持ち悪い！」と思う人もいるかもしれませんが
ちょいちょいデフォルト値で設定されてるパラメータで見かけますよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/ae75b48b-1725-52c4-87ad-2ad7f6cc6c68.png)
こういうのとか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/cc95ec30-4bde-4e8a-cdec-947742b80474.png)
こういうのみたいに複数行にまたがって書くようなやつとか。
ドキュメントには

:::note
複数行のパラメータエクスプレッションを作成した場合、それは 関数ボディ として評価されるので、 returnを使用しなければなりません 。
:::

と書かれてるのでreturnで返り値をちゃんと指定しましょうね。
HScriptだと
`if( ch("ty") > 0, ch("tz"), if(ch("ty") == 0, 0, ch('tx')) )`

```
if ch("ty") == 0:
    return 0
if ch("ty") < 0:
    return ch("tx")
return ch("tz")
```
HScriptだと1行に長く書くので可読性が低いですがPythonだと分かりやすいですね。

## Pythonシェル
WindowメニューのPython Shellから呼び出せます。(Alt+Shift+P)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/792bbf28-f04c-94ae-ab03-9f446805a6e5.png)
例えばこんな風に
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153910/10e15e28-282e-741d-93fd-d5fb85e609a8.png)
選択しているものの名前とかHDAとか定義とかを表示したり
```
for i in dir( hou.selectedNodes()[0] ):
    print(i)

```
どんなメソッドがあるか調べたりprintでいろんな情報を表示したり
```
import hou				
import re				
				
oldPath = 'D:/maya/projects/hogehoge/data/'				
newPath = '$HIP/fbx/'				
fileNodes = hou.sopNodeTypeCategory().nodeType("file").instances()				
				
for i in fileNodes:				
	targetParm = i.parm("file")			
	currentPath = i.evalParm("file")			
	newFullPath = re.sub( oldPath, newPath, currentPath )			
	print( currentPath )		# 最初は現状把握	
	#print( newFullPath )			
	#targetParm.set(newFullPath)		#保険で最初はオフ	
 ```
 リソースのパスを書き換えたりいろいろ出来ると思います。
 こういうのは普段の作業でよくやるのでシェルフのボタンでもいいですかね。

 ## HDAのコールバックスクリプト
 HDAのボタンを押した時に実行されたりHDAが呼ばれたタイミングで実行されたり
 インプットをまるっと何らかのプリミティブグループにしてしまったり出来るアレです。
 **[ここ](https://www.sidefx.com/ja/docs/houdini/hom/locations.html#asset_events)** を見て下さい。
 あと一番有名なHDAのコールバックの資料が[ポリフォニー山部さんのこの資料](chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/http://cdn2.gran-turismo.com/data/www/pdi_publications/CEDEC_2019_yamabe_PDI.pdf)なので
 これを見ておくと良いと思います。

 ## まとめ
 Houdiniの中でPythonを使える場所は多岐に渡りますね。
 驚く程多いです。
 ぶっちゃけ公式ドキュメントの[Pythonスクリプトの場所](https://www.sidefx.com/ja/docs/houdini/hom/locations.html)に全部まとまってると思うんですが
 こうやって俯瞰してみると改めて実感しました。
 HoudiniをやるならwrangleもそうなんですがPythonもしっかり扱えた方が良さそうですね。