Houdini Engine API Python をさくっと書いてみる

# Houdini Engine API Python をさくっと書いてみる
この記事は[Houdini Apprenticeアドベントカレンダー2020](https://qiita.com/advent-calendar/2020/happrentice) 13日目の記事です。
普段はHoudiniを使った仕事をしていないので初心者です。今回、18.5の新機能の中で私の琴線に触れた
Houdini Engine Python APIについての資料をまとめてみました。

先に伝えておくと、out-of-processでのHoudini Engineを外のPythonインタープリタを使ってhapiパッケージとして扱うことはできなかったですので、

この文言だけで分かるHoudiniチョットデキル方はページバックで大丈夫です。何をいってるかわからん、と言う方はそのまま御読みください。(Indieの制限があるかもしれないので、Engineライセンスをお持ちの方で検証してもらえると記事を作成した甲斐があります。)

記事を書いてる途中でまさに公式ドキュメントが追記されていってますので、記事の内容も若干ニュアンスが変わると思います。そのあたりご了承ください。

```python:確認環境  
>>> hou.applicationPlatformInfo()
'macosx10.14-x86_64-clang10.0-targetosx10.12'
>>> hou.applicationVersion()
(18, 5, 351)
>>> hou.licenseCategory()
licenseCategoryType.Indie
```

## Houdini Engine とは
@jyouryuusuiさんの [記事](https://qiita.com/jyouryuusui/items/3d86ab8e69652d5f951c) に技術的な検証を含めとてもよく纏まっているのでそちらを参照いただきつつ、
現在の Houdini Engine で出来ることををおさらいすると以下の２点です。

1. ホストアプリケーションで動作するEngineプラグイン
2. hbatch/hython を使ったバッチ処理

エンドユーザー観点だとUnrealやUnity,Maya&3dsMaxに加えてCinema4Dアプリケーション内にHDAを読み込んできて使う、というのが一般的かと思います。

(余談ですが、リンク先の動画は国内で数少ないHoudini Engineを内製エンジンに組み込んだ例なので、まだご覧になっていない方は是非。実際に会場で聞いて興奮したのを覚えています。
https://www.youtube.com/watch?v=ABpjD-9FRe0&vl=ja)

今回のhapiはEngineライセンス内に後者のワークフローやパイプライン制作に役立つ機能がひとつ増えた、と要約できるかと思います。
                                            
プロダクション環境ではCoreとFXのライセンスの調整は日常的に行われていると思いますので、アセット開発やパイプライン開発をそれらのライセンスで行った後に

Engineライセンスに移譲したPythonを使って(追加費用なしに)機能拡張できるのは純粋なメリットです。

18.5リリース当初は公開されていなかったドキュメントですが、https://www.sidefx.com/docs/houdini/hapi/ で見ることができます。

Engine C API の忠実なバインドになっていて、prefixが整理されたAPIがそのまま動きます。例えば関数の戻り値はオブジェクトではなく、intとかboolです。

これは逆説的にいうと困ったときにはC APIドキュメントを漁って課題解決する必要があると言う意味です。この記事もC APIドキュメントを読みながら作っています。


### Python API をつかってみる
C APIの導入と同じようにEngineをinitializeするまでの流れです。C API導入より簡単に使い始めることができます。

```python
import hapi
session = hapi.createInProcessSession()
options = hapi.CookOptions()
try:
    hapi.initialize(session, options)
except hapi.AlreadyInitializedError:
    pass
``` 

GlobalNode が出来ました。
![スクリーンショット 0002-11-28 11.05.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48607/40095983-4d52-f03f-1324-49df853593a0.png)


ちなみにサンプルにあるソケットサーバとの通信でout-of-processセッションを作るスクリプトは、前準備なしにそのまま動きません。

```python
session = hapi.createThriftSocketSession('localhost', 9090)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
FailureError
```  

これはリンク先にある[Houdini Engine Remote Server(HARS)](https://www.sidefx.com/docs/hengine/_h_a_p_i__sessions.html#HAPI_Sessions_Thrift_Server)
を先に起動しておくことで、問題なく動くようになります。
 
`HARS -a -s 9090`

同時接続はできない仕組みになっているので、作業が終わればsessionを閉じておきます。

```python
hapi.closeSession(session)
```                       

ノードを作ってcookしてみます。

```python
node_id = hapi.createNode(session, -1, 'Sop/')
hapi.cookNode(session, node_id)
```

あとはアセットの読み書きやhipへの保存ですね。

```python:サンプルですみません
hda_bytes = open('/tmp/myhda.hda', 'rb').read()
lib_id = hapi.loadAssetLibraryFromMemory(session, hda_bytes,
    len(hda_bytes), True)

# Get a [Py:hapi.NodeInfo] for the new node
node_info = hapi.getNodeInfo(session, node_id)

# Getting a string is a two step process
str_len = hapi.getStringBufLength(session, node_info.nameSH)
node_name = hapi.getString(session, node_info.nameSH, str_len)

print('Created {}'.format(node_name))

hapi.saveHIPFile(session, '/tmp/debug.hip')
hapi.saveGeoToFile(session, node_id, '/tmp/debugGeo.bgeo.sc')
```      

一通り試したところで検証しておきたかったもう一つの項目、ホストアプリケーションからhapiにアクセスして外部からHoudini Engineに接続する、

ですが、ライブラリへのパスなどの参照を待たせても手元でやった限りうまくいきませんでした。C API ドキュメントを見ると、HAPIをinitializeする前に

`libHAPIL`を先に名前解決しろとあるのでこれを試してみたものの課題解決に至らず。環境変数を通さないで使うhouも同じメッセージを出すのでその辺りの挙動は同じのようです。

```python
import sys
sys.path.append("""hapiのパス""")
import hapi

# エラー: ImportError: file /Applications/Houdini/Houdini18.5.351/Frameworks/Houdini.framework/Versions/18.5/Resources/houdini/python2.7libs/hou.py line 32: dlopen(/Applications/Houdini/Houdini18.5.351/Frameworks/Houdini.framework/Versions/18.5/Resources/houdini/python2.7libs/_hou.so, 2): Library not loaded: @rpath/libOpenImageIO_Util_sidefx.2.0.10.dylib
  Referenced from: /Applications/Houdini/Houdini18.5.351/Frameworks/Houdini.framework/Versions/18.5/Resources/houdini/python2.7libs/_hou.so
  Reason: image not found # 
```
hython起動環境下だと滞りなくパスが通るので、そこからMaya等のホストアプリケーションを起動すれば、hapiを使ったEngine Sessionのコントロールを

スクリプトで実行することはできます。が、エンドユーザー的に使いやすいとは言えないです。

公式で想定している使い方としては、In-ProcessでのEngineに用途を絞って提供していて、オブジェクトモデルに基づいた操作は hou と言う住み分けでしょうか？

あるいは学習用途としてPython APIで実装しておいて、C APIに移植するというのもありそうですね。


### hou との関連
先に検証したように現時点ではhouを置き換えるようなパッケージではない、と言う感想です。

SessionSync周りが`hou.startHoudiniEngineDebugger(9090)`から、hapiで作る流れに変わったぐらい。

HOMが便利すぎると言う感想でした。


#### まとめ
Engine APIも定期的に更新が続けられているため、将来的にエコシステムにも広がりがあると嬉しいですね。

今後に期待という締めで終わりたいと思います。