ViewerStateでハンドルを利用する時のサンプルコードを書いてみた。ハンドルには最初から個数を決めておく静的なハンドルと、個数が可変の動的なハンドルがあり、今回は動的なハンドルを使う。
構成
HDAを作成し、UIにはMultiparm Block(list)を「points」という名前で追加し、ここにFloat Vector3を「pt_#」という名前で配置した。
この座標の個数だけハンドルをバインドする関係になっている。
コード
デバッグ目的で、Aキーを押すとポイントを追加して新規ハンドルをバインドし、Zキーを押すとポイントを削除してハンドルをアンバインドするようにしている。
import hou
import viewerstate.utils as su
class State(object):
def __init__(self, state_name, scene_viewer):
self.state_name = state_name
self.scene_viewer = scene_viewer
def onEnter(self, kwargs):
self.node = kwargs["node"]
# 各エントリにハンドルを作成
multiparm = self.node.parm("points")
for i in range(multiparm.evalAsInt()):
self.scene_viewer.bindViewerHandle("translate", f"handle_{i}", handle_parms=["tx", "ty", "tz"])
# ハンドルの初期位置をリセットするために何かしらを更新する(修正したい)
parm_pos = self.node.parmTuple("pt_%d" % 0).eval()
self.node.parmTuple("pt_%d" % 0).set(parm_pos)
# ユーザがハンドルを操作した時にコールされるメソッド
def onHandleToState(self, kwargs):
# ハンドルのパラメータをMultiparmに反映
handle = kwargs["handle"]
parms = kwargs["parms"]
mod_parms = kwargs["mod_parms"]
prev_parms = kwargs["prev_parms"]
ui_event = kwargs["ui_event"]
# ハンドル名からインデックスを取得
handle_name = kwargs["handle"]
index = int(handle_name.split("_")[1])
# Houdiniノードのパラメータを更新
self.node.parmTuple("pt_%d" % index).set([parms["tx"], parms["ty"], parms["tz"]]) # 座標をセット
# ユーザがパラメータを変更した時にコールされるメソッド
def onStateToHandle(self, kwargs):
# Multiparmの状態をハンドルに反映
handle_name = kwargs["handle"]
index = int(handle_name.split("_")[1])
multiparm = self.node.parm("points")
if index < multiparm.evalAsInt():
parms = kwargs["parms"]
parm_pos = self.node.parmTuple("pt_%d" % index).eval()
parms["tx"] = parm_pos[0]
parms["ty"] = parm_pos[1]
parms["tz"] = parm_pos[2]
# キーボード入力
def onKeyEvent(self,kwargs):
ui_event = kwargs["ui_event"]
self.key_pressed = ui_event.device().keyString()
# ポイントの追加処理
if self.key_pressed in ("a"):
multiparm = self.node.parm("points")
index = multiparm.evalAsInt()
multiparm.insertMultiParmInstance(index)
self.scene_viewer.bindViewerHandle("translate", f"handle_{index}", handle_parms=["tx", "ty", "tz"])
return True
# ポイントの削除処理
if self.key_pressed in ("z"):
multiparm = self.node.parm("points")
index = 0 # 消去するインデックス
# 一番後ろのインデックスから消去していく
self.scene_viewer.unbindViewerHandle(f"handle_{multiparm.evalAsInt()-1}")
# アンバインドしてからポイントを削除する
multiparm.removeMultiParmInstance(index)
return True
self.key_pressed = None
return False
def createViewerStateTemplate():
state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
state_label = "ViewerStateBindViewerHandle::1.0"
state_cat = hou.sopNodeTypeCategory()
template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
template.bindFactory(State)
template.bindIcon(kwargs["type"].icon())
return template
ハンドルのバインドはhou.SceneViewerクラスで行う。
座標の変更処理は、onHandleToState()とonStateToHandle()内でkwargs(キーワードアーグス)から返るハンドルの名前”handle”とパラメータ”parms”を使う。
bindViewerHandle()
AキーでbindViewerHandle()でハンドルを追加した瞬間にonStateToHandle()が呼び出されるので、そこでparms[]にmultiparm listの値がバインドされている。
下のようにhandle_parmsを記述することで必要な要素だけ表示させることができる。
self.scene_viewer.bindViewerHandle("translate", f"handle_{i}", handle_parms=["ty"])
hou.Handleクラス
handleクラスからハンドルへアクセスして情報を取得したい場合。
handle = hou.Handle(self.scene_viewer, "handle_1")
HDA内のコード
HDAのアウトプットにつなげているWrangleコード。Multiparm Blockの座標をポイントに変換して出力している。
//
// Multiparm Blockの座標をポイントに変換する
// Run Over: Detail
//
int numPoints = `chs("../points")`;
for(int i = 0; i < numPoints; i++)
{
vector p = chv(sprintf("../pt_%g", i));
int pt = addpoint(0, p);
}
参考
hou.SceneViewerクラス
bindViewerHandle()で動的なハンドルをバインドをする。unbindViewerHandle()でハンドルの除去。
https://www.sidefx.com/ja/docs/houdini/hom/hou/SceneViewer.html
hou.Handleクラス
https://www.sidefx.com/ja/docs/houdini/hom/hou/Handle.html
ハンドルの種類
https://www.sidefx.com/ja/docs/houdini/hom/state_handles.html#handle-types