Skip to content

Commit 7f65191

Browse files
committed
Houdini Engine for Unreal - Version 2.0.2
Update 2 of the V2 Plugin. The plug-in is now linked to Houdini 18.5.596 / HAPI 3.6.2. Documentation for version 2.0 of the plug-in is available: https://www.sidefx.com/docs/unreal/ ------------------------------------------------------- New features: ------------------------------------------------------- - Added support for Landscape Edit Layers: Output: Heightfield data can now be assigned to landscape edit layers on existing landscapes. Those landscapes need to have the "Enable Edit Layers" option enabled. The "unreal_landscape_editlayer_landscape" prim string attribute can be used to set the name of the target landscape actor. The "unreal_landscape_editlayer_name" prim string attribute can be used to set the name of the targeted edit layer. Finally, the "unreal_landscape_editlayer_after" primitive string attributes can be used to specify the name of another layer that the target layer should be created after. Input: If a Landscape with edit layers is used as input, the (final) heightfield and paint layers are still imported as usual, but we will also create heightfields of the individual edit layers using the layer names, prefixed by landscapelayer_. The imported layers will be hidden by default, and the visibility SOP is needed to unhide them. - Added a public API to the plugin: The API supports instantiating HDAs, setting parameters and inputs, cooking, inspecting and baking outputs of instantiated HDAs via C++, Blueprints and Python. The API has an example Editor Utility Widget and example Python scripts in the plugin's "Content/Examples" folder. Documentation for the public API will be added soon to the plugin's documentation. ------------------------------------------------------- Bug fixes: ------------------------------------------------------- - Fixed a regression that caused generic property attributes to not always be properly applied to generated Static Mesh Components. (specifically when using RawMeshes). - Fixed "unreal_uproperty_CollisionProfileName" not being properly applied in some cases. - Fixed crash when using the bake button in the output section. - Generic property attributes are now applied on spline outputs. - Textures from Houdini Materials are now created only if the corresponding "use" parameter isn't disabled. - Reduced the number of HAPI calls when translating Houdini materials. - Optimized mesh creation. (removed redundant calls to Physics/NavCollisions functions that are already part of the StaticMesh::Build function) - Mesh creation: removed unnecessary attempt to load an unreal material with an invalid path. - Fixed World inputs creating invalid input nodes with "Import as Reference" enabled. - Fixed "Import as Reference" not triggering input updates when disabled. - Fixed issues with multiparm/ramps sync if a HDA's parameter interface had changed. This could cause mix-ups in the multiparms children parameters, and even crashes due to a recursive loop after load/rebuild. - Fixed missing output detail UI when an output's node is marked as editable but is not an editable curve. - Fixed potential crash caused by the plugin attempting to update rendering while loading a level. - Fixed an issue that caused OBJ HDAs with nested OBJ_geo subnets to not display/use the main OBJ geo. This caused some HDAs that used vellum solvers/DOP networks to not properly output their results. - Float Vector3 parameters now have spin buttons on the XYZ values. - Fixed regression causing mesh sockets to be ignored when they were on a separate part. This was likely to happen when using socket groups on "floating" points (point that are not used by a mesh). - Fixed generated mesh sockets displaying the default "Not found" mesh when they had no actor assigned. We only display the default mesh when we failed to attach or spawn the assigned Actor. - The output UI for Proxy meshes now display the number of sockets found in the output. - Fixed issue where landscape updates wouldn't immediately update viewport after cooking. - Fixed material assignments for Meshes with LODs (when using RawMesh) Before, all LODs used the first material. - Fixed materials being mixed up when using multiple point instancers with the "unreal_split_attr" attributes. Only the first instancer had the proper materials. - Fixed multiple issues when importing one/multiple landscape proxies via a world/landscape input: - The paint layers data were not properly converted to HF masks when converting the landscape to Heightfield. - Fixed landscape proxies having an incorrect transform in Houdini. - Fixed multiple landscapes not being imported properly with World Inputs.
1 parent 8a254df commit 7f65191

File tree

254 files changed

+143981
-130222
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

254 files changed

+143981
-130222
lines changed
776 KB
Binary file not shown.
1.46 MB
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
""" An example script that uses the API to instantiate two HDAs. The first HDA
2+
will be used as an input to the second HDA. For the second HDA we set 2 inputs:
3+
an asset input (the first instantiated HDA) and a curve input (a helix). The
4+
inputs are set during post instantiation (before the first cook). After the
5+
first cook and output creation (post processing) the input structure is fetched
6+
and logged.
7+
8+
"""
9+
import math
10+
11+
import unreal
12+
13+
_g_wrapper1 = None
14+
_g_wrapper2 = None
15+
16+
17+
def get_copy_curve_hda_path():
18+
return '/HoudiniEngine/Examples/hda/copy_to_curve_1_0.copy_to_curve_1_0'
19+
20+
21+
def get_copy_curve_hda():
22+
return unreal.load_object(None, get_copy_curve_hda_path())
23+
24+
25+
def get_pig_head_hda_path():
26+
return '/HoudiniEngine/Examples/hda/pig_head_subdivider_v01.pig_head_subdivider_v01'
27+
28+
29+
def get_pig_head_hda():
30+
return unreal.load_object(None, get_pig_head_hda_path())
31+
32+
33+
def configure_inputs(in_wrapper):
34+
print('configure_inputs')
35+
36+
# Unbind from the delegate
37+
in_wrapper.on_post_instantiation_delegate.remove_callable(configure_inputs)
38+
39+
# Create a geo input
40+
asset_input = in_wrapper.create_empty_input(unreal.HoudiniPublicAPIAssetInput)
41+
# Set the input objects/assets for this input
42+
# asset_input.set_input_objects((_g_wrapper1.get_houdini_asset_actor().houdini_asset_component, ))
43+
asset_input.set_input_objects((_g_wrapper1, ))
44+
# copy the input data to the HDA as node input 0
45+
in_wrapper.set_input_at_index(0, asset_input)
46+
# We can now discard the API input object
47+
asset_input = None
48+
49+
# Create a curve input
50+
curve_input = in_wrapper.create_empty_input(unreal.HoudiniPublicAPICurveInput)
51+
# Create a curve wrapper/helper
52+
curve_object = unreal.HoudiniPublicAPICurveInputObject(curve_input)
53+
# Make it a Nurbs curve
54+
curve_object.set_curve_type(unreal.HoudiniPublicAPICurveType.NURBS)
55+
# Set the points of the curve, for this example we create a helix
56+
# consisting of 100 points
57+
curve_points = []
58+
for i in range(10):
59+
t = i / 10.0 * math.pi * 2.0
60+
x = 100.0 * math.cos(t)
61+
y = 100.0 * math.sin(t)
62+
z = i * 10.0
63+
curve_points.append(unreal.Transform([x, y, z], [0, 0, 0], [1, 1, 1]))
64+
curve_object.set_curve_points(curve_points)
65+
# Set the curve wrapper as an input object
66+
curve_input.set_input_objects((curve_object, ))
67+
# Copy the input data to the HDA as node input 1
68+
in_wrapper.set_input_at_index(1, curve_input)
69+
# We can now discard the API input object
70+
curve_input = None
71+
72+
73+
def print_api_input(in_input):
74+
print('\t\tInput type: {0}'.format(in_input.__class__))
75+
print('\t\tbKeepWorldTransform: {0}'.format(in_input.keep_world_transform))
76+
print('\t\tbImportAsReference: {0}'.format(in_input.import_as_reference))
77+
if isinstance(in_input, unreal.HoudiniPublicAPICurveInput):
78+
print('\t\tbCookOnCurveChanged: {0}'.format(in_input.cook_on_curve_changed))
79+
print('\t\tbAddRotAndScaleAttributesOnCurves: {0}'.format(in_input.add_rot_and_scale_attributes_on_curves))
80+
81+
input_objects = in_input.get_input_objects()
82+
if not input_objects:
83+
print('\t\tEmpty input!')
84+
else:
85+
print('\t\tNumber of objects in input: {0}'.format(len(input_objects)))
86+
for idx, input_object in enumerate(input_objects):
87+
print('\t\t\tInput object #{0}: {1}'.format(idx, input_object))
88+
if hasattr(in_input, 'get_object_transform_offset'):
89+
print('\t\t\tObject Transform Offset: {0}'.format(in_input.get_object_transform_offset(input_object)))
90+
if isinstance(input_object, unreal.HoudiniPublicAPICurveInputObject):
91+
print('\t\t\tbClosed: {0}'.format(input_object.is_closed()))
92+
print('\t\t\tCurveMethod: {0}'.format(input_object.get_curve_method()))
93+
print('\t\t\tCurveType: {0}'.format(input_object.get_curve_type()))
94+
print('\t\t\tReversed: {0}'.format(input_object.is_reversed()))
95+
print('\t\t\tCurvePoints: {0}'.format(input_object.get_curve_points()))
96+
97+
98+
def print_inputs(in_wrapper):
99+
print('print_inputs')
100+
101+
# Unbind from the delegate
102+
in_wrapper.on_post_processing_delegate.remove_callable(print_inputs)
103+
104+
# Fetch inputs, iterate over it and log
105+
node_inputs = in_wrapper.get_inputs_at_indices()
106+
parm_inputs = in_wrapper.get_input_parameters()
107+
108+
if not node_inputs:
109+
print('No node inputs found!')
110+
else:
111+
print('Number of node inputs: {0}'.format(len(node_inputs)))
112+
for input_index, input_wrapper in node_inputs.items():
113+
print('\tInput index: {0}'.format(input_index))
114+
print_api_input(input_wrapper)
115+
116+
if not parm_inputs:
117+
print('No parameter inputs found!')
118+
else:
119+
print('Number of parameter inputs: {0}'.format(len(parm_inputs)))
120+
for parm_name, input_wrapper in parm_inputs.items():
121+
print('\tInput parameter name: {0}'.format(parm_name))
122+
print_api_input(input_wrapper)
123+
124+
125+
def run():
126+
# get the API singleton
127+
api = unreal.HoudiniPublicAPIBlueprintLib.get_api()
128+
129+
global _g_wrapper1, _g_wrapper2
130+
# instantiate the input HDA with auto-cook enabled
131+
_g_wrapper1 = api.instantiate_asset(get_pig_head_hda(), unreal.Transform())
132+
133+
# instantiate the copy curve HDA
134+
_g_wrapper2 = api.instantiate_asset(get_copy_curve_hda(), unreal.Transform())
135+
136+
# Configure inputs on_post_instantiation, after instantiation, but before first cook
137+
_g_wrapper2.on_post_instantiation_delegate.add_callable(configure_inputs)
138+
# Print the input state after the cook and output creation.
139+
_g_wrapper2.on_post_processing_delegate.add_callable(print_inputs)
140+
141+
142+
if __name__ == '__main__':
143+
run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import unreal
2+
3+
""" Example script for instantiating an asset, cooking it and baking all of
4+
its outputs.
5+
6+
"""
7+
8+
_g_wrapper = None
9+
10+
11+
def get_test_hda_path():
12+
return '/HoudiniEngine/Examples/hda/pig_head_subdivider_v01.pig_head_subdivider_v01'
13+
14+
15+
def get_test_hda():
16+
return unreal.load_object(None, get_test_hda_path())
17+
18+
19+
def on_post_instantiation(in_wrapper):
20+
print('on_post_instantiation')
21+
# in_wrapper.on_post_instantiation_state_exited_delegate_delegate.remove_callable(on_post_instantiation)
22+
23+
# Set parameter values for the next cook
24+
# in_wrapper.set_bool_parameter_value('add_instances', True)
25+
# in_wrapper.set_int_parameter_value('num_instances', 8)
26+
in_wrapper.set_parameter_tuples({
27+
'add_instances': unreal.HoudiniParameterTuple(bool_values=(True, )),
28+
'num_instances': unreal.HoudiniParameterTuple(int32_values=(8, )),
29+
})
30+
31+
# Print all parameter values
32+
param_tuples = in_wrapper.get_parameter_tuples()
33+
print('parameter tuples: {}'.format(len(param_tuples) if param_tuples else 0))
34+
if param_tuples:
35+
for param_tuple_name, param_tuple in param_tuples.items():
36+
print('parameter tuple name: {}'.format(param_tuple_name))
37+
print('\tbool_values: {}'.format(param_tuple.bool_values))
38+
print('\tfloat_values: {}'.format(param_tuple.float_values))
39+
print('\tint32_values: {}'.format(param_tuple.int32_values))
40+
print('\tstring_values: {}'.format(param_tuple.string_values))
41+
42+
# Force a cook/recook
43+
in_wrapper.recook()
44+
45+
46+
def on_post_bake(in_wrapper, success):
47+
in_wrapper.on_post_bake_delegate.remove_callable(on_post_bake)
48+
print('bake complete ... {}'.format('success' if success else 'failed'))
49+
50+
# Delete the hda after the bake
51+
in_wrapper.delete_instantiated_asset()
52+
global _g_wrapper
53+
_g_wrapper = None
54+
55+
56+
def on_post_process(in_wrapper):
57+
print('on_post_process')
58+
59+
# in_wrapper.on_post_processing_delegate.remove_callable(on_post_process)
60+
61+
# Print out all outputs generated by the HDA
62+
num_outputs = in_wrapper.get_num_outputs()
63+
print('num_outputs: {}'.format(num_outputs))
64+
if num_outputs > 0:
65+
for output_idx in range(num_outputs):
66+
identifiers = in_wrapper.get_output_identifiers_at(output_idx)
67+
print('\toutput index: {}'.format(output_idx))
68+
print('\toutput type: {}'.format(in_wrapper.get_output_type_at(output_idx)))
69+
print('\tnum_output_objects: {}'.format(len(identifiers)))
70+
if identifiers:
71+
for identifier in identifiers:
72+
output_object = in_wrapper.get_output_object_at(output_idx, identifier)
73+
output_component = in_wrapper.get_output_component_at(output_idx, identifier)
74+
is_proxy = in_wrapper.is_output_current_proxy_at(output_idx, identifier)
75+
print('\t\tidentifier: {}'.format(identifier))
76+
print('\t\toutput_object: {}'.format(output_object.get_name() if output_object else 'None'))
77+
print('\t\toutput_component: {}'.format(output_component.get_name() if output_component else 'None'))
78+
print('\t\tis_proxy: {}'.format(is_proxy))
79+
print('')
80+
81+
# bind to the post bake delegate
82+
in_wrapper.on_post_bake_delegate.add_callable(on_post_bake)
83+
84+
# Bake all outputs to actors
85+
print('baking all outputs to actors')
86+
in_wrapper.bake_all_outputs_with_settings(
87+
unreal.HoudiniEngineBakeOption.TO_ACTOR,
88+
replace_previous_bake=False,
89+
remove_temp_outputs_on_success=False)
90+
91+
92+
def run():
93+
# get the API singleton
94+
api = unreal.HoudiniPublicAPIBlueprintLib.get_api()
95+
96+
global _g_wrapper
97+
98+
# instantiate an asset, disabling auto-cook of the asset (so we have to
99+
# call wrapper.reCook() to cook it)
100+
_g_wrapper = api.instantiate_asset(get_test_hda(), unreal.Transform(), enable_auto_cook=False)
101+
102+
# Bind to the on post instantiation delegate (before the first cook)
103+
_g_wrapper.on_post_instantiation_delegate.add_callable(on_post_instantiation)
104+
# Bind to the on post processing delegate (after a cook and after all
105+
# outputs have been generated in Unreal)
106+
_g_wrapper.on_post_processing_delegate.add_callable(on_post_process)
107+
108+
109+
if __name__ == '__main__':
110+
run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import unreal
2+
3+
""" Example script for instantiating an asset, cooking it and baking an
4+
individual output object.
5+
6+
"""
7+
8+
_g_wrapper = None
9+
10+
11+
def get_test_hda_path():
12+
return '/HoudiniEngine/Examples/hda/pig_head_subdivider_v01.pig_head_subdivider_v01'
13+
14+
15+
def get_test_hda():
16+
return unreal.load_object(None, get_test_hda_path())
17+
18+
19+
def on_post_process(in_wrapper):
20+
print('on_post_process')
21+
# Print details about the outputs and record the first static mesh we find
22+
23+
sm_index = None
24+
sm_identifier = None
25+
26+
# in_wrapper.on_post_processing_delegate.remove_callable(on_post_process)
27+
28+
num_outputs = in_wrapper.get_num_outputs()
29+
print('num_outputs: {}'.format(num_outputs))
30+
if num_outputs > 0:
31+
for output_idx in range(num_outputs):
32+
identifiers = in_wrapper.get_output_identifiers_at(output_idx)
33+
output_type = in_wrapper.get_output_type_at(output_idx)
34+
print('\toutput index: {}'.format(output_idx))
35+
print('\toutput type: {}'.format(output_type))
36+
print('\tnum_output_objects: {}'.format(len(identifiers)))
37+
if identifiers:
38+
for identifier in identifiers:
39+
output_object = in_wrapper.get_output_object_at(output_idx, identifier)
40+
output_component = in_wrapper.get_output_component_at(output_idx, identifier)
41+
is_proxy = in_wrapper.is_output_current_proxy_at(output_idx, identifier)
42+
print('\t\tidentifier: {}'.format(identifier))
43+
print('\t\toutput_object: {}'.format(output_object.get_name() if output_object else 'None'))
44+
print('\t\toutput_component: {}'.format(output_component.get_name() if output_component else 'None'))
45+
print('\t\tis_proxy: {}'.format(is_proxy))
46+
print('')
47+
48+
if (output_type == unreal.HoudiniOutputType.MESH and
49+
isinstance(output_object, unreal.StaticMesh)):
50+
sm_index = output_idx
51+
sm_identifier = identifier
52+
53+
# Bake the first static mesh we found to the CB
54+
if sm_index is not None and sm_identifier is not None:
55+
print('baking {}'.format(sm_identifier))
56+
success = in_wrapper.bake_output_object_at(sm_index, sm_identifier)
57+
print('success' if success else 'failed')
58+
59+
# Delete the instantiated asset
60+
in_wrapper.delete_instantiated_asset()
61+
global _g_wrapper
62+
_g_wrapper = None
63+
64+
65+
def run():
66+
# get the API singleton
67+
api = unreal.HoudiniPublicAPIBlueprintLib.get_api()
68+
69+
global _g_wrapper
70+
# instantiate an asset with auto-cook enabled
71+
_g_wrapper = api.instantiate_asset(get_test_hda(), unreal.Transform())
72+
73+
# Bind to the on post processing delegate (after a cook and after all
74+
# outputs have been generated in Unreal)
75+
_g_wrapper.on_post_processing_delegate.add_callable(on_post_process)
76+
77+
78+
if __name__ == '__main__':
79+
run()

0 commit comments

Comments
 (0)