Making this was a bit more complicated than I thought, but I have learned a lot from what I have made. A quick rundown of the current features:
Using Python, Houdini can be run without opening its GUI, making it faster and easier to use when you only need to run a single HDA to do one specific thing. I plan to add parameter support in the future, as it only runs the HDA's default values.
def execute(self, context):
files = context.scene.files_props
# Get the index of the currently selected file using the ids contained in the dropdown menu
index = [x['id'] for x in data_for_enum].index(files.selected_file_enum)
# Create a temporary directory
with TemporaryDirectory() as tmp_d:
temp_file_paths = ExportTempFiles(tmp_d, files.process_individually_toggle)
houdini_settings = context.scene.settings_props
# Format data into a JSON dictionary
# Gets the required python files to run Hython and the paths of selected meshes
data ={
"houdini_path" : houdini_settings.houdini_path,
"houdini_python_lib_path" : houdini_settings.houdini_python_lib_path,
"hda_path" : data_for_enum[index]['path'],
"mesh_path" : temp_file_paths
}
data_json = json.dumps(data, indent= 2)
python_exe = houdini_settings.python_exe_path
# Run python script using the selected python.exe (hython.exe) and use the formated data as an argument for the HoudiniUtils script to run Houdini
houdiniProcess = subprocess.run([python_exe, 'HoudiniUtils.py', data_json], capture_output=True, text=True)
print(houdiniProcess.stdout)
print(houdiniProcess.stderr)
#import the files back to blender
for file in temp_file_paths:
bpy.ops.wm.fbx_import(filepath=file)
return {'FINISHED'}How the code works:
HoudinUtils.py is a script made to handle Houdini with Python, using the data obtained from blender to run the HDA and exports it.
def ProcessMeshWithHDA(string_data):
data = json.loads(string_data)
#imports the hou module using the paths provided
hou = enableHouModule(houdini_path = data['houdini_path'], python_lib_path = data['houdini_python_lib_path'])
# Install the selected HDA and gets its label
hda_path = data['hda_path']
hou.hda.installFile(hda_path)
definition = hou.hda.definitionsInFile(hda_path)
hda_label = definition[0].nodeTypeName()
for mesh in data['mesh_path']:
obj = hou.node('/obj')
geo = obj.createNode('geo')
# Import the file
file = geo.createNode('file')
file.parm('file').set(mesh)
# Connect file to HDA
hda = geo.createNode(hda_label)
hda.setInput(0, file)
# Handle export
export = geo.createNode('rop_fbx')
export.setInput(0, hda)
export.parm('sopoutput').set(mesh)
export.parm('convertunits').set(True)
export.parm('execute').pressButton()How the code works:
A settings.json file is created and used to save and load previously created HDA items in blender. Everytime change is made, this file is updated. Example from the video above.
{
"houdini_path": "C:\\Program Files\\Side Effects Software\\Houdini 20.0.896\\",
"houdini_python_lib_path": "C:\\Program Files\\Side Effects Software\\Houdini 20.0.896\\houdini\\python3.9libs",
"python_exe_path": "C:\\Program Files\\Side Effects Software\\Houdini 20.0.896\\bin\\hython.exe",
"scripts_and_digital_assets": [
{
"id": "Points to Cube",
"name": "Points to Cube",
"description": "Points to Cube",
"path": "C:\\Lazy Tools\\Blender\\BlenderToHoudini\\sop_points_to_cube.hdalc"
},
{
"id": "Root On Object",
"name": "Root On Object",
"description": "Grows roots from the base of an object to the top",
"path": "C:\\Lazy Tools\\Blender\\HDA_Test\\sop_RootOnObject_alt.hdalc"
},
{
"id": "Random Scatter",
"name": "Random Scatter",
"description": "Scatters random objects on selected mesh",
"path": "C:\\Lazy Tools\\Blender\\HDA_Test\\sop_ScatterRandomObjects.hdalc"
}
]
}One problem that came up when running Houdini completely headless is changing the parameters. One solution is to load the HDA parameters inside of Blender and pass those paramaters into the HDA. Another solution is to open the meshes inside in houdini and have a shelf tool that exports all of those meshes, then tells Blender to import them.
I chose the latter because it would be slightly easier and allows further addition to the node tree if needed.
To allow communication between Blender and Houdini, I had to use sockets to create a server and client communication between the two applications. To help me create a good base for the system and learn more about sockets, I used this website guide: https://realpython.com/python-sockets. I found some people in forums reccomending this page to learn sockets and it was exactly what I needed.

The overall flow is like this:
Sections and explanations are not fully done. The project is still being worked on.