ViewerStateで動的なハンドルを使う

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

タイトルとURLをコピーしました