Houdini Engine for Unreal
|
Version 2 of the Houdini Engine Plugin for Unreal now contains a public API. The public API is usable in C++, Blueprints and Python. The public API supports instantiating HDAs as actors in a world, setting parameters and inputs, cooking, inspecting and iterating over outputs and baking outputs.
We shall cover the functionality of the public API by building a script to instantiate an HDA, configure a curve input and a geometry input, and then iterate over the outputs once the node has cooked.
The public API provides a class, UHoudiniPublicAPI
, with some functions for controlling the session and instantiating Houdini Asset Actors.
The UHoudiniPublicAPI
class provides some functions for controlling the session and instantiating Houdini Asset Actors via the API:
IsSessionValid
: Returns true
if there is a valid Houdini Engine session running/connected.CreateSession
: Start a new Houdini Engine Session if there is no current session.StopSession
: Stops the current Houdini Engine session.RestartSession
: Stops, then creates a new Houdini Engine session.InstantiateAsset
: Instantiates an HDA in the specified world/level. Returns a wrapper for instantiated asset, see Instantiating HDAs.IsAssetCookingPaused
: Returns true
if asset cooking is paused.PauseAssetCooking
: Pause asset cooking (if not already paused).ResumeAssetCooking
: Resume asset cooking (if it was paused).CreateEmptyInput
: Create a new empty API input object. The user must populate it and then set it as an input on an asset wrapper. See Inputs.To get the UHoudiniPublicAPI
instance we fetch it via its associated blueprint function library: UHoudiniPublicAPIBlueprintLib
.
In C++:
In Blueprints:
In Python:
With the API instance in hand we can check the status of the Houdini Engine session, and start a session if required.
In C++:
In Blueprints:
In Python:
Now we can instantiate an HDA in our world. In our case we shall use one of the example HDAs included with the plugin: copy_to_curve_1_0
.
In C++:
In Blueprints:
In Python:
The InstantiateAsset
function returns an API wrapper class (UHoudiniPublicAPIAssetWrapper
) that we can use to interact with the instantiated HDA, for example to set parameter values or assign inputs. The InstantiateAsset
function has a number of parameters:
InHoudiniAsset
: The HDA uasset to instantiate.InInstantiateAt
: The Transform to instantiate the HDA actor with.InWorldContextObject
: A world context object for identifying the world to spawn in, if InSpawnInLevelOverride
is null.InSpawnInLevelOverride
: If not nullptr, then the AHoudiniAssetActor
is spawned in that level. If both InSpawnInLevelOverride
and InWorldContextObject
are null, then the actor is spawned in the current editor context world's current level.bInEnableAutoCook
: If true
(the default) the HDA will cook automatically after instantiation and after parameter, transform and input changes.bInEnableAutoBake
: If true
, the HDA output is automatically baked after a cook. Defaults to false
.InBakeDirectoryPath
: The directory to bake to if the bake path is not set via attributes on the HDA output.InBakeMethod
: The bake target (to actor vs blueprint).bInRemoveOutputAfterBake
: If true
, HDA temporary outputs are removed after a bake. Defaults to false
.bInRecenterBakedActors
: Recenter the baked actors to their bounding box center. Defaults to false
.bInReplacePreviousBake
: If true
, on every bake replace the previous bake's output (assets + actors) with the new bake's output. Defaults to false
.The API wraps instantiated HDAs in instances of UHoudiniPublicAPIAssetWrapper
. This class allows us to manipulate the instantiated HDA by setting parameters, inputs, cooking, baking and accessing outputs.
In the following subsections we shall cover the delegates the wrapper provides, how to set and get parameter values, how to set and get inputs and how to get outputs after a successful cook.
In many instances version 2 of the plug-in interacts with Houdini Engine asynchrounsly (when cooking, for example). This means that we need to be able to bind to a delegate in order to receive a callback after an operation has completed. For example, if we want to change a parameter, have the node cook and then fetch the output, we need to bind to the appropriate delegate in order to receive a callback once the process has completed. For these purposes the API wrapper provides a number of delegates:
OnPreInstantiationDelegate
: Broadcast just before an HDA is instantitated: the HDA's default parameter definitions are available, but the node has not yet been instantiated in HAPI/Houdini Engine. Parameters can be set at this point.OnPostInstantiationDelegate
: Broadcast after the asset was successfully instantiated. This is a good place to set/configure inputs before the first cook.OnPostCookDelegate
: Broadcast after a cook completes. Output objects/assets have not yet been created/updated.OnPreProcessStateExitedDelegate
: Broadcast at the end of the pre-processing phase (after a cook). Output objects/assets have not yet been created/updated.OnPostProcessingDelegate
: Broadcast after output processing has been done in Unreal. Output objects/assets have been created/updated.OnPostBakeDelegate
: Broadcast after baking the asset (not called for individual outputs that are baked to the content browser).OnPostPDGTOPNetworkCookDelegate
: Broadcast after a cook of a TOP network completes. Work item results have not necessarily yet been loaded.OnPostPDGBakeDelegate
: Broadcast after baking PDG outputs (not called for individual outputs that are baked to the content browser).OnProxyMeshesRefinedDelegate
: Broadcast after refining all proxy meshes for this wrapped asset.In the case of our example, we want to bind to OnPreInstantiationDelegate
to set some parameters once the parameter interface is ready, but before the first cook.
Assuming we have a class ACurveInputExample
(see Parameters for the definition of ACurveInputExample), we can bind some of its functions to the delegates where we want to set parameters and/or inputs.
In C++:
In Blueprints:
In Python:
The API wrapper provides a number of functions for setting and getting parameter values. The following functions can be used to set and get parameter values of a named parameter tuple (the parameter tuple name is a required argument, and the index of the parameter in the tuple is optional, defaulting to 0).
SetFloatParameterValue
: Set the value of a float parameterGetFloatParameterValue
: Get the value of a float parameterSetColorParameterValue
: Set the value of a float parameterGetColorParameterValue
: Get the value of a float parameterSetIntParameterValue
: Set the value of a integer parameterGetIntParameterValue
: Get the value of a integer parameterSetBoolParameterValue
: Set the value of a boolean/toggle parameterGetBoolParameterValue
: Get the value of a boolean/toggle parameterSetStringParameterValue
: Set the value of a string parameterGetStringParameterValue
: Get the value of a string parameterSetAssetRefParameterValue
: Set the value of string parameter from a UObject as an asset referenceGetAssetRefParameterValue
: Get the UObject instance referenced by a string parameterTriggerButtonParameter
: Executes the callback script of a button parameterGetParameterTuples
: Gets the values of all parameter tuples as an array of structsSetParameterTuples
: Sets the values of the parameter tuples in the given array of structsThe following functions are available to simplify working with ramp parameters:
SetRampParameterNumPoints
: Set the number of points of ramp parameterGetRampParameterNumPoints
: Get the number of points of ramp parameterSetFloatRampParameterPointValue
: Set the point position, value and interpolation of a float ramp point by indexGetFloatRampParameterPointValue
: Get the point position, value and interpolation of a float ramp point by indexSetFloatRampParameterPoints
: Set all of the points of a float ramp parameter from an arrayGetFloatRampParameterPoints
: Get all of the points of a float ramp parameter as an array.SetColorRampParameterPointValue
: Set the point position, value and interpolation of a color ramp point by indexGetColorRampParameterPointValue
: Get the point position, value and interpolation of a color ramp point by indexSetColorRampParameterPoints
: Set all of the points of a color ramp parameter from an arrayGetColorRampParameterPoints
: Get all of the points of a color ramp parameter as an array.Continuing with our example, we want to set two parameters: upvectoratstart
to false
and scale
to 0.2
during the HDA's pre-instantiation phase. In Delegates we bound the SetInitialParameterValues()
function to the pre-instantiation delegate. We shall now implement the function to set our parameter values.
In C++:
In Blueprints:
In Python:
At this point, once the plug-in begins the process of instantiating the HDA, the parameters will be set, ready for the first cook. The next thing we want to do is to set the inputs we need.
The API exposes all of the input types supported by the plug-in via API wrapper classes for each input type:
UHoudiniPublicAPIInput
: The base class of the inputs, one would only use this directly when creating subclasses from it for new input types.UHoudiniPublicAPIGeoInput
: A geometry input.UHoudiniPublicAPICurveInput
: A curve input.UHoudiniPublicAPIAssetInput
: An asset input (using another instantiated asset as an input).UHoudiniPublicAPIWorldInput
: A world input: using an actor from the world as an input.UHoudiniPublicAPILandscapeInput
: A landscape as an input.In order to set an input on a wrapped instantiated HDA, we must first instantiate the appropriate input class from the list above, and then set input objects and settings on the input. We can instantiate inputs by using the UHoudiniPublicAPIAssetWrapper::CreateEmptyInput()
function. The function takes the class of the input we want to create as its only argument.
Continuing with our example, we want to create two inputs: a geometry input, for the purpose of our example a cube will suffice, and a curve input.
In order to create the geometry input we must:
CreateEmptyInput()
to instantiate the new inputIn C++:
In Blueprints:
In Python:
Now that we have created and assigned the geometry input, we must create the second input, our curve input.
Curve inputs in the API have an additional helper class for constructing a curve from points: UHoudiniPublicAPICurveInputObject
. The object has a number of functions for setting/adding curve points, where each point is represented by an FTransform
:
SetCurvePoints
: Set the curve points to the specified array of FTransform
s.AppendCurvePoint
: Append a point, as an FTransform
, to the curve.ClearCurvePoints
: Remove all points from the curve.GetCurvePoints
: Get the curve's points as an array of FTransform
s.In addition to this other curve properties, such as the type (NURBS, Bezier etc), and whether the curve is open or closed, can also be configured on UHoudiniPublicAPICurveInputObject
via the following functions:
IsClosed
: Returns true
if the curve is closed.SetClosed
: Set whether the curve is closed or not.IsReversed
: Returns true
if the curve is reversed.SetReversed
: Set whether the curve is reversed.GetCurveType
: Get the curve type as an enum (Polygon, NURBs, Bezier, Points): EHoudiniPublicAPICurveType
.SetCurveType
: Set the curve type via the EHoudiniPublicAPICurveType
enum.GetCurveMethod
: Get the curve method as an enum (CVs, Breakpoints, Freehand): EHoudiniPublicAPICurveMethod
.SetCurveMethod
: Set the curve method via an enum EHoudiniPublicAPICurveMethod
.In order to create the curve input we must:
CreateEmptyInput()
to instantiate the new inputUHoudiniPublicAPICurveInputObject
UHoudiniPublicAPICurveInputObject
UHoudiniPublicAPICurveInputObject
instances as input objects, it also supports using UHoudiniSplineComponent
instances as input objects.In C++:
In Blueprints:
In Python:
To fetch the existing inputs of a wrapped asset, we can use the GetInputAtIndex()
and GetInputParameter()
functions, for node inputs and parameter-based inputs, respectively.
GetInputAtIndex()
and GetInputParameter()
functions return a copy of the input configuration of the wrapped asset. Changing the properties or input objects on the objects returned by GetInputAtIndex()
and GetInputParameter()
will not automatically update the inputs of the wrapped asset. In order to apply changes, the inputs have to be set again using SetInputAtIndex()
or SetInputParameter()
.Let us recap the example up to this point:
At this point the plug-in will continue processing the HDA asynchronously, sending parameters and inputs to Houdini, and once Houdini has cooked the node, receive the resulting output and build any outputs (meshes, materials etc).
The result of our example looks like this in the viewport:
And its details panel (notice the inputs and modified parameter values):
In the next section we shall look at how to access the outputs of the HDA after it has cooked and processed (created/updated) output objects/assets.
The UHoudiniPublicAPIAssetWrapper
class provides a number of functions that can be used to access to outputs of the HDA after a cook:
GetNumOutputs
: Returns the number of outputs the HDA has.GetOutputTypeAt
: Returns the type of the output (EHoudiniOutputType
: Mesh, Instancer, Landscape, Curve, Skeletal) at the given output index.GetOutputIdentifiersAt
: Populates an array of FHoudiniPublicAPIOutputObjectIdentifier
for a given output index. These identify individual objects in the output at the given index. For example, a rendered static mesh, and its collision meshes.GetOutputObjectAt
: Returns the output object at the given output index identified by the a FHoudiniPublicAPIOutputObjectIdentifier
. For example, a UStaticMesh
.GetOutputComponentAt
: Returns the output component for the object at the given output index identified by the a FHoudiniPublicAPIOutputObjectIdentifier
. For example, a UStaticMeshComponent
.HasAnyCurrentProxyOutput
: Returns true
any output contains any current proxy mesh.HasAnyCurrentProxyOutputAt
: Returns true
if the output at the specified index.IsOutputCurrentProxyAt
: Returns true
if the output at the specified index and identifier is a current proxy.As an example we shall:
OnPostProcessingDelegate
(already done in Delegates),In C++:
In Blueprints:
In Python:
The UHoudiniPublicAPIAssetWrapper
class also supports baking outputs. Broadly speaking there are 3 ways to approach baking. The first is to enable auto-baking:
SetAutoBakeEnabled
: Enables/disables auto-baking after a successful cook.IsAutoBakeEnabled
: Returns true
if auto-baking is enabled.Auto-baking will automatically bake the outputs after a cook and uses the bake settings configured on the instantiated HDA. These settings can be queried/set via:
SetBakeMethod
: Sets the bake method: bake to actors, or blueprints.GetBakeMethod
: Gets the bake method.SetRemoveOutputAfterBake
: Enables/disables automatically removing temporary HDA output from the world after a bake.GetRemoveOutputAfterBake
: Return true
if temporary HDA output will be removed from the world after a bake.SetRecenterBakedActors
: Enables/disables recentering baked output actors at their bounding box center.GetRecenterBakedActors
: Returns true
if recentering is enabled.SetReplacePreviousBake
: Enables/disables bake replacement mode: if enabled the previous bake's assets and actors are replaced (if names match).GetReplacePreviousBake
: Returns true
if replacement mode is enabled.The second way is to manually bake all outputs of an HDA:
BakeAllOutputs
: This bakes all outputs with the settings configured via the wrapper.BakeAllOutputsWithSettings
: This bakes all outputs but the bake settings can be passed to the function, overriding settings configured on the wrapper.The third way to bake is to bake a specific output object only, to the content browser, identified by its output index and identifier:
BakeOutputObjectAt
: Bakes the output object specified by its output index and indentifier.Since the instantiation and cooking of the HDAs are handled asynchronously in the plug-in we use delegates in order to be notified when HDA processing has entered a certain phase or when cooking is complete. This means we would have to manually bind and unbind to a lot of delegates while writing scripts/tools using the API. In order to simplify this a bit in Blueprints, the API also includes a Blueprint Async Action node: UHoudiniPublicAPIProcessHDANode
, or ProcessHDA
.
The ProcessHDA
node has all of the options of the UHoudiniPublicAPI::InstantiateAsset()
function, but it also provides some quality of life features:
FName
to FHoudiniParameterTuple
to set parameters without having to manually bind to delegates,int32
to UHoudiniPublicAPIInput
to set index-based node iputs without having to manually bind to delegates,FName
to UHoudiniPublicAPIInput
to set parameter-based inputs without having to manually bind to delegates,OnPreInstantiateDelegate
, OnPostInstantiateDelegate
and OnPostProcessDelegate
) to make it easier to bind custom logic to these events.Here is a screenshot of using the Process HDA
node with the blueprint version of our curve input example (slightly modified in that we have MakeGeoInput
and MakeCurveInput
functions to create and populate the UHoudiniPublicAPIGeoInput
and UHoudiniPublicAPICurveInput
respectively):
For Python there is something similar to the ProcessHDA
Blueprint node (see The Blueprint Async Processor) available: the ProcessHDA
Python class, located in the HoudiniEngineV2.asyncprocessor
module. The ProcessHDA
class constructor takes the same arguments as the Async Blueprint node. Processing is started by calling the ProcessHDA.activate()
function. The difference is that the ProcessHDA
class has a function for each of the delegates:
on_failure
on_complete
on_pre_instantiation
on_post_instantiation
on_post_auto_cook
on_pre_process
on_post_processing
on_post_auto_bake
These are internally bound to a UHoudiniPublicAPIAssetWrapper
and called when the delegates are broadcast. Users can subclass the ProcessHDA
class and override the above functions to implement their own functionality.
For example, here is our curve input example, but implemented as a subclass of ProcessHDA
:
The Public API also supports interacting with the PDG asset link if an HDA contains one or more TOP networks.
There are two delegates for PDG related events:
GetOnPostPDGTOPNetworkCookDelegate()
: Broadcast after cooking a TOP Network in the HDA. Work item results have not necessarily yet been loaded.GetOnPostPDGBakeDelegate()
: Broadcast after baking PDG outputs.The following functions are available in UHoudiniPublicAPIAssetWrapper
for interacting with the PDG asset link:
HasPDGAssetLink()
: Returns true
if the wrapped asset is valid and has a PDG asset link.GetPDGTOPNetworkPaths()
: Gets the paths (relative to the instantiated asset) of all TOP networks in the HDA.GetPDGTOPNodePaths()
: Gets the paths (relative to the specified TOP network) of all TOP nodes in the network.PDGDirtyAllNetworks()
: Dirty all TOP networks in this asset.PDGDirtyNetwork()
: Dirty the specified TOP network.PDGDirtyNode()
: Dirty the specified TOP node.PDGCookOutputsForNetwork()
: Cook all outputs for the specified TOP network.PDGCookNode()
: Cook the specified TOP node.PDGBakeAllOutputs()
: Bake all outputs of the instantiated asset's PDG contexts using the settings configured on the asset.PDGBakeAllOutputsWithSettings()
: Bake all outputs of the instantiated asset's PDG contexts using the specified settings.SetPDGAutoBakeEnabled()
: Set whether to automatically bake PDG work items after a successfully loading them.IsPDGAutoBakeEnabled()
: Returns true if PDG auto bake is enabled.SetPDGBakeMethod()
: Sets the bake method to use for PDG baking (to actor, blueprint, foliage).GetPDGBakeMethod()
: Gets the currently set bake method to use for PDG baking (to actor, blueprint, foliage).SetPDGBakeSelection()
: Set which outputs to bake for PDG, for example, all, selected network, selected node.GetPDGBakeSelection()
: Get which outputs to bake for PDG, for example, all, selected network, selected node.SetPDGRecenterBakedActors()
: Determines if baked actors are recentered to their bounding box center after a PDG bake, on the PDG asset link.GetPDGRecenterBakedActors()
: Returns true
if baked actors are recentered to their bounding box center after a PDG bake, on the PDG asset link.SetPDGBakingReplacementMode()
: Set the replacement mode to use for PDG baking (replace previous bake output vs incrementing the output names).GetPDGBakingReplacementMode()
: Get the replacement mode to use for PDG baking.bEnableAutoCook == true
), PDG will not cook automatically. The auto-cook setting for the PDG asset link is not currently exposed via the public API, so the user must call PDGCookNode()
or PDGCookOutputsForNetwork()
to cook PDG.A common pattern in the public API is that functions return a boolean to indicate success or failure. If an error occurred, the last error message can be retrieved use the GetLastErrorMessage()
function (available on any classes that derive from UHoudiniPublicAPIObjectBase
, such as UHoudiniPublicAPI
, UHoudiniPublicAPIAssetWrapper
, the input classes deriving from UHoudiniPublicAPIInput
and UHoudiniPublicAPICurveInputObject
).
The Content/Examples
directory of the plug-in contains a number of example HDAs, Python scripts and an Editor Utility Widget and Editor Utility Actor.
The following C++ example is available:
CurveInputExample.h and CurveInputExample.cpp
: The ACurveInputExample
discussed in this documentation.The following Blueprint examples are available:
EUA_CurveInputExample
: The blueprint example from this documentation.EUW_APIExample
: An example editor utility widget with various options for instantiating and HDA and setting parameters.The following Python examples are available:
asset_input_example.py
: An example script using asset inputs.bake_all_outputs_example.py
: An example script that bakes all outputs of the HDA after a cook.bake_output_object_example.py
: An example script that bakes a specific output object to the content browser after a cook.curve_input_example.py
: Similar to the example built in this documentation: using an input curve.eua_curve_input_example.py
: The example Python script from this documentation.geo_input_example.py
: An example scirpt for using geometry inputs.instances_example.py
: Setting a parameter, cooking an HDA with instances as output.landscape_input_example.py
: Using landscape inputs.outputs_example.py
: Iterating over the outputs after cooking an HDA.pdg_example.py
: Cooking and baking PDG output.process_hda_example.py
: Using the ProcessHDA
class to simplify delegate handling.ramp_parameter_example.py
: Using the helper functions in the API for setting ramp parameter points.start_session_example.py
: A simple example that just starts the Houdini Engine session.world_input_example.py
: Using world inputs.