kick the base

Houdiniと、CG技術と、日々のこと。

Houdini: Pythonでノード操作してみる。

今回は簡単なPythonコードを用いて、Houdiniでよくやる作業を自動化する方法についての記事です。

要件としては下記のとおりです。

  1. シーンにGeometryノードを作成し、名前をWORKにする
  2. 内部のFileSOPを削除する
  3. シーンにすでにWORKがある場合は処理を行わず抜ける
  4. シェルフを作成し、ツールを作成・スクリプトを登録、クリック一発で実行できるようにする

本記事を作成するにあたり、Houdini実践ハンドブックWrangle×Pythonに大きなインスパイアをいただきました。

こちらの書籍については後日レビューを書く予定です。

Pythonを実行する下準備

Pythonの実行はPythonScriptノードやPythonSOPにPythonコードを書くことで実行できますが、今回は最終的にシェルフにツールを登録するので先にシェルフの用意をしておきましょう。

シェルフからツールを実行する際はアイコンをクリックするだけなので、わかりやすく試行錯誤にも向いているかと思います。

シェルフの新規作成

シェルフの+ボタンを押して「New Shelf…」を実行します。

f:id:kickbase:20170526131720j:plain

シェルフの設定

Name、Labelをお好みで設定します。ここでは下記のとおりとしました。

  • Name: kickbase
  • Label: Kickbase

f:id:kickbase:20170526130744j:plain

ツールの作成

作成したシェルフ上で右クリックメニューを表示、「New Tool…」を実行します。

f:id:kickbase:20170526114402j:plain

ツールの設定

Name、Labelをお好みで設定します。ここでは下記のとおりとしました。

  • Name: work
  • Label: Work

f:id:kickbase:20170526114401j:plain

スクリプトの記述

Scriptタブに移動し、任意のスクリプトを記述していきます。

f:id:kickbase:20170526114400j:plain

スクリプトの実行

スクリプトを実行するにはアイコンをクリックするだけです。

f:id:kickbase:20170526130742j:plain

コード解説

最終的なコード

たったの6行!普段コードを書かない方でも十分追える分量かと思います。

目標としては、できるだけシンプルで汎用性が高いツールを目指します。今回のツールは新しいシーンを作るとき必ず最初にやる作業なので、最初に作るツールとしてはお手頃で良いんじゃないでしょうか。

ちなみにぼくは素材は主にWORKノード内ですべて作成し、個別に見栄えを調整したいオブジェクトはObjectMergeSOPでシーンに出すというスタイルで作成しています。

以下に実際に書いた順に見ていきましょう。

Geometryノードの作成

geo = hou.node('/obj/').createNode('geo', 'WORK')

最終コードでは2行目に当たりますが、この行から書き始めました。じっくり見ていきます。

まず、Geometryノードをつくるだけならhou.node('/obj/').createNode('geo')これだけでOKです。これはobjレベルにGeometryノードを作成します。名前はgeo1となりました。

ノード生成時に名前をつけたい場合はcreateNodeメソッドの第二引数に名前を渡します。コードは下記のようになります。

hou.node('/obj/').createNode('geo', 'WORK')

生成したGeometryノードにあとでアクセスするため、戻り値を変数geoに保持しておきます。これでgeo = hou.node('/obj/').createNode('geo', 'WORK')となりました。

生成したノードの配置位置を自動調整

geo = hou.node('/obj/').createNode('geo', 'WORK')
geo.moveToGoodPosition()

追加したgeo.moveToGoodPosition()は非常にシンプルです。メソッド名の通り、ネットワークビューでノードをいい感じに自動配置してくれます。

(注)このメソッドを実行しないと遠いところにノードが生成されてしまったり、すでにあるノードに重なってしまったりということが起こります

Geometryノード内のFileSOPを削除

geo = hou.node('/obj/').createNode('geo', 'WORK')
geo.moveToGoodPosition()

for n in geo.children():
    n.destroy()

追加したのは下記部分です。

for n in geo.children():
    n.destroy()

これも特に難しくはありません。geoは生成したGeometryノードなので、geo.children()で取得できるのはGeometryノード内にあるすべてのノードとなります。これをforループで処理し、個々のノードをdestroyメソッドで削除していきます。

Geometryノードが生成時に持っているのはFileSOPのみですが、子要素をすべて削除することを明示するためにforループを用いています。

複数回実行を防ぐ

運用方法としてWORKノードはひとつだけにしたいですが、このままではクリックするたびに実行され、Geometryノードがつど生成されてしまいます。

f:id:kickbase:20170526114358j:plain

それを防ぐため「すでにWORKという名前のノードがあったら処理を実行しない」というコードを追加しましょう。*1

if not hou.node('/obj/WORK'):
    geo = hou.node('/obj/').createNode('geo', 'WORK')
    geo.moveToGoodPosition()

    for n in geo.children():
        n.destroy()

一行目にif文を追加しました。元々書いていたコードのインデントをひとつ下げるのをお忘れなく。

これで複数回クリックしても一度だけ実行されるようになりました。

まとめ

今回紹介したコードは6行と短く、機能も非常にシンプルです。しかし、ツールは複雑なコードを書けばいいというものではなく、よく行う作業を効率化できればそれでOKだと思います。

追加したい機能

実は実装できなかった機能として「WORKノードを作成したあと、ネットワークビューでその中に入った状態にする」というのがあります。(つまり/obj/WORK/にいる状態にしたい)

この方法がわからなかったので、どなたかご存じの方はご教授いただけると幸いです。

*1:コードとしては「WORKというノードがないときのみ実行する」という書き方になります