Loading Materials

Python script for batch loading Redshift materials in Houdini

About

This Python script automates the creation of Redshift materials in Houdini by scanning a folder of textures or subfolders of materials. It matches texture files to material parameters and builds Redshift material networks automatically.

Usage

To run this script, open Houdini and go to Windows > Python Shell. Then enter:

exec(open("%DIR_SCRIPTS%/material_setup.py").read())

Replace %DIR_SCRIPTS% with the folder path where you saved the script.

Script

import os
import hou
from tkinter import Tk, filedialog

def ask_texture_folder():
    """Open a dialog to select a folder containing textures or subfolders of materials."""
    root = Tk()
    root.withdraw()  # Hide the main tkinter window
    folder_selected = filedialog.askdirectory(title='Select Texture Folder')
    root.destroy()
    return folder_selected

def create_redshift_material(mat_name, tex_folder, matnet="/mat"):
    matnet_node = hou.node(matnet)
    if not matnet_node:
        raise RuntimeError(f"{matnet} node not found")

    # Create Redshift Material Builder subnet
    rs_builder = matnet_node.createNode("redshift_vopnet", mat_name + "_rsbuilder")

    # Create Redshift Material node inside builder
    rs_material = rs_builder.createNode("redshift::Material", "redshift_material")
    rs_material.moveToGoodPosition()

    # Map material parameters to Poliigon texture filename keywords
    maps = {
        "diffuse_color": ["col", "basecolor", "albedo"],
        "refl_roughness": ["gloss", "roughness", "rough"],
        "refl_metalness": ["metal", "metallic"],
        "bump_input": ["nrm", "normal"],
        "displacement_input": ["disp", "displacement", "height"],
        "refl_reflect_weight": ["refl", "reflection"],
        "diffuse_weight": ["ao", "occlusion"],
        "emission_color": ["emit", "emissive"],
    }

    def find_tex_file(keywords):
        for fname in os.listdir(tex_folder):
            lname = fname.lower()
            for kw in keywords:
                if kw in lname and lname.endswith((".png", ".jpg", ".jpeg", ".tif", ".exr")):
                    return os.path.join(tex_folder, fname)
        return None

    for param, keywords in maps.items():
        filepath = find_tex_file(keywords)
        if filepath:
            tex_node_name = param + "_tex"
            tex_node = rs_builder.createNode("redshift::TextureSampler", tex_node_name)
            tex_node.parm("tex0").set(filepath)
            tex_node.moveToGoodPosition()

            input_index = rs_material.inputIndex(param)
            if input_index >= 0:
                rs_material.setInput(input_index, tex_node)
            else:
                print(f"Warning: Material parameter '{param}' not found")

    rs_builder.layoutChildren()
    rs_builder.moveToGoodPosition()

    print(f"Created Redshift material '{rs_builder.name()}' with textures from {tex_folder}")
    return rs_builder

def create_redshift_materials_for_folder(folder, matnet="/mat"):
    """
    Creates Redshift materials for either:
    - All subfolders inside `folder` (each subfolder is a material), or
    - The folder itself if it has no subfolders (single material).
    """
    subfolders = [entry for entry in os.scandir(folder) if entry.is_dir()]
    if subfolders:
        # Multiple subfolders: create a material for each subfolder
        for entry in subfolders:
            matname = entry.name
            create_redshift_material(matname, entry.path, matnet)
    else:
        # No subfolders: treat the folder itself as a single material
        matname = os.path.basename(folder)
        create_redshift_material(matname, folder, matnet)

if __name__ == '__main__':
    parent_folder = ask_texture_folder()
    if not parent_folder:
        print("No folder selected. Exiting.")
    else:
        create_redshift_materials_for_folder(parent_folder)