On this page

概要

ここでは、HOMを使ってHoudiniで単純なタスクを実行するという簡単なサンプルを載せています。このサンプルの詳細が分からなくても心配しないでください。 このサンプルでは、Houdiniのスクリプトが一体どのようなものなのか感じてください。

Windows ▸ Python Shell を選択してPythonシェルウィンドウを開きます。

# シーン内のすべてのノードのツリーをプリントします:
>>> def print_tree(node, indent=0):
...     for child in node.children():
...         print (" " * indent + child.name())
...         print_tree(child, indent + 3)
... # Enterを押して、定義を終了します。

>>> print_tree(hou.node('/'))
obj
   cam1
      file1
      properties
         standard
out
part
ch
shop
img
   img1
vex

はじめよう

HoudiniのPythonシェルウィンドウを開くと、>>>プロンプトが表示されることに気づくでしょう。このプロンプトは、あなたからPythonエクスプレションまたはステートメントを入力することを待っています。長いPythonスクリプトを記述するつもりがなくても、Pythonシェルは手軽な計算機として重宝します:

>>> 2 + 2
4
>>> 0.03 * 25.1
0.753
>>> min(hou.frame(), 7) * 3
3.0

hou.frame()とは何か?これは、Hodiniのモジュールであるhouに実装されたPython APIです。osモジュールでいうos.getcwdのようなものです。hou.framehouモジュールの関数で、現在のフレーム番号を返します。import houを記述してhouモジュールを使用する必要がないことに注目してください。その理由は、Houdiniを起動した時に自動的にhouモジュールがインポートされているからです。

Ctrl+Dを押すとフローティングPythonシェルウィンドウが閉じます。メインメニューのショートカットを確認し、フローティングPythonシェルウィンドウを開いてみてください。

フローティングウィンドウを使いたくない場合は、Pythonシェルをメインウィンドウのペイン内で開くこともできます。

Pythonシェル内でHome/Ctrl+Aを押すとラインの先頭に移動し、End/Ctrl+Eはラインの後頭に、上/下矢印はヒストリーを行き来することができます。

Pythonシェルでは、Ctrl+Cを使ってコピーすることができません。その理由は、Ctrl+CはKeyboardInterruptの例外を送信するショートカットだからです。Pythonシェルからテキストをコピーするには、クリックでCopyを選択してください。

Hipファイルの読み込み

hou.hipFileサブモジュールを使えば、現在のセッションをhipファイルに保存したり、hipファイルから現在のセッションに読み込んだりすることができます。hou.hipFile.load()は、ファイルが正常に読み込まれても、警告があればhou.LoadWarningの例外を投げることに注目してください。以下のコードは警告を出力し、残りのスクリプトを続行します。

# 警告を出力しますが、読み込みが成功すれば続行します。
try:
    hou.hipFile.load("myfile.hip")
except hou.LoadWarning, e:
    print e

ノードへのアクセス

Houdiniはノード(例: SOP、DOP、Objectノードなど)指向で設計されているので、ほぼスクリプトでノードを操作することができます。ここでは、初めてノードを操作するための簡単な例を載せています。

hou.node関数は、ノードのパスを受け取り、hou.Nodeオブジェクトを返します。パスが無効であればNoneを返します。

# 現在のセッションをクリアします。
>>> hou.hipFile.clear()

>>> hou.node('/obj')
<hou.Node at /obj>
# hou.node関数が/objノードに該当するhou.Nodeを返しました。

>>> n = hou.node('/asdfasdf')
# パスが無効なので、nはNoneオブジェクトを返します。
>>> print n
None

>>> g = hou.node('/obj').createNode('geo')
>>> g
<hou.ObjNode of type geo at /obj/geo1>
# gは新しく作成した/obj/geo1ノードに該当するhou.Nodeオブジェクトです。
# gは実際にはhou.OjbNodeインスタンスであり、これはhou.Nodeのサブクラス
# であることに注目してください。

# hou.Nodeオブジェクトのparmメソッドはhou.Parmオブジェクト(パラメータ名が無効ならNone)
# を返します。
>>> tx = g.parm('tx')
>>> tx
<hou.Parm tx in /obj/geo1>

# パラメータを評価し、パラメータの値を変更します。
>>> tx.eval()
0.0
>>> tx.set(3.5)
>>> tx.eval()
3.5

>>> hou.node('/obj/geo1').parm('tx').eval()
3.5
# hou.parmはparmに直接アクセスするショートカットです。
>>> hou.parm('/obj/geo1/tx').eval()
3.5
# hou.evalParmはパラメータを評価するショートカットです。
>>> hou.evalParm('/obj/geo1/tx')
3.5
# hou.chはhou.evalParmと同じです。
>>> hou.ch('/obj/geo1/tx')
3.5

# hou.Parm.nameはパラメータの名前を返します。
# hou.Node.parmsはNodeのパラメータすべてをタプルで返します。
>>> [p.name() for p in g.parms()]
['stdswitcher1', 'stdswitcher2', 'stdswitcher3', 'stdswitcher4', 'keeppos',
'pre_xform', 'xOrd', 'rOrd', 'tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy',
'sz', 'px', 'py', 'pz', 'scale', 'lookatpath', 'lookup', 'pathobjpath',
'roll', 'pos', 'uparmtype', 'pathorient', 'upx', 'upy', 'upz', 'bank',
'shop_materialpath', 'shop_materialopts', 'tdisplay', 'display',
'use_dcolor', 'dcolorr', 'dcolorg', 'dcolorb', 'picking', 'pickscript',
'caching', 'vport_shadeopen', 'vport_displayassubdiv', 'vm_phantom',
'vm_renderable', 'folder01', 'folder02', 'folder03', 'folder04',
'categories', 'reflectmask', 'lightmask', 'geo_velocityblur',
'vm_shadingquality', 'vm_rayshadingquality', 'vm_rmbackface',
'shop_geometrypath', 'vm_rendersubd', 'vm_renderpoints', 'vm_metavolume',
'vm_coving', 'vm_computeN']

# hou.Parmのタプルはパラメータグループ(tx,ty,tzパラメータならtがそれです)に該当します:
>>> t = g.parmTuple('t')
>>> t
<hou.ParmTuple t in /obj/geo1>
>>> tuple(t)
(<hou.Parm tx in /obj/geo1>, <hou.Parm ty in /obj/geo1>, <hou.Parm tz in /obj/geo1>)
>>> t.eval()
(3.5, 0.0, 0.0)
>>> t.set((1.0, 2.0, 3.0))
>>> t.eval()
(1.0, 2.0, 3.0)

# 簡単なSOPネットワークを構築します。
>>> hou.hipFile.clear()
>>> geo = hou.node('/obj').createNode('geo')
>>> box = geo.createNode('box')
>>> subd = geo.createNode('subdivide')
>>> subd.parm('iterations').set(3)
>>> subd.setFirstInput(box)
>>> subd.moveToGoodPosition()
# ノードのタイルが重複しないように移動します。
>>> subd.setDisplayFlag(True)
>>> subd.setRenderFlag(True)
>>> subd.setCurrent(True, clear_all_selected=True)

アニメーションパラメータとキーフレームの取り扱い

“アニメーションパラメータ”という用語を聞くと、一般的にはキーフレームの値、Bezier曲線、アニメーショングラフエディタが頭に浮かぶと思います。初心に戻って考えてください。エクスプレッションを定義したパラメータもアニメーションパラメータです。すべてのアニメーションパラメータには、最低1つのキーフレームがあり、各キーフレームにはエクスプレッションを定義しています。エクスプレッションを定義した典型的なパラメータは、単に1つのキーフレームがあり、そのエクスプレッションはsin($F)cos(time())のようなものです。一方で、典型的なアニメーションカーブには複数のキーフレームがあり、それらのエクスプレッションはbezier()のようなものになっています。

そう考えると、bezier()のような関数は時間別に別の値をどうやって評価するのでしょうか?時間が経過する度に値を変化させるbezier()に渡すパラメータが明らかにありません。そして、bezier()に渡すキーフレームやスロープもありません。その答えは、キーフレームはエクスプレッションよりもたくさんの情報を記録しているからです。キーフレームは、それらの値、スロープ、加速度、Bezierのような特定の関数を記録し、現行のキーフレームと次のキーフレームの値にアクセスします。sin($F)のようなエクスプレッションを定義したキーフレームに関しては、それらのキーフレームの余分な値を設定せず、使用しません。

各キーフレームは、時間と関係付けられています。その時間と1秒あたりのフレーム数を使って、キーフレーム間のフレームを計算することができます。エクスプレッションは キーフレーム間 で有効であると考えることができます。Houdiniは、現行のキーフレームと 次の キーフレーム間のエクスプレッションを評価します。次のキーフレームがなければ、アニメーション関数(例:bezier,cubicなど)は単純に現行のキーフレームの値を評価します。最初のキーフレームの前の時間に関しては、パラメータは最初のキーフレームの時間で値を評価します。

hou.Parm.keyframes()がキーフレームの値、スロープ、加速度を制御します。

  • inの値を設定しoutの値を設定しなかった場合、同じ値が設定されます。inの値を設定すると、inとoutの値の間の繋がりを壊します。inまたはoutのどちらかの値を設定しなかった場合は、それらの値が繋がっているとみなされます。

  • 例えば、キーフレームを現行の値とスロープで設定するには、キーフレームにその値またはスロープを設定しないでください。

  • 自動的にスロープを決定するには、スロープを設定しないでキーフレームを設定してください。

  • 時間とエクスプレッション

  • inとoutの値

  • 繋がりの値(tie)

  • asCode()

  • HScriptエクスプレッションとPythonエクスプレッションは同じ構文です。

オブジェクトとトランスフォームの取り扱い

  • worldTransform(), setWorldTransform()

  • 行列、展開

  • トランスフォーム(p T1 T2)が縦ベクトルで、(T2 T1 p)が横ベクトルです。

  • object_xformクックブックのサンプルを参照してください。

エラーメッセージを調べる場所

  • シェルフ/タブメニュー: ポップアップウィンドウの詳細セクション内。

  • HDAコールバック: コンソール内。

  • 123.py/456.py: コンソール内。

  • パラメータ: ノード上を

  • Pythonベースのノード: ノード上を

Pythonエラーメッセージの分析

Pythonコードは、ハンドルされていない例外を生成した時、その例外が発生した箇所でのコールスタックのトレースバックを表示します。 そのトレースバックの最後のエントリーを見ることで、その例外が発生したコードの行を調べることができます。

例えば、fixFilePrefixesの実装にスペルのエラーがあると、以下のトレースバックが表示されます。

>>> hou.session.fixFilePrefix(hou.node('/'), '/home/luke/project', '$HIP')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "hou.session", line 12, in fixFilePrefix
NameError: global name 'to_prefix' is not defined

このトレースバックの最後の行に、その例外の文字列表現が表示されます。 houモジュールでよく発生する例外は、hou.OperationFailed, hou.PermissionError, hou.LoadWarning, hou.ObjectWasDeletedです。 houモジュールに定義されているすべての例外タイプのリストは、リファレンスドキュメントで調べることができます。

Tip

Pythonソースエディタ、複数行のエクスプレッションエディタ、シェルフスクリプトエディタ、HDAスクリプトエディタは、右下コーナーに行番号を表示するので、例外が発生した行を特定することができます。

役立つ情報

  • ネットワークエディタからノードをPythonシェルにドラッグすると、hou.nodeエクスプレッションがペーストされます。Pythonシェルがペイン内にあれば、このことに早く気づいていたかもしれません。

  • 何度もhou.nodeやhou.parmをコールするのが面倒なら、変数を使ってhou.Node、hou.Parm、hou.ParmTupleのオブジェクトを格納してください。

  • hou.Node.asCode()からの出力を使えば、ノードの作成とパラメータとキーフレームの設定をするHOM APIを学習するのに役に立ちます。

Pythonスクリプト

はじめよう

次のステップ

リファレンス

  • hou

    Houdiniにアクセスできるサブモジュール、クラス、ファンクションを含んだモジュール。

導師レベル

Python Viewerステート

Pythonビューアハンドル

プラグインタイプ