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 = "ViewerStateMoveHandle::1.0"
state_cat = hou.sopNodeTypeCategory()
template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
template.bindFactory(State) # ステートインターフェースの実装を返す
template.bindIcon(kwargs["type"].icon()) # Viewerステートのアイコン画像を設定します
return template
ハンドルクラスのリストを管理するのではなく、kwargs(キーワードアーグス)から返るハンドルの名前”handle”とパラメータ”parms”で変更処理を行っている。ちょっとスッキリしないが他の方法はわからない。
bindViewerHandle()
AキーでbindViewerHandle()でハンドルを追加した瞬間にonStateToHandle()が呼び出されるので、そこでparms[]にmultiparm listの値がバインドされている。
下のようにhandle_parmsを記述することで必要な要素だけ表示させることができる。
self.scene_viewer.bindViewerHandle("translate", f"handle_{i}", handle_parms=["ty"])
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
ハンドルの種類
https://www.sidefx.com/ja/docs/houdini/hom/state_handles.html#handle-types