On this page |
このチュートリアルでは、以下を通して、Public APIの機能をいくつか説明していきます:
-
HDAをインスタンス化するためのスクリプトを作成する
-
カーブ入力とジオメトリ入力を設定する
-
ノードがクックされたら出力を反復させる
利用可能なコマンドのリストは、Public APIドキュメントを参照してください。
Houdini Public APIインスタンス ¶
UHoudiniPublicAPI
インスタンスは、それ関連のブループリント関数ライブラリUHoudiniPublicAPIBlueprintLib
を介して取得します。
プラグインのPublic APIはEditorモジュールの中にあるため、Editor Onlyのブループリントからのみアクセスすることができます。
ブループリントを作成する際は、EditorUtilityActor
またはEditorUtilityWidget
(UIウィジェット用)といったEditor Onlyの基本クラスを選択し、ブループリントをEditor Onlyにする必要があります。
Warning
既存のブループリントを編集すると、そのブループリントはEditor Onlyのブループリントの子に親子化し直されます。 しかし、新しい親クラスと古い親クラスに互換性がないと、その親子化によって問題が発生したり、データが失なわれてしまう可能性があります。
詳細は、Unrealのブループリントによるスクリプト化のドキュメントを参照してください。
C++:
#include "HoudiniPublicAPIBlueprintLib.h" #include "HoudiniPublicAPI.h" UHoudiniPublicAPI* const API = UHoudiniPublicAPIBlueprintLib::GetAPI();
ブループリント:
Python:
import unreal api = unreal.HoudiniPublicAPIBlueprintLib.get_api()
セッションの開始 ¶
APIインスタンスセットアップを使用して、Houdini Engineセッションのステータスを確認し、(必要な場合)そのセッションを開始します。
C++:
if (!API->IsSessionValid()) API->CreateSession();
ブループリント:
Python:
if not api.is_session_valid(): api.create_session()
HDAのインスタンス化 ¶
ワールドでHDAをインスタンス化することができます。
ここでは、プラグインに含まれているサンプルHDAの1つであるcopy_to_curve_1_0
を使用しています。
C++:
// HDA uassetをロードします。 UHoudiniAsset* const ExampleHDA = Cast<UHoudiniAsset>(StaticLoadObject(UHoudiniAsset::StaticClass(), nullptr, TEXT("/HoudiniEngine/Examples/hda/copy_to_curve_1_0.copy_to_curve_1_0"))); // HDAをインスタンス化してそれを操作できるようにAPIラッパーインスタンスを作成します。 UHoudiniPublicAPIAssetWrapper* const Wrapper = API->InstantiateAsset(ExampleHDA, FTransform::Identity)
ブループリント:
Python:
# HDA uassetをロードします。 example_hda = unreal.load_object(None, '/HoudiniEngine/Examples/hda/copy_to_curve_1_0.copy_to_curve_1_0') # HDAをインスタンス化してそれを操作できるようにAPIラッパーインスタンスを作成します。 wrapper = api.instantiate_asset(example_hda, instantiate_at=unreal.Transform())
InstantiateAsset
関数は、インスタンス化されたHDAを操作(例えば、パラメータ値の設定や入力の割り当てなど)するAPIラッパークラス(UHoudiniPublicAPIAssetWrapper
)を返します。
Asset Wrapper - デリゲート ¶
たいていのインスタンスでは、プラグインはHoudini Engineと非同期でやり取りします。 つまり、操作が完了した後にコールバックを受け取るには、デリゲートをバインドする必要があります。
パラメータインターフェースの準備が整ったら、最初のクックの前に、OnPreInstantiationDelegate
をバインドしてパラメータを設定します。
Note
これは、最も早いタイミングでのパラメータ設定ですが、必ずしも必要ではありません。 HDAをインスタンス化してクックし、パラメータを設定して、再クックすることも可能です。 HDAのクックに長い時間がかかる場合は、デリゲートをいくつか効率的に使用して、負荷の大きい再クックを避けることをお勧めします。
クラスACurveInputExample
を使用すると、一部の関数をデリゲートにバインドして、パラメータおよび/または入力を設定できます。
C++:
void ACurveInputExample::RunCurveInputExample_Implementation() { // APIインスタンスを取得します。 UHoudiniPublicAPI* const API = UHoudiniPublicAPIBlueprintLib::GetAPI(); // 確実にセッションが実行するようにします。 if (!API->IsSessionValid()) API->CreateSession(); // HDA uassetをロードします。 UHoudiniAsset* const ExampleHDA = Cast<UHoudiniAsset>(StaticLoadObject(UHoudiniAsset::StaticClass(), nullptr, TEXT("/HoudiniEngine/Examples/hda/copy_to_curve_1_0.copy_to_curve_1_0"))); // HDAをインスタンス化してそれを操作できるようにAPIラッパーインスタンスを作成します。 AssetWrapper = API->InstantiateAsset(ExampleHDA, FTransform::Identity); if (IsValid(AssetWrapper)) { // インスタンス化前が、パラメータ値を設定できる最も早いタイミングです。 AssetWrapper->GetOnPreInstantiationDelegate().AddUniqueDynamic(this, &ACurveInputExample::SetInitialParameterValues); // もう少し後で: 入力を設定する必要もありますが、入力を使用できるのはインスタンス化後です。 AssetWrapper->GetOnPostInstantiationDelegate().AddUniqueDynamic(this, &ACurveInputExample::SetInputs); // もう少し後で: また、ノードをクックしてプラグインが出力を処理した後、出力を表示する必要もあります。 AssetWrapper->GetOnPostProcessingDelegate().AddUniqueDynamic(this, &ACurveInputExample::PrintOutputs); } }
Note
この例では、インスタンス化前にパラメータを設定し、インスタンス化後に入力を設定しています。 インスタンス化後にパラメータと入力を設定することもできます。
ブループリント:
Python:
def run_curve_input_example(self): # APIインスタンスを取得します。 api = unreal.HoudiniPublicAPIBlueprintLib.get_api() # 確実にセッションが実行するようにします。 if not api.is_session_valid(): api.create_session() # HDA uassetをロードします。 example_hda = unreal.load_object(None, '/HoudiniEngine/Examples/hda/copy_to_curve_1_0.copy_to_curve_1_0') # HDAをインスタンス化してそれを操作できるようにAPIラッパーインスタンスを作成します。 self._asset_wrapper = api.instantiate_asset(example_hda, instantiate_at=unreal.Transform()) if self._asset_wrapper: # インスタンス化前が、パラメータ値を設定できる最も早いタイミングです。 self._asset_wrapper.on_pre_instantiation_delegate.add_callable(self._set_initial_parameter_values) # もう少し後で: 入力を設定する必要もありますが、入力を使用できるのはインスタンス化後です。 self._asset_wrapper.on_post_instantiation_delegate.add_callable(self._set_inputs) # もう少し後で: また、ノードをクックしてプラグインが出力を処理した後、出力を表示する必要もあります。 self._asset_wrapper.on_post_processing_delegate.add_callable(self._print_outputs)
パラメータの設定 ¶
HDAのインスタンス化前の段階で、2つのパラメータを設定します:
-
upvectoratstart
を false に -
scale
を 0.2 に
デリゲートでは、インスタンス化前のデリゲートにSetInitialParameterValues()
関数をバインドしました。
これによって、その関数を実装することでパラメータ値が設定できるようになりました。
C++:
#pragma once #include "CoreMinimal.h" #include "EditorUtilityActor.h" #include "CurveInputExample.generated.h" class UHoudiniPublicAPIAssetWrapper; UCLASS() class HOUDINIENGINEEDITOR_API ACurveInputExample : public AEditorUtilityActor { GENERATED_BODY() public: ACurveInputExample(); UFUNCTION(BlueprintNativeEvent, BlueprintCallable, CallInEditor) void RunCurveInputExample(); protected: // 初期パラメータ値を設定: upvectorstartを無効にし、スケールを0.2に設定します。 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, CallInEditor) void SetInitialParameterValues(UHoudiniPublicAPIAssetWrapper* InWrapper); // 入力を設定: 入力0は立方体、入力1は螺旋にします。 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, CallInEditor) void SetInputs(UHoudiniPublicAPIAssetWrapper* InWrapper); // HDAによって生成された出力を表示します(クック後)。 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, CallInEditor) void PrintOutputs(UHoudiniPublicAPIAssetWrapper* InWrapper); UPROPERTY(BlueprintReadWrite) UHoudiniPublicAPIAssetWrapper* AssetWrapper; }; void ACurveInputExample::SetInitialParameterValues_Implementation(UHoudiniPublicAPIAssetWrapper* InWrapper) { // upvectoratstartパラメータのチェックを外します。 InWrapper->SetBoolParameterValue(TEXT("upvectoratstart"), false); // スケールを0.2に設定します。 InWrapper->SetFloatParameterValue(TEXT("scale"), 0.2f); // 初期値の設定を完了したので、デリゲートからバインドを解除できます。 InWrapper->GetOnPreInstantiationDelegate().RemoveDynamic(this, &ACurveInputExample::SetInitialParameterValues); }
ブループリント:
Python:
import math import unreal @unreal.uclass() class CurveInputExample(unreal.PlacedEditorUtilityBase): def __init__(self, *args, **kwargs): self._asset_wrapper = None def run_curve_input_example(self): ... def _set_initial_parameter_values(self, in_wrapper): """ 初期パラメータ値を設定: upvectorstartを無効にし、スケールを0.2に設定します。 """ # upvectoratstartパラメータのチェックを外します。 in_wrapper.set_bool_parameter_value('upvectoratstart', False) # スケールを0.2に設定します。 in_wrapper.set_float_parameter_value('scale', 0.2) # 初期値の設定を完了したので、デリゲートからバインドを解除できます。 in_wrapper.on_pre_instantiation_delegate.remove_callable(self._set_initial_parameter_values) def _set_inputs(self, in_wrapper): """ 入力を設定: 入力0は立方体、入力1は螺旋にします。 """ raise NotImplementedError def _print_outputs(self, in_wrapper): """ HDAによって生成された出力を表示します(クック後)。 """ raise NotImplementedError
プラグインがHDAのインスタンス化プロセスを開始すると、パラメータが設定され、最初のクックの準備が整います。
入力の作成 ¶
ラップされ、インスタンス化されたHDAに入力を設定するには、適切な入力クラスをインスタンス化してから、入力オブジェクトと設定を入力に設定する必要があります。
UHoudiniPublicAPIAssetWrapper::CreateEmptyInput()
関数を使用して、入力をインスタンス化します。
関数は、入力のクラスを唯一の引数として受け取ります。この例では、ジオメトリおよびカーブ入力を作成します。
ジオメトリ入力を作成するには:
-
CreateEmptyInput()
と入力し、新しい入力をインスタンス化します。 -
その入力オブジェクトとして立方体アセットを設定します。
-
インスタンス化されたHDAに入力を設定します。
C++:
void ACurveInputExample::SetInputs_Implementation(UHoudiniPublicAPIAssetWrapper* InWrapper) { // 空っぽのジオメトリ入力を作成します。 UHoudiniPublicAPIGeoInput* const GeoInput = Cast<UHoudiniPublicAPIGeoInput>(InWrapper->CreateEmptyInput(UHoudiniPublicAPIGeoInput::StaticClass())); // 立方体のスタティックメッシュアセットをロードします。 UStaticMesh* const Cube = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), nullptr, TEXT("/Engine/BasicShapes/Cube.Cube"))); // ジオメトリ入力に入力オブジェクト配列を設定します(この場合は立方体のみを含んでいます)。 GeoInput->SetInputObjects({Cube}); // ラッパーを介してインスタンス化されたHDAに入力を設定します。 InWrapper->SetInputAtIndex(0, GeoInput); // やること: カーブ入力を作成する // 初期値の設定を完了したので、デリゲートからバインドを解除できます。 InWrapper->GetOnPostInstantiationDelegate().RemoveDynamic(this, &ACurveInputExample::SetInputs); }
ブループリント:
Python:
class CurveInputExample(unreal.PlacedEditorUtilityBase): ... def _set_inputs(self, in_wrapper): """ 入力を設定: 入力0は立方体、入力1は螺旋にします。 """ # 空っぽのジオメトリ入力を作成します。 geo_input = in_wrapper.create_empty_input(unreal.HoudiniPublicAPIGeoInput) # 立方体のスタティックメッシュアセットをロードします。 cube = unreal.load_object(None, '/Engine/BasicShapes/Cube.Cube') # ジオメトリ入力に入力オブジェクト配列を設定します(この場合は立方体のみを含んでいます)。 geo_input.set_input_objects((cube, )) # ラッパーを介してインスタンス化されたHDAに入力を設定します。 in_wrapper.set_input_at_index(0, geo_input) # やること: カーブ入力を作成する # 入力の設定を完了したので、デリゲートからバインドを解除できます。 in_wrapper.on_post_instantiation_delegate.remove_callable(self._set_inputs)
カーブ入力 ¶
カーブ入力を作成するには:
-
CreateEmptyInput()
と入力し、新しい入力をインスタンス化します。 -
UHoudiniPublicAPICurveInputObject
を作成します。 -
UHoudiniPublicAPICurveInputObject
にカーブポイントを追加します。 -
カーブ入力に、入力オブジェクトとしてカーブオブジェクトを設定します。
-
インスタンス化されたHDAに入力を設定します。
Note
APIカーブ入力は、入力オブジェクトとしてUHoudiniPublicAPICurveInputObject
に制限されていません。
また、入力オブジェクトとしてUHoudiniSplineComponent
インスタンスもサポートしています。
C++:
void ACurveInputExample::SetInputs_Implementation(UHoudiniPublicAPIAssetWrapper* InWrapper) { // 空っぽのジオメトリ入力を作成します。 ... // 空っぽのカーブ入力を作成します。 UHoudiniPublicAPICurveInput* const CurveInput = InWrapper->CreateEmptyInput(UHoudiniPublicAPICurveInput::StaticClass()); // カーブ入力オブジェクトを作成します。 UHoudiniPublicAPICurveInputObject* const CurveObject = Cast<UHoudiniPublicAPICurveInput>(NewObject<UHoudiniPublicAPICurveInputObject>(CurveInput)); // それをNURBSカーブにします。 CurveObject->SetCurveType(EHoudiniPublicAPICurveType::Nurbs) // カーブのポイントを設定します。この例では、100ポイントで構成される螺旋を作成します。 TArray<FTransform> CurvePoints; CurvePoints.Reserve(100); for (int32 i = 0; i < 100; ++i) { const float t = i / 20.0f * PI * 2.0f; const float x = 100.0f * cos(t); const float y = 100.0f * sin(t); const float z = i; CurvePoints.Emplace(FTransform(FVector(x, y, z))); } CurveObject->SetCurvePoints(CurvePoints); // 入力オブジェクトとしてカーブラッパーを設定します。 CurveInput->SetInputObjects({CurveObject}); // ノード入力1として、入力データをHDAにコピーします。 InWrapper->SetInputAtIndex(1, CurveInput); // 初期値の設定を完了したので、デリゲートからバインドを解除できます。 InWrapper->GetOnPostInstantiationDelegate().RemoveDynamic(this, &ACurveInputExample::SetInputs); }
ブループリント:
Python:
class CurveInputExample(unreal.PlacedEditorUtilityBase): ... def _set_inputs(self, in_wrapper): """ 入力を設定: 入力0は立方体、入力1は螺旋にします。 """ # 空っぽのジオメトリ入力を作成します。 ... # カーブ入力を作成します。 curve_input = in_wrapper.create_empty_input(unreal.HoudiniPublicAPICurveInput) # カーブラッパー/ヘルパーを作成します。 curve_object = unreal.HoudiniPublicAPICurveInputObject(curve_input) # それをNURBSカーブにします。 curve_object.set_curve_type(unreal.HoudiniPublicAPICurveType.NURBS) # カーブのポイントを設定します。 # この例では、100ポイントで構成される螺旋を作成します。 curve_points = [] for i in range(100): t = i / 20.0 * math.pi * 2.0 x = 100.0 * math.cos(t) y = 100.0 * math.sin(t) z = i curve_points.append(unreal.Transform([x, y, z], [0, 0, 0], [1, 1, 1])) curve_object.set_curve_points(curve_points) # 入力オブジェクトとしてカーブラッパーを設定します。 curve_input.set_input_objects((curve_object, )) # ノード入力1として、入力データをHDAにコピーします。 in_wrapper.set_input_at_index(1, curve_input) # 入力の設定を完了したので、デリゲートからバインドを解除できます。 in_wrapper.on_post_instantiation_delegate.remove_callable(self._set_inputs)
ラップされたアセットの既存の入力を取得するには、ノード入力とパラメータベースの入力にGetInputAtIndex()
関数とGetInputParameter()
関数を使用します。
まとめ ¶
例のまとめ:
-
Houdini Public APIインスタンス で、Houdini Engineセッションが実行されていることを確認し、HDAのインスタンス化プロセスを開始します。
-
デリゲート では、デリゲートを使用して、カスタム関数をHDAのインスタンス化前およびインスタンス後の段階にバインドします。
-
パラメータ では、インスタンス化前の段階でHDAのパラメータを設定します。
-
入力 では、インスタンス後の段階でHDAに入力を作成および設定します。
この時点で、プラグインはHDAの処理を非同期で継続し、Houdiniにパラメータと入力を送ります。Houdiniがノードをクックしたら、結果の出力を受け取って出力(メッシュ、マテリアル)を作成します。
Unrealビューポートでの例の結果:
Detailsパネル(入力と変更されたパラメータ値に注目してください):
出力 ¶
このセクションでは、HDAがクックされ、出力オブジェクトおよびアセットを処理した後に、HDAの出力にアクセスする方法を説明します。
-
OnPostProcessingDelegateにバインドし(デリゲートで実行済み)、
-
すべての出力をイテレートして、出力オブジェクトとコンポーネント名を表示し、
-
出力オブジェクトがプロキシであるかどうかを表示します。
C++:
void ACurveInputExample::PrintOutputs_Implementation(UHoudiniPublicAPIAssetWrapper* InWrapper) { // HDAによって生成されたすべての出力を表示します。 const int32 NumOutputs = InWrapper->GetNumOutputs(); UE_LOG(LogTemp, Log, TEXT("NumOutputs: %d"), NumOutputs); if (NumOutputs > 0) { for (int32 OutputIndex = 0; OutputIndex < NumOutputs; ++OutputIndex) { TArray<FHoudiniPublicAPIOutputObjectIdentifier> Identifiers; InWrapper->GetOutputIdentifiersAt(OutputIndex, Identifiers); UE_LOG(LogTemp, Log, TEXT("\toutput index: %d"), OutputIndex); UE_LOG(LogTemp, Log, TEXT("\toutput type: %d"), InWrapper->GetOutputTypeAt(OutputIndex)); UE_LOG(LogTemp, Log, TEXT("\tnum_output_objects: %d"), Identifiers.Num()); if (Identifiers.Num() > 0) { for (const FHoudiniPublicAPIOutputObjectIdentifier& Identifier : Identifiers) { UObject* const OutputObject = InWrapper->GetOutputObjectAt(OutputIndex, Identifier); UObject* const OutputComponent = InWrapper->GetOutputComponentAt(OutputIndex, Identifier); const bool bIsProxy = InWrapper->IsOutputCurrentProxyAt(OutputIndex, Identifier); UE_LOG(LogTemp, Log, TEXT("\t\tidentifier: %s_%s"), *(Identifier.PartName), *(Identifier.SplitIdentifier)); UE_LOG(LogTemp, Log, TEXT("\t\toutput_object: %s"), IsValid(OutputObject) ? *(OutputObject->GetFName().ToString()) : TEXT("None")) UE_LOG(LogTemp, Log, TEXT("\t\toutput_component: %s"), IsValid(OutputComponent) ? *(OutputComponent->GetFName().ToString()) : TEXT("None")) UE_LOG(LogTemp, Log, TEXT("\t\tis_proxy: %d"), bIsProxy) UE_LOG(LogTemp, Log, TEXT("")) } } } } }
ブループリント:
Python:
class CurveInputExample(unreal.PlacedEditorUtilityBase): ... def _print_outputs(self, in_wrapper): """ HDAによって生成された出力を表示します(クック後)。 """ num_outputs = in_wrapper.get_num_outputs() print('num_outputs: {}'.format(num_outputs)) if num_outputs > 0: for output_idx in range(num_outputs): identifiers = in_wrapper.get_output_identifiers_at(output_idx) print('\toutput index: {}'.format(output_idx)) print('\toutput type: {}'.format(in_wrapper.get_output_type_at(output_idx))) print('\tnum_output_objects: {}'.format(len(identifiers))) if identifiers: for identifier in identifiers: output_object = in_wrapper.get_output_object_at(output_idx, identifier) output_component = in_wrapper.get_output_component_at(output_idx, identifier) is_proxy = in_wrapper.is_output_current_proxy_at(output_idx, identifier) print('\t\tidentifier: {}'.format(identifier)) print('\t\toutput_object: {}'.format(output_object.get_name() if output_object else 'None')) print('\t\toutput_component: {}'.format(output_component.get_name() if output_component else 'None')) print('\t\tis_proxy: {}'.format(is_proxy)) print('')
出力をベイクする ¶
UHoudiniPublicAPIAssetWrapper
クラスも出力のベイクをサポートしています。
ベイクする方法は3通りあります:
-
自動ベイクを有効にします。これは、クック後に出力を自動的にベイクし、インスタンス化されたHDAに設定されたベイク設定を使用します。
-
HDAのすべての出力を手動でベイクします。
-
出力インデックスおよび識別子によって識別された特定の出力オブジェクトのみベイクします。
詳細は、出力をベイクするを参照してください。
次のステップ ¶
ブループリント/Pythonバージョンの非同期プロセス、コンテンツサンプル、Public APIのコマンドリストに関する詳細は、Public APIドキュメントを参照してください。