Houdini to Unreal — Typed Placement Routing for HISM and Blueprint Actors

pavelzosim:~/atlas_SYS.ONLINE / UTC+3

01 Overview

My procedural maze generator eventually hit an architectural limit: tens of thousands of modular elements, with significant variation, concentrated in a relatively small area. Culling, streaming, World Partition, and HLOD are all valid optimization layers — but my first goal was to improve the underlying representation of the generated content, not to paper over it downstream.

Houdini Engine already supports native ISM/HISM output. My problem was slightly different.

02 The gap in native Houdini Engine output

For gameplay modules, I still needed independent Blueprint Actors — chests, doors, enemies, anything with its own logic. But structural modules (ground, walls, caps) did not need separate Actor-level logic at all. What I wanted was for Houdini points to target specific HISM components inside one Unreal-owned Blueprint container:

BP_StructuralContainer
├─ HISM_Ground_I
├─ HISM_Ground_L
├─ HISM_Wall_Straight
└─ HISM_Wall_Corner

Native Houdini Engine ISM/HISM output instantiates by asset path — it doesn't have a concept of "this point belongs on the third named component inside an artist-authored container." Getting there meant building a typed integration layer on top of the Houdini Engine public API, not just consuming its default output.

03 A typed placement contract

The HDA now assigns one of two representation modes per point:

Δ1 // Actor
Blueprint Path → independent Actor. Used for gameplay modules that need their own logic.
Δ2 // Instanced
Blueprint Container Path → Named HISM Component → Instance Transform. Used for structural modules where scale and editor performance matter.

Instead of telling Unreal which mesh to instantiate directly, Houdini sends a semantic assignment. Unreal remains responsible for the meshes, materials, collision settings, and component configuration — the HDA never reaches into that; it only says which named target a given transform belongs to.

The bridge reads named PCG outputs and typed point metadata from the cooked HDA, then routes every resolved assignment to either an Actor or a specific HISM component. That gives the procedural system two clean options: Actors where independent logic is required, HISM containers where scale and editor performance matter.

04 Houdini side: authoring the semantic assignment

On the Houdini asset, each structural module in the registry gets a structural_type, a topology_type, and — for the instanced path — a hism_component_name. The registry resolves those into the container path and component name the Unreal side will look for:

Houdini Data Registry panel showing the Instanced Mode module list with Container Blueprint Root and per-module HISM Component Name fields such as HISM_Window_One_A and HISM_Panel_B
Instanced-mode registry on the HDA — each module gets a Structural Type, a Topology Type, and the named HISM component it targets.
record = {
    "registry_index": registry_index,
    "representation_mode": "instanced",
    "structural_type": structural_type,
    "topology_type": topology_type,
    "container_blueprint_name": blueprint_name,
    "container_blueprint_path": _join_unreal_content_path(
        container_root,
        blueprint_name,
    ),
    "hism_component_name": hism_component_name,
    "static_mesh_name": static_mesh_name,
    "static_mesh_path": _join_unreal_content_path(
        mesh_root,
        static_mesh_name,
    ),
}

This record is Houdini's entire opinion on the matter: a container path and a component name, both strings. It never touches a mesh reference, a material, or a collision setting — those stay entirely on the Unreal side, authored once on the container Blueprint itself.

05 Unreal side: resolving named HISM components

The container Blueprint is an ordinary self-contained Actor with one HISM component per module variant, each given a meaningful component name:

Unreal Blueprint editor showing a container Actor's component tree with several named Hierarchical Instanced Static Mesh components, including HISM_Balcony_A, HISM_Entrance_A, HISM_Panel_A/B/C, and HISM_Window_One_A
Container Blueprint — one HISM component per module variant, named so the bridge can resolve them by string.

On the C++ side, resolving a hism_component_name against a spawned or reused container Actor is a plain component lookup by name, cast to the expected type:

UHierarchicalInstancedStaticMeshComponent* FindNamedHism(AActor* ContainerActor, const FString& ComponentName, FString& OutError)
{
    TInlineComponentArray Components;
    ContainerActor->GetComponents(Components);
    for (UActorComponent* Component : Components)
    {
        if (Component->GetName() == ComponentName)
        {
            if (UHierarchicalInstancedStaticMeshComponent* Hism = Cast(Component))
            {
                return Hism;
            }
            OutError = FString::Printf(TEXT("component '%s' is not a UHierarchicalInstancedStaticMeshComponent"), *ComponentName);
            return nullptr;
        }
    }
    OutError = FString::Printf(TEXT("named HISM component '%s' is missing"), *ComponentName);
    return nullptr;
}

A missing or misspelled hism_component_name fails loudly, per placement, with the exact component name in the message — never a silent no-op. Placements grouped under the same container path are batched by HISM name, so one container Actor with four named components receives four separate AddInstances calls instead of one undifferentiated mesh dump.

06 Before and after

Without this layer, the previous approach spawned one Blueprint Actor per structural instance. On a single generated level that meant thousands of individual, independently-ticking Actors in the Outliner for what is, structurally, repeated static geometry:

Unreal Outliner listing thousands of individual BP_Balcony_A, BP_Balcony_A1 through BP_Balcony_A67 Blueprint Actor instances generated by the earlier per-actor approach
Before — the earlier per-instance approach: 7,535 individual Actors for one generated level, each a separate Blueprint instance.

With the typed bridge, the same generation pass produces one reused container Actor per Blueprint path, with instances batched into the correct named HISM component:

Large procedurally generated multi-story level in the Unreal viewport after a successful Houdini Engine cook through the typed placement bridge
After — the same class of generation, routed through the typed bridge: structural volume without a matching Actor count.

07 Result

The maze generator was the reason I built this layer, but the architecture itself is not maze-specific. Any high-volume procedural tool that needs more control than direct asset instancing provides — and that has a mix of "this needs its own logic" and "this is just repeated geometry" — can sit on the same contract: a typed placement record, Houdini deciding domain and target, Unreal owning everything about how that target actually renders and behaves.

// END OF ARTICLE // HOUDINI_UNREAL_TYPED_PLACEMENT_ROUTING // EOF