はじめに
概要
この記事はHoudini Apprenticeアドベントカレンダー2024 8日目の記事です。
みなさんはShelfツール使ってますか?
なんかスクリプト使う場所だったり、エフェクト作るときのプリセットだなぁというイメージかと思います。
ではこれらを自前でカスタマイズしてHoudiniライフをもっとよくしてみませんか?
環境
Houdini20.5.410 py3.11
※こちらを使用していますが、もちろん別のバージョンでもある程度汎用的に使用できるTipsだと思います。
関連記事
今回Shelf Editの基本的な部分は省略するので、上記のshelfツール セットアップの項目の部分の説明を参考にしてみてください。
まずは意識を低く使ってみよう
今回はこのようなノード群を作ってみましょう。
Attribtue DeleteとGroup Deleteでメッシュ以外のアトリビュートを削除して、Colorで赤色にしましょう。
作成したら、下の3つをShelfにD&Dします。設定はこんな感じです。
これで完成です。
クリックすると3つのノードが新しく追加されたのが分かります。
これが簡単なノードプリセットの完成です。
ここから少しアレンジを加えていきます。
Shelfを右クリックして、Edit Tool
で改良しましょう。
まずはIcomをBUTTONS_color
にします。
そしてContextタブからNetwork PaneでSOPにチェックをいれてましょう。
TAB Submenu Path
は任意のカテゴリを追加します。ここが空白だとMoreというカテゴリになります。/
で区切ることでサブカテゴリとして追加することも可能です。
ここまで設定したらAcceptで確定させます。
これによってShelfのツールをNetwork Viewから呼び出せるようになりました。
わざわざHDAを作らなくてもカスタムプリセットとして呼び出せるようになりました!便利!
ちょっと余談
Mountain
を呼び出したときにAttributeNoise
が呼び出されるのって何でだろう?とかVellum系のノードを呼び出すと複数出てくるものとか、Tabから呼び出すCube
とBox
の違いって何?など考えたことがあると思いますが、これらは全てShelfで設定されているノード群です。
SOPをD&DをしたScriptはかなり長いコマンドになっているのですが、HDAにするまでもないPresetや一時的に作成した便利なWrangleなどサクッと使用するには便利なので使いどころがあるのではないのなかぁと思ってます。
ただしSolverの中身のコネクションやExpressionのリレーションなど維持できないものも多いので、使いどころは難しいですが気軽に使えるものだと思ってます。
別PCからノードを急ぎ持っていきたいときにShelfにしてしまってそのスクリプトをコピペして持っていくなどにも使用できます。
さてここからさらに発展させましょう。
HoudiniでPythonを呼び出すときに、諸々の情報を含んだkwargs
を活用することで発展した機能を追加することが可能です。
これを利用してさっきのShelfツールを改良しましょう。
①Edit Tool
からScriptの5行目あたりに次の変数を追加します。
if kwargs["ctrlclick"]:
custom_color = "( 1 1 0 )"
else:
custom_color = "( 1 0 0 )"
②91行目当たりのh_cmd = r'''
をh_cmd=fr'''
に変更します。
③138行目あたりのopparm $_obj_geo1_color1 color ( 1 0 0 ) ramp2pos ( 1 ) ramp2c ( 1 1 1 )
を
opparm $_obj_geo1_color1 color {custom_color} ramp2pos ( 1 ) ramp2c ( 1 1 1 )
に変更します。
これをAcceptで確定させます。
一応全文
import sys
import toolutils
if kwargs["ctrlclick"]:
custom_color = "( 1 1 0 )"
else:
custom_color = "( 1 0 0 )"
outputitem = None
inputindex = -1
inputitem = None
outputindex = -1
num_args = 1
h_extra_args = ''
pane = toolutils.activePane(kwargs)
if not isinstance(pane, hou.NetworkEditor):
pane = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)
if pane is None:
hou.ui.displayMessage(
'Cannot create node: cannot find any network pane')
sys.exit(0)
else: # We're creating this tool from the TAB menu inside a network editor
pane_node = pane.pwd()
if "outputnodename" in kwargs and "inputindex" in kwargs:
outputitem = pane_node.item(kwargs["outputnodename"])
inputindex = kwargs["inputindex"]
h_extra_args += 'set arg4 = "' + kwargs["outputnodename"] + '"\n'
h_extra_args += 'set arg5 = "' + str(inputindex) + '"\n'
num_args = 6
if "inputnodename" in kwargs and "outputindex" in kwargs:
inputitem = pane_node.item(kwargs["inputnodename"])
outputindex = kwargs["outputindex"]
h_extra_args += 'set arg6 = "' + kwargs["inputnodename"] + '"\n'
h_extra_args += 'set arg9 = "' + str(outputindex) + '"\n'
num_args = 9
if "autoplace" in kwargs:
autoplace = kwargs["autoplace"]
else:
autoplace = False
# If shift-clicked we want to auto append to the current
# node
if "shiftclick" in kwargs and kwargs["shiftclick"]:
if inputitem is None:
inputitem = pane.currentNode()
outputindex = 0
if "nodepositionx" in kwargs and "nodepositiony" in kwargs:
try:
pos = [ float( kwargs["nodepositionx"] ),
float( kwargs["nodepositiony"] )]
except:
pos = None
else:
pos = None
if not autoplace and not pane.listMode():
if pos is not None:
pass
elif outputitem is None:
pos = pane.selectPosition(inputitem, outputindex, None, -1)
else:
pos = pane.selectPosition(inputitem, outputindex,
outputitem, inputindex)
if pos is not None:
if "node_bbox" in kwargs:
size = kwargs["node_bbox"]
pos[0] -= size[0] / 2
pos[1] -= size[1] / 2
else:
pos[0] -= 0.573625
pos[1] -= 0.220625
h_extra_args += 'set arg2 = "' + str(pos[0]) + '"\n'
h_extra_args += 'set arg3 = "' + str(pos[1]) + '"\n'
h_extra_args += 'set argc = "' + str(num_args) + '"\n'
pane_node = pane.pwd()
child_type = pane_node.childTypeCategory().nodeTypes()
if 'color' not in child_type:
hou.ui.displayMessage(
'Cannot create node: incompatible pane network type')
sys.exit(0)
# First clear the node selection
pane_node.setSelected(False, True)
h_path = pane_node.path()
h_preamble = 'set arg1 = "' + h_path + '"\n'
h_cmd = fr'''
if ($argc < 2 || "$arg2" == "") then
set arg2 = 0
endif
if ($argc < 3 || "$arg3" == "") then
set arg3 = 0
endif
# Automatically generated script
# $arg1 - the path to add this node
# $arg2 - x position of the tile
# $arg3 - y position of the tile
# $arg4 - input node to wire to
# $arg5 - which input to wire to
# $arg6 - output node to wire to
# $arg7 - the type of this node
# $arg8 - the node is an indirect input
# $arg9 - index of output from $arg6
\set noalias = 1
set saved_path = `execute("oppwf")`
opcf $arg1
# Node $_obj_geo1_attribdelete1 (Sop/attribdelete)
set _obj_geo1_attribdelete1 = `run("opadd -e -n -v attribdelete attribdelete1")`
oplocate -x `$arg2 + 0` -y `$arg3 + 0` $_obj_geo1_attribdelete1
opparm $_obj_geo1_attribdelete1 ptdel ( * ) vtxdel ( * ) primdel ( * ) dtldel ( * )
opset -d off -r off -h off -f off -y off -t off -l off -s off -u off -F on -c on -e on -b off $_obj_geo1_attribdelete1
opexprlanguage -s hscript $_obj_geo1_attribdelete1
opuserdata -n '___Version___' -v '' $_obj_geo1_attribdelete1
opset -p on $_obj_geo1_attribdelete1
opcf $arg1
# Node $_obj_geo1_groupdelete1 (Sop/groupdelete)
set _obj_geo1_groupdelete1 = `run("opadd -e -n -v groupdelete groupdelete1")`
oplocate -x `$arg2 + 0.0034499950706958771` -y `$arg3 + -1.0000000119209287` $_obj_geo1_groupdelete1
opparm $_obj_geo1_groupdelete1 deletions ( 1 )
opparm -V 20.5.410 $_obj_geo1_groupdelete1 group1 ( * )
opset -d off -r off -h on -f off -y off -t off -l off -s off -u off -F on -c on -e on -b off $_obj_geo1_groupdelete1
opexprlanguage -s hscript $_obj_geo1_groupdelete1
opuserdata -n '___Version___' -v '20.5.410' $_obj_geo1_groupdelete1
opset -p on $_obj_geo1_groupdelete1
opcf $arg1
# Node $_obj_geo1_color1 (Sop/color)
set _obj_geo1_color1 = `run("opadd -e -n -v color color1")`
oplocate -x `$arg2 + 0.0034499950706958771` -y `$arg3 + -2.0000000238418574` $_obj_geo1_color1
opparm $_obj_geo1_color1 ramp ( 2 )
opparm $_obj_geo1_color1 color {custom_color} ramp2pos ( 1 ) ramp2c ( 1 1 1 )
opset -d on -r on -h off -f off -y off -t off -l off -s off -u off -F on -c on -e on -b off $_obj_geo1_color1
opexprlanguage -s hscript $_obj_geo1_color1
opuserdata -n '___Version___' -v '' $_obj_geo1_color1
opuserdata -n '___toolcount___' -v '2' $_obj_geo1_color1
opuserdata -n '___toolid___' -v 'sop_color' $_obj_geo1_color1
opset -p on $_obj_geo1_color1
opcf $arg1
opwire -n $_obj_geo1_testgeometry_pighead1 -0 $_obj_geo1_attribdelete1
opcf $arg1
opwire -n $_obj_geo1_attribdelete1 -0 $_obj_geo1_groupdelete1
opcf $arg1
opwire -n $_obj_geo1_groupdelete1 -0 $_obj_geo1_color1
set oidx = 0
if ($argc >= 9 && "$arg9" != "") then
set oidx = $arg9
endif
if ($argc >= 5 && "$arg4" != "") then
set output = $_obj_geo1_color1
opwire -n $output -$arg5 $arg4
endif
if ($argc >= 6 && "$arg6" != "") then
set input = $_obj_geo1_attribdelete1
if ($arg8) then
opwire -n -i $arg6 -0 $input
else
opwire -n -o $oidx $arg6 -0 $input
endif
endif
opcf $saved_path
'''
hou.hscript(h_preamble + h_extra_args + h_cmd)
さてこれで実行すると赤色になっていますが、実行時にCtrlを押しながら呼び出すと黄色になります。
このような感じでCtrl/Cmd/Alt/Shiftなどのキー入力や、Shelfの実行がVeiw PortなのかNetwork Viewなのか、ネットワークのワイヤーに繋がっているノードなど様々な情報を取得できるので、ちょっと複雑な条件設定を組む時に活用すると便利です。
kwargsについてもっと調べたい人向け
Shelfにこれを登録して、色々なところで実行することで返り値を調べることが出来る。
print(kwargs)
出力地の確認としてデフォルトだとHoudini Consoleに表示されるが、Python Shell
ウィンドウを開いておくとそっちに出力されるようになるので、好きな方で確認すると良い。
実際に使っているShelfのおまけ
Old Mountian
import soptoolutils
import toolutils
if kwargs['ctrlclick']:
soptoolutils.genericTool(kwargs, 'mountain::2.0')
else :
kwargs['parms'] = {
'displace': 1,
'attribs': 'P',
'noiserange': 1,
'amplitude': 0.25,
'fractal': 3,
'oct': 8,
'rough': 0.4 }
soptoolutils.genericTool(kwargs, 'attribnoise::2.0', force_filter=True, nodename = 'mountain1')
Deform ShelfのMountainツールの改良です。
Ctrlを押すと古いMountainSOP
を実行できます。古い方に慣れていると組んでおくと便利。
Reset Viewport
from labsopui import reset_viewport
reset_viewport.resetSceneViewers()
LabsToolを入れている場合、上のメニューバーからLabs/Reset Viweportで呼び出せる機能をTabから呼び出せるように設定しているものです。
HoudiniはViewがエラー起きがちなので、Tabから呼び出せるようにした方が便利です。
参考ページ
Icon一覧確認用ツールめっちゃ使います。
HoudiniのPython Wiki
おわりに
難しく考えなくても使える、そんなところからでもShelfを活用してみてはいかがでしょうか?
それでは、しゃーした~。