Houdini 21.0 ネットワークとパラメータ

レシピのスクリプトを書く方法

スクリプトを使用して、レシピを作成、適用、実行する方法。

On this page

概要

レシピの一部としてスクリプトを実行することができます。 たいていの場合、レシピでキャプチャされたパラメータ値を適用したり、キャプチャされたノードを生成したりするだけで十分です。 しかし、その生成された値やノードを現在の状態に合うように変更したい場合もあります。例えば:

  • シミュレーションされる炎のレシピを配置する時に、現在のビューポートに合わせてボリューム光源をオン/オフする必要がある場合。

  • レシピでカメラを作成する時、現行ビューポートに合わせる必要がある場合

プリスクリプトとポストスクリプトを使用することで、レシピの適用前または適用後にPythonを実行することができます。

レシピは、内部ではdata.recipe.jsonに格納されている宣言的なデータモデルに基づいてキャプチャデータを格納します。 このセクションには、そのレシピに関連する他のプロパティも格納されます。

Note

レシピを使用して何もデータをキャプチャしないのであれば、プリスクリプトを使用することで、タスクを実行したり、他のレシピを呼び出すことができます。

作成

既存のノードからレシピを作成するスクリプトを書くには、以下のHOM関数を使用します:

適用

ネットワーク内にレシピを適用するスクリプトを書くには、以下のHOM関数を使用します:

Note

DecorationレシピおよびToolレシピには、itemsキー辞書が含まれています。 この辞書のキーは、データに記録されているノード名です。 レシピのノード名を知る必要がある場合は、この出力を確認してください。 レシピはデータ内に格納されている名前でノードの作成を試みますが、その名前のノードが既に存在する場合は別の名前を使用するので、この情報が役に立ちます。

プリスクリプトとポストスクリプト

Parameter Presetレシピを作成する場合、または、Recipe Builderを使用する場合、レシピ適用前に実行されるプリスクリプト用パラメータとレシピ適用後に実行されるポストスクリプト用パラメータを使用することができます。

レシピは、内部では、プリスクリプトをpre-script.recipe.pyセクションに、ポストスクリプトをpost-script.recipe.pyセクションに格納します。

プリスクリプトでは、以下のキーを持つkwargs辞書にアクセスすることができます

name: str

レシピの内部名。これはデバッグで役に立ちます。

data: dict

Houdiniセッションに適用されるレシピに格納されているデータ。 このデータを変更することで、そのレシピによって設定されるノードやパラメータを制御することができます。

node: hou.Node

Parameter PresetまたはNode Presetを実行した時に、ここには、そのプリセットが適用されるノードが格納されます。

network_editor

TabメニューからToolを実行した時に、ここには、そのタブの呼び出し元のhou.NetworkEditorが格納されます。

parent

Toolを実行した時に、ここには、そのツールが中で作成されるノードが格納されます。

central_node: hou.Node

Decorationを実行した時に、ここには、レシピが適用されて他のデコレータノードを生成するノードが格納されます。

anchor_node: hou.Node

Toolを実行した時に、ここには、そのツールが配置されるネットワークエディタ内のカーソル下に表示されるノードが格納されます。

click_to_place: bool

DecorationまたはToolを実行した時に、これは、ツールを配置するのにネットワークエディタ内をクリックする必要があるのかどうかを設定します。 配置選択中は、カーソルの下にアンカーノードが表示され、他のすべてのアイテムはそのアンカーノードを基準に配置されます。

drop_on_wire: bool

DecorationまたはToolを実行した時に、これは、ワイヤで接続された2つのノード間にツールをドロップすると既存のワイヤを再接続するかどうかを設定します。

avoid_overlap: bool

DecorationまたはToolを実行した時に、これは、作成されるノードが他のノードと重ならないように再配置するかどうかを設定します。

prompt: bool

Toolを実行した時に、これは、レシピ作成時に設定されたプロンプトを呼び出すかどうかを設定します。

ポストスクリプトでは、以下のキーを持つkwargs辞書にアクセスすることができます

name: str

レシピの内部名。これはデバッグで役に立ちます。

node: hou.Node

Parameter PresetまたはNode Presetを実行した時に、ここには、そのプリセットが適用されたノードが格納されます。

network_editor

TabメニューからToolを実行した時に、ここには、そのタブを呼び出したhou.NetworkEditorが格納されます。

central_node: hou.Node

Decorationを実行した時に、ここには、レシピが適用されて他のデコレータノードを生成したノードが格納されます。

anchor_node: hou.Node

Toolを実行した時に、ここには、そのツールが配置されたネットワークエディタ内のカーソル下に表示されたノードが格納されます。

items: dict[str, hou.Node]

DecorationまたはToolを実行した時に、ここには、そのレシピが生成または編集した他のすべてのノードが格納されます。

プリスクリプトの例: Pyro Sourceにマスクを追加

この使用例は、Pyro Source SOPにmaskソース(まだ存在しない場合)を追加するレシピの作成例です。

  1. SOPネットワーク内にRecipe Builderノードを作成します。

  2. そのサブネットワークの中に入って、Pyro Source SOPを追加して、その名前をpyrosource1にします。

  3. そのPyro Source SOP上の Attributes1に設定して、その新しいインスタンスの Namemaskに設定します。

  4. Recipe Builderに戻って、以下の操作を行ないます:

    • Recipe CategoryParameter Preset に設定します。

    • Internal Namepryosourcemaskに設定します。

    • LabelMaskに設定します。

  5. Data タブで、 Preset Nodepyrosource1に、 Preset Parameterattributesに設定します。

  6. Invocation タブで、 Multiparm OperationAppendに設定します。

  7. Scripts タブで、 Before Data Applied トグルをオンにして、以下のコードを入力して、レシピがまず最初に既存のmaskソースをチェックするようにします。

    # インスタンスが2回追加されないようにします。
    node = kwargs['parm'].node()
    
    # マルチパラメータの親。
    multiparm_parent = kwargs['parm']
    
    # 既存のインスタンスと比較するパラメータの名前。
    name = 'name'
    # データに格納されているのと同じパラメータの名前。
    data_name = f'{name}#'
    
    # 既存のソース名すべてを収集。
    existing_names = [node.parm(name + str(i)).evalAsString() for i in range(1, multiparm_parent.evalAsInt() + 1)]
    
    # 既存のソース名に基づいて、格納されているデータをフィルタリング。
    updated_data = []
    for entry_data in kwargs['data']:
        # 'data_name'キーには、あなたが探している値が直接保持されていない場合がありますが、
        # 代わりに、'value'キーが見つかる他の辞書を保持しています。
        value = entry_data[data_name]['value'] if isinstance(entry_data[data_name], dict) else entry_data[data_name]
        # このマルチパラメータインスタンスデータがまだ存在しない場合にのみこのマルチパラメータインスタンスデータを保持します。
        if entry_data[data_name]['value'] not in existing_names:
            updated_data.append(entry_data)
    
    # インスタンスが2回追加されないように、新しい更新リストを渡します。
    kwargs['data'] = updated_data
    

ポストスクリプトの例: 現在のタイムラインに基づいて開始フレームを設定

この使用例は、レシピが作成された時に、そのレシピ内のアンカーノードの Start Frame パラメータを現在のタイムラインのフレーム範囲の開始フレームに設定したい場合の例です。

  1. Recipe BuilderノードScripts タブをクリックします。

  2. After Data Applied トグルをオンにして、以下のコードを入力します:

    anchor_node = kwargs['anchor_node']
    anchor_node.parm('startframe').set(hou.playbar.frameRange()[0])
    

サンプル: 階層的なレシピ

この使用例は、ツールレシピでノードセットを作成したいものの、それらのノードのうち1つのノードにプリセットレシピを適用したい場合の例です。

ノードに正しい値を適用した状態でツールレシピを保存することもできますが、プリセットレシピに依存するツールレシピをリセットすることなくそのプリセットレシピを変更できる柔軟性が欲しい場合があります。 これを行なうには、ツールレシピを適用してから、作成されたノードの1つを検索して、そのノードにプリセットを適用するようなツールまたはRecipe Builderのスクリプトを作成することで可能です。

hou.data.applyToolRecipe("first_recipe")
node = kwargs["items"]["foobar"]
hou.data.applyNodePresetRecipe("second_recipe", node)

サンプル: プリセットメニューの作成

この使用例は、ノードに対して利用可能なレシピをOrdered Menu(プリセットメニュー)に表示し、そのメニューから項目を選択する度にレシピを呼び出す方法の例です。

  1. ノードに対して複数のNode PresetまたはParameter Presetを作成します。 Recipe Builderを使用した場合、 Submenu パラメータを使用することで、レシピをカテゴリに分けることができます。

  2. Edit Parameter Interfaceウィンドウを開いて、プリセットメニューとして設定したい Ordered Memu パラメータをクリックします。

  3. Menu タブで Use Menu トグルをオンにして、 Menu Script タブに以下のコードを入力します。 このコードは、サブメニューをMaterialsに設定して作成されたレシピに基づいて、プリセットメニューの項目を構築します。 submenuが空っぽの文字列の場合、そのメニューにはそのノードで利用可能なすべてのプリセットが表示されます。

    import recipeutils as ru
    return ru.buildPresetMenu(kwargs,
                                submenu='Materials')
    
  4. Parameter タブで、 Callback Script に以下のコードを設定します。 これによって、このプリセットメニューから項目を選択する度にレシピが呼び出されます。

    import recipeutils as ru; ru.setPresetMenu(kwargs)
    

プリセットが選択された時にデフォルト状態に戻るプリセットメニューを設定するには、上記の手順に従い、コードを以下のように変更します:

  • 各プリセットレシピのポストスクリプトとして以下のコードを入力します。 presetmenuはプリセットメニューパラメータの名前です。 これによって、 Recipeメニューからプリセットを呼び出した時にも、プリセットパラメータが変更されます。

    kwargs['node'].parm('presetmenu').set(0)
    
  • Edit Parameter Interface ウィンドウ内のプリセットパラメータに対して、 Menu Script タブに以下のコードを入力します:

    import recipeutils as ru
    return ru.buildPresetMenu(kwargs,
                            submenu='Materials',
                            revert=True,
                            revert_token='materials',
                            revert_label='Materials')
    
  • Callback Script パラメータには、以下のコードを入力します:

    import recipeutils as ru; ru.setPresetMenu(kwargs, revert=True)
    

サンプル: デコレータのマージ

この使用例は、作成されたデコレータノードを特定の方法で接続する複雑な挙動の例です。 このレシピは、アンカーノードの入力にマージノードが接続されるようにして、新しく作成されるすべてのデコレーションがそのマージノードに接続されるようにします。

  1. LOPネットワーク内にRecipe Builderノードを作成します。

  2. そのサブネットワークの中に入って、2個のNullノードを追加します。

  3. それらの新しいNullノードを接続して、それぞれ名前をstart1end1に変更します。

    Note

    end1を既存のnull1ノードに接続しないでください。

  4. Recipe Builderノードに戻って、以下の設定を行ないます:

    • Recipe CategoryDecoration に設定します。

    • Internal Namedecoration_mergerに設定します。

    • LabelDecoration Mergerに設定します。

  5. Scripts タブで、 Before Data Applied を有効にして、以下のコードを入力します:

    # 作成後のノードの再配置を強制的に無効にします。
    kwargs['avoid_overlap'] = False)
    
  6. Scripts タブで、 After Data Applied を有効にして、以下のコードを入力します:

    from recipeutils import pprint
    
    # まず最初にターゲットノードを取得します。
    target_node = kwargs['central_node']
    
    merge_node = None
    
    # ターゲットノードに入力がなかった場合、マージノードを作成します。
    if len(target_node.inputs()) == 0:
        # 命令的なPython関数ではなく、データを使用してマージノードを作成します。
        merge_node_name = 'wire_nodes_into_here'
        result = hou.data.createItemsFromData(target_node.parent(), {
            merge_node_name : {
                "type": "merge",
                "position": [target_node.position().x(), target_node.position().y() + 1.0], # offset merge node upwards
                "outputs": [
                    {
                        "to": target_node.name(),
                        "to_index": 0,
                        "from_index": 0,
                    }
                ]
            }
        })
        # 作成されたノードから実際のhou.Nodeオブジェクトを取得します。
        # このキーは、データ内のノード名です。
        merge_node = result[merge_node_name]
    else: # ターゲットノードに入力があった場合、1番目の入力からノードを取得します。
        # 場合によっては、ここでそのノードがマージノードかどうかをチェックしたいかもしれませんが、この例ではその手順をスキップします。
        merge_node = target_node.inputs()[0]
    
    # pprint(kwargs['items'])
    # 同様に、チェーン内の末端ノードを取得します。その末端ノードはデータ内では'end1'として記録されているので、それをキーとして使用します。
    end_node = kwargs['items']['end1']
    
    if merge_node: # この時点で、有効なマージノードが取得できているはずです。
        # データ関数を使用して入力コネクションを作成します。
        # 既存のコネクションを取得して、それをデータに追加します。
        input_data = merge_node.inputsAsData()
    
        input_data.append({
            "from": end_node.name(), # これによって、確実に正しい名前が取得されます。
            "from_index": 0,
            "to_index": len(input_data),
        })
    
        # コネクションを作成します。
        merge_node.setInputsFromData(input_data)
    
  7. Recipe タブで、 Save Recipe をクリックします。

  8. LOPネットワーク内にNullノードを追加して、 Decoration Merger レシピを複数回呼び出します。すると、以下の結果が得られます:

Tips

  • 生成されたレシピデータ構造を読みやすい形式で出力するには、以下のスクリプトを使用します。

    import recipeutils as ru
    
  • recipeutilsモジュールを使用すると、利用可能なNode Presetなどのレシピ情報をプリントすることができます。例:

    import recipeutils as ru
    
    names = ru.recipeNames(ru.RecipeCategory.nodePreset,
                            nodetype_categories=hou.sopNodeTypeCategory(),
                            include_label=True, pad=True)
    
    ru.pprint(names)
    

    また、name_patternオプションを指定することで、レシピの内部名に基づいてレシピをフィルタリングすることができます。

ネットワークとパラメータ

ネットワーク

パラメータの編集

次のステップ

エクスプレッション

レシピ

リファレンス

導師レベル