On this page |
概要 ¶
SOPフィルターノードのワークフローでは、そのノードを動作させるコンポーネント(例えば、ポリゴンやポイント)をユーザに選択させることが多いです。 そのようなツールでは複数選択を許容/必須にする場合があり、例えば、“Copy to Points”ツールだと、コピーするジオメトリとコピー先のポイントを受け取ります。 選択したコンポーネントは、新しく作成されたノードの“Group”パラメータに入力します。
Pythonステートを使った独自ツールで選択に対応させる方法がいくつかあります:
-
選択の度にユーザにお願いをする手動型のスクリプトによる選択。これは、既存のシェルフツールで行なわれている従来の方法です。私達は、このメソッドを使用することを強く推奨しています。
-
持続ジオメトリセレクターをバインドする方法。これは、アクティブなPythonステートに選択をお願いします。ユーザが選択を完了した時に、
onSelection()
コールバックがTrue
を返すことで、その選択を受け取ることができます。1個だけでなくたくさんのジオメトリセレクターを追加することができます。 -
持続オブジェクトセレクターをバインドする方法。ジオメトリセレクターと同様ですが、代わりにオブジェクトを選択します。1個だけでなくたくさんのオブジェクトセレクターを追加することができます。
-
Drawableセレクターをバインドする方法。Drawableに設定されたジオメトリを選択するPythonステートを有効にします。
-
1つ以上のエントリーセレクターをバインドする方法。これは、手動型のスクリプトの選択に似ています。現在のところ、このメソッドは推奨していません。
Pythonステート用の高度な選択機能:
-
Volatile選択をハンドルする方法。Pythonステートがアクティブになった時にVolatile選択にアクセスすることができます。
-
既存選択をハンドルする方法。Pythonステートがアクティブになった時に既存選択にアクセスすることができます。
-
Secure選択サポート。セレクターをビューアのSecure選択に準拠させるかどうか指定することができます。
手動型のスクリプトによる選択 ¶
これは、HOMスクリプトを使ってシェルフツールスクリプト(通常ではアセットに埋め込みます)でユーザに選択をお願いして、ユーティリティ関数を使ってノードを作成してから、そのノードのステートにする従来のHoudiniのワークフローです。
ツールスクリプトの書き方に関する詳細は、ツールスクリプトを参照してください。 アセットタイプのカスタムPythonステートを記述する方法の詳細は、Pythonのステートとノードを参照してください。
import stateutils import soptoolutils pane = stateutils.activePane() # (ネットワークエディタ内でノードを配置するのではなく)ビューア内にいる場合は、セレクタースクリプトのみを実行します。 if isinstance(pane, hou.SceneViewer): # まず最初に、コピーするプリミティブ(s)を尋ねます。 source = stateutils.Selector( name="select_polys", geometry_types=[hou.geometryType.Primitives], prompt="Select primitive(s) to copy, then press Enter", primitive_types=[hou.primType.Polygon], # プリミティブ番号を入力するパラメータ group_parm_name="sourcegroup", # この選択の接続先となる新しいノードの入力 input_index=0, input_required=True, ) # 次に、コピー先のポイントを尋ねます。 target = stateutils.Selector( name="select_points", geometry_types=[hou.geometryType.Points], prompt="Select points to copy onto, then press Enter", group_parm_name="targetgroup", # 各選択を正しい入力に接続することを忘れないでください。 :) input_index=1, input_required=True, ) # この関数は、Selectorオブジェクトのリストを受け取り、それぞれの選択をユーザに促します。 container, selections = stateutils.runSelectors( pane, [source, target], allow_obj_selection=True ) # この関数は、runSelectors()からのコンテナと選択を受け取り、 # マージと作成先のコンテキストを考慮して新しいノードを作成します。 newnode = stateutils.createFilterSop( kwargs, "$HDA_NAME", container, selections ) # 最後に、このノードのステートにします。 pane.enterCurrentNodeState() else: # ビューア以外での操作に関しては、ローレベルの関数で代用します。 soptoolutils.genericTool(kwargs, "$HDA_NAME")
ジオメトリセレクターのバインド ¶
hou.ViewerStateTemplate.bindGeometrySelectorを使用することで、ビューアがカスタムSOPステートになった時に実行するSOPレベルの単一セレクターをバインドすることができます。
ユーザが選択を完了すると、Houdiniは、あなたのステートのonSelection()
コールバックメソッドをコールします。
onSelection()
メソッドでは、そのメソッドに渡される辞書からselection
アイテム(hou.GeometrySelectionオブジェクト)を取得することができます。
その辞書のname
から現行セレクターを取得することもできます。
その選択を受け取りたいのであれば、そのメソッドでTrue
を返すことで、Houdiniはセレクターを終了して、あなたのステートの通常の処理に戻ります。
False
を返すことで、その選択を拒否して、セレクターの実行を継続させることができます。
これは、ノードを使用しないステートで役に立ちます。例えば、表示ジオメトリからコンポーネントを選択し、それらのコンポーネントの情報を表示するステートがそうです。
Houdiniは、複数のジオメトリセレクターをステートにバインドすることができ、hou.SceneViewer.triggerStateSelectorを使って、個々のセレクターを開始/終了することができます。 バインド時に設定可能な名前によって、それらのセレクターがトリガーされます。
色々な選択ハンドラーの詳細は、選択イベントハンドラーを参照してください。
import hou class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onSelection(self, kwargs): sel = kwargs["selection"] sel_name = kwargs["name"] if sel_name == "selector1": # 3つのポリゴンを含んでいる選択のみを受け入れます。 are_prims = sel.geometryType() == hou.geometryType.Primitive are_all_polys = all(pt == hou.primType.Polygon for pt in sel.primitiveTypes()) selection = sel.selections()[0] count = selection.numSelected() return are_prims and are_all_polys and count == 3 count = 0 if sel_name == "selector2": # selector2 support selection = sel.selections()[0] count = selection.numSelected() return count > 0 template = hou.ViewerStateTemplate( "mystate", "My State", hou.sopNodeTypeCategory() ) template.bindFactory(MyState) # selector #1 template.bindGeometrySelector( name="selector1", prompt="Select three polygons", quick_select=False, use_existing_selection=True, geometry_types=hou.geometryType.Primitives, primitive_types=hou.primType.Polygon, allow_other_sops=False ) # selector #2 template.bindGeometrySelector( name="selector2", prompt="Select a primitive", quick_select=True, geometry_types=hou.geometryType.Primitives )
hou.ViewerStateTemplate.bindGeometrySelectorメソッドの引数に関する情報は、そのヘルプを参照してください。
オブジェクトセレクターのバインド ¶
hou.ViewerStateTemplate.bindObjectSelectorを使用することで、ビューアがカスタムOBJステートになった時に実行するOBJレベルの単一セレクターをバインドすることができます。
ユーザが選択を完了すると、Houdiniは、あなたのステートのonSelection()
コールバックメソッドをコールします。
onSelection()
メソッドでは、そのメソッドに渡される辞書からselection
アイテム(hou.OpNodeオブジェクトのリスト)を取得することができます。
その選択を受け取りたいのであれば、そのメソッドでTrue
を返すことで、Houdiniはセレクターを終了して、あなたのステートの通常の処理に戻ります。
False
を返すことで、その選択を拒否して、セレクターの実行を継続させることができます。
Houdiniは、複数のオブジェクトセレクターをステートにバインドすることができ、hou.SceneViewer.triggerStateSelectorを使って、個々のセレクターを開始/終了することができます。 バインド時に設定可能な名前によって、それらのセレクターがトリガーされます。
class MyOBJState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onSelection(self, kwargs): cameras = kwargs["selection"] print "Number of cameras selected: ", len(cameras) return True template = hou.ViewerStateTemplate("myOBJstate", "My OBJ State", hou.objNodeTypeCategory()) template.bindFactory(MyOBJState) # カメラオブジェクトだけを選択するセレクターを追加します。 template.bindObjectSelector( prompt="Select camera object(s) and press Enter", quick_select=False, use_existing_selection=True, allowed_types=('*cam*',))
hou.ViewerStateTemplate.bindObjectSelectorメソッドの引数に関する情報は、そのヘルプを参照してください。
Drawableセレクターのバインド ¶
Drawableセレクターは、Pythonステートで生成されたhou.GeometryDrawableとhou.SimpleDrawableのエンティティを選択することができます。 このDrawableセレクターをPythonステートにバインドするには、hou.ViewerStateTemplate.bindDrawableSelectorを使用します。
Drawableセレクターは、たくさんの局面においてジオメトリセレクターやオブジェクトセレクターに似ています:
-
Drawableコンポーネントは、矩形選択や縄選択などのHoudini Selectツールオプションを使用して選択またはロケート(マウス下の検索)することができます。
-
Drawableコンポーネントの選択とロケートは、Houdiniで検知されます。
-
DrawableセレクターはPythonステート登録時にそのPythonステートにバインドすることができます。
-
Drawable選択が発生すると
onSelection
ハンドラーがコールされます。
しかし、通常のセレクターと異なる局面もあります:
-
Pythonステートは、ロケートや選択されたDrawableエレメントを描画する役割を担っています。
-
Drawableコンポーネントがロケートされると(マウスが上に乗ると)、
onLocateSelection
ハンドラーがコールされます。このハンドラーによって、Pythonステートがジオメトリコンポーネントをハイライトさせることができます。 -
マウスをジオメトリ上に乗せた時にDrawableジオメトリのみが選択でき、他のシーンジオメトリは単に無視されます。
-
Selectツールのジオメトリモードメニューに関係なくDrawableコンポーネントを選択することができます。
-
Secure選択設定はDrawableセレクターに適用されません。
ジオメトリセレクターやオブジェクトセレクターと同様に、Pythonステートプラグイン側で新しい選択を受信するonSelection
ハンドラーを実装することができます。
onSelection
ハンドラーは、選択されたコンポーネントインデックスが格納されたPython辞書形式で新しい選択を取得します。
Pythonステートプラグイン側でマウスがDrawable上に置かれた時に新しいDrawableロケート情報を受信するonLocateSelection
ハンドラーを実装することができます。
それぞれのハンドラーのkwargs
辞書のdrawable_selection
エントリーには、新しい選択とロケート情報が格納されます。
Drawable選択フォーマット ¶
onSelection
ハンドラーは、Drawableコンポーネントが選択される度にコールされます。
onLocateSelection
ハンドラーは、Drawableコンポーネントがロケートされる度に(マウスが上に乗る度に)コールされます。
どちらのハンドラーもPython辞書のkwargs
のdrawable_selection
エントリーにDrawableコンポーネント情報が受信されます。
そのDrawableコンポーネント情報のフォーマットは以下のとおりです:
Drawable辞書 |
コンポーネント |
データ |
---|---|---|
Drawable名 |
face |
Drawableジオメトリ上のプリミティブインデックスのリスト。 |
line |
Drawableジオメトリ上のポイントインデックスのリスト。1番目と2番目のインデックスでラインが表現され、3番目と4番目のインデックスで別のラインが表現されていきます。 |
|
point |
Drawableジオメトリ上のポイントインデックスのリスト。 |
drawable_selection
エントリーの例:
{ "box_lines" : { "line" : [5, 7, 6, 4, 7, 6, 7, 2, 3, 6], }, "simple_tube" : { "face" : [3, 8, 7, 6, 5, 4], "line" : [4, 19, 3, 4, 5, 20, 4, 5, 6, 21, 5, 6, 7, 22, 6, 7], "point" : [8, 4, 5, 6, 7], }, "tube_lines" : { "line" : [1, 16, 0, 1, 15, 0, 1, 2, 17, 16, 14, 29, 13, 14, 14, 0], }, "tube_points" : { "point" : [17, 14, 2, 1, 0, 16], } }
以下の例では、Drawableセレクターを使用して、ロケートされたラインのポイントをハイライトさせます。
import hou def createViewerStateTemplate(): state_typename = "highlight_line_points" state_label = "Highlight Line Points Demo" state_cat = hou.sopNodeTypeCategory() template = hou.ViewerStateTemplate(state_typename, state_label, state_cat) template.bindFactory(State) template.bindIcon("MISC_python") template.bindDrawableSelector("Select a drawable component", name="my_drawable_selector", auto_start=True, drawable_mask=["box_lines"]) return template class State(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer # ロケートされたワイヤーフレームのDrawableボックス self.box_lines = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Line, "box_lines") self.box_lines.setParams({"color1":hou.Color(1,1,0)}) # ロケートされたラインのポイントをハイライトさせるDrawable。 self.box_points_h = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Point, "box_points_h") self.box_points_h.setParams({"color1":hou.Color(1,0,0), "radius":7, "style":hou.drawableGeometryPointStyle.LinearSquare}) def onEnter(self, kwargs): verb = hou.sopNodeTypeCategory().nodeVerb("box") verb.setParms({"type" : 1, "divrate":(5,5,5), "size":(3,3,3), "t": (0,1,0) }) geo = hou.Geometry() verb.execute(geo, []) self.box_lines.setGeometry(geo) self.box_points_h.setGeometry(geo) self.box_lines.show(True) def onDraw( self, kwargs ): handle = kwargs["draw_handle"] self.box_lines.draw(handle) self.box_points_h.draw(handle) def onLocatedSelection(self, kwargs): self.box_points_h.show(False) # 選択をループさせて、ハイライトさせるポイントを設定します。 for k,v in kwargs["drawable_selection"].items(): if k == "box_lines": if "line" in v: indices = v["line"] self.box_points_h.setParams({"indices":indices}) self.box_points_h.show(True) def onStopSelection(self, kwargs): selector_name = kwargs["name"] if selector_name == "my_drawable_selector": # ロケートされたコンポーネントをクリアします。 self.box_points_h.show(False)
Drawableセレクターハンドラーの詳細は、選択イベントハンドラーを参照してください。
Pythonステートは、Selectツールメニューで選択された選択修飾子(Add,Toggle,Removeなど)にも反応することができます。
以下のサンプルは、修飾子に対して標準のHoudiniカラーを用意するviewerstate.utils.DrawableSelectorColors
クラスを使用することで、ロケートされたジオメトリを選択された修飾子で色分けします。
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 self.selection = None # セレクターのカラーを管理します。 self.colors = su.DrawableSelectorColors(self.scene_viewer) # ピック/選択用のフェースDrawable。 self.faces = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Face, "faces") self.faces.setParams({"color1":(0,0,0,0.5)}) # ハイライト用のフェース。 self.faces_h = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Face, "faces_h") # 選択用のフェース。 self.faces_s = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Face, "faces_s") self.faces_s.setParams({"color1":self.colors["s"]}) # 選択不可なラインDrawable。 self.lines = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Line, "lines") self.lines.setParams({"color1":(1,1,1,0.5)}) def onEnter(self, kwargs): verb = hou.sopNodeTypeCategory().nodeVerb('sphere') verb.setParms({"type" : 2, "rad":(1,1,1), "rows": 50, "cols": 50}) geo = hou.Geometry() verb.execute(geo, []) self.faces.setGeometry(geo) self.faces_h.setGeometry(geo) self.faces_s.setGeometry(geo) self.faces.show(True) self.lines.setGeometry(geo) self.lines.show(True) def onDraw( self, kwargs ): handle = kwargs["draw_handle"] self.faces.draw(handle) self.faces_h.draw(handle) self.faces_s.draw(handle) self.lines.draw(handle) def onLocatedSelection(self, kwargs): self.faces_h.show(False) # ロケートされたフェースをループさせてハイライトさせます。 for k,v in kwargs["drawable_selection"].items(): if k == "faces" and "face" in v: indices = v["face"] # 選択された修飾子に合わせてDrawableにフェースカラーを設定します。 self.faces_h.setParams({"indices":indices, "color1":self.colors["face"]}) self.faces_h.show(True) def onSelection(self, kwargs): if "drawable_selection" in kwargs: self.selection = kwargs["drawable_selection"] self.faces_h.show(False) self.faces_s.show(False) if not self.selection: return # 選択されたDrawableをループさせて、選択されたフェースを描画します。 for k,v in self.selection.items(): if k == "faces" and "face" in v: indices = v["face"] if len(indices): self.faces_s.setParams({"indices":indices}) self.faces_s.show(True) return False def createViewerStateTemplate(): state_typename = kwargs["type"].definition().sections()["DefaultState"].contents() state_label = "Drawable selector modifier demo" state_cat = hou.sopNodeTypeCategory() template = hou.ViewerStateTemplate(state_typename, state_label, state_cat) template.bindFactory(State) template.bindIcon(kwargs["type"].icon()) hk1 = su.hotkey(state_typename, "drawable selector", "1") template.bindDrawableSelector("Select a drawable component", name="my_drawable_selector", auto_start=True, drawable_mask=["faces"], hotkey=hk1) return template
エントリーセレクターのバインド ¶
現在のところ、このメソッドは推奨されていません 。
理論的には、上記のスクリプトのstateutils.Selector
オブジェクトとstateutils.runSelectors()
と同様に、hou.ViewerStateTemplate.bindSelectorを使って複数のコンポーネントセレクターをステートにバインドすることができます。
ステートは、これらのセレクターを介して実行され、そのステートのonGenerate()
コールバックでノードを作成することができます。
しかし、私どもは、それらの選択を手動でスクリプト化し、上記で説明しているようにユーティリティ関数を使ってノードを作成することを推奨しています。
Volatile選択のハンドル ¶
Volatile選択(つまり、select
ステートを介した選択)は、Viewerステートによって単にonSelection
ハンドラーを実装するだけで可能です。
これは、指定したカテゴリのViewerステートセレクターまたはDrawableセレクターをバインドする際に通常実装するものと同じハンドルです。
しかし、Volatile選択に対応させるために何もセレクターをバインドさせる必要はありません。
選択ハンドラーに関する詳細は、ここを参照してください。
S
キーを押してVolatile選択を終了させると、選択されているエレメントを入力としてonSelection
がコールされます。
このコールバックはPythonステートセレクター専用に設計されており、Volatileセレクターの終了前にコールされないので、
onStartSelection
ハンドラーの実装はVolatile選択には何の役にも立ちません。
これはonStopSelection
でも同様です。
既存選択のハンドル ¶
ステートになった時に現行選択にアクセスすることができます。
ステートになった時に既に選択が存在していた場合、Houdiniは、その選択されたエレメント(s)をonSelection
ハンドラーに渡して、自動的にその選択を使用するようになります。
Volatile選択と同様に、既存選択をハンドルするためにセレクターをバインドする必要はなくて、単にonSelection
を実装するだけで十分です。
1つ以上のセレクターがバインドされている場合、Houdiniは、auto_start
セレクターまたはuse_existing_selector=True
で設定された最初のセレクターを使用するようになります。
Secure選択サポート ¶
セレクターが開始した時に、セレクターを現行ビューアの Secure選択 に準拠させるのかしないのかを設定することができます。 Secure選択は、例えば、ユーザがハンドルをクリックしたつもりが不意に選択を変更してしまわないようにするのに役立ちます。
セレクターは、以下のSecure選択オプションのどれかを使って登録することができます:
-
Obey: セレクターが現行ビューアのSecure選択設定に準拠します。これがデフォルトのオプションです。
-
On: セレクターが開始すると、セレクターが現行ビューアのSecure選択をOnに設定します。このオプションを使用すると、ステートを出た時に以前のビューアのSecure選択設定が復元されます。
-
Off: セレクターが開始すると、セレクターが現行ビューアのSecure選択をOffに設定します。このオプションを使用すると、ステートを出た時に以前のビューアのSecure選択設定が復元されます。
-
Ignore: このオプションを使用すると、セレクターはビューアのSecure選択設定を無視し、常にエレメントを選択することができます。
Secure選択が有効な時、アクティブセレクターは受け身モードなので、ユーザは現行選択を変更することができません。 Secure選択が無効な時、ユーザは現行選択を変更することができます。
詳細は、以下を参照してください: