Preview
Houdini
Three-tier asset pipeline: Sub-COPhda Main-COPhda SOPhda Wrapper.
Pipeline Breakdown
1. Innermost Layer: Sub-COP HDA (Python Line Drawing)
Recursively grows branching lines on the 2D buffer using line length as spawning weights.
Lines are rasterized using a custom Bresenham’s Line Algorithm.
Border hits trigger coordinate wrapping for tileable output; intersections terminate the trace to prevent overlaps.
# =============== pix_line_bresenham ===============
ix1, iy1 = int(round(x1)), int(round(y1))
ix2, iy2 = int(round(x2)), int(round(y2))
dx = abs(ix2 - ix1)
dy = -abs(iy2 - iy1)
sx = 1 if ix1 < ix2 else -1
sy = 1 if iy1 < iy2 else -1
err = dx + dy
i = 0
while i < 99999:
ix_actual, iy_actual = ix1 % w, iy1 % h
# Wrap canvas coordinates for tileable output
if not (0 <= ix1 < w and 0 <= iy1 < h) \
and (ix_actual == 0 or ix_actual == w-1 or iy_actual == 0 or iy_actual == h-1):
add(ids, startid)
if kwargs['tileable']:
startid = XYtoId(ix_actual, iy_actual)
add(ids, startid)
else:
stopflag = 1
break
# Stop tracing if we hit an existing panel line
if i != 0 and pixs[iy_actual * w + ix_actual] >= .999:
startid = XYtoId(ix_actual, iy_actual)
add(ids, startid)
stopflag = 2
break
startid = draw_pix(ix_actual, iy_actual)
if ix1 == ix2 and iy1 == iy2:
break
e2 = 2 * err
if e2 >= dy:
err += dy
ix1 += sx
if e2 <= dx:
err += dx
iy1 += sy
i += 1
# =============== pix_line_bresenham =============== 2. Intermediate Layer: Main-COP HDA (Details and coloring)
To convert the rasterized lines into clean, beveled plates without rounding off structural corners, the custom Dilate/Erode node implements Manhattan and Chebyshev distance metrics instead of standard Euclidean.
// Custom Distance Metrics for Hard Surface Bevels
float len2;
if (method == 0)
{
len2 = dx * dx + dy * dy; // Euclidean (Round bevels)
}
else if (method == 1)
{
len2 = abs(dx) + abs(dy); // Manhattan (45-degree bevels)
len2 *= len2;
}
else if (method == 2)
{
len2 = max(abs(dx), abs(dy)); // Chebyshev (90-degree sharp grid)
len2 *= len2;
} Supports custom falloff profiles (linear, smoothstep, or ramp lookup):
if (@falloff_type == 0)
falloff = 1 - falloff;
else if (@falloff_type == 1) //default
{
//smoothstep y = (1-x^2)^2
falloff = 1 - falloff*falloff;
falloff *= falloff;
}
else
falloff = @ramp.getAt(falloff); Panel segmentation is done via connectivity analysis.
Shuffling the island IDs is performed on the GPU using Kensler’s stateless CMJ permutation.
This avoids memory allocation and thread synchronization. See Stateless Permutation for details.
uint CMJPermute(uint i, uint l, uint p)
{
uint w = l - 1;
w |= w >> 1;
w |= w >> 2;
w |= w >> 4;
w |= w >> 8;
w |= w >> 16;
do
{
i ^= p;
i *= 0xe170893d;
i ^= p >> 16;
i ^= (i & w) >> 4;
i ^= p >> 8;
i *= 0x0929eb3f;
i ^= p >> 23;
i ^= (i & w) >> 1;
i *= 1 | p >> 27;
i *= 0x6935fa69;
i ^= (i & w) >> 11;
i *= 0x74dcb303;
i ^= (i & w) >> 2;
i *= 0x9e501cc3;
i ^= (i & w) >> 2;
i *= 0xc860a3df;
i &= w;
i ^= i >> 5;
}
while (i >= l);
return (i + p) % l;
} 3. Outermost Layer: SOP HDA (A Wrapper For Unreal)
The outermost SOP wrapper generates the grid mesh and handles the Nanite/displacement parameters.
Since Houdini Engine’s default viewport proxy meshes don’t support displacement previews, assigning overrides forces Unreal to cook full static meshes for real-time viewport feedback.
// Enable Nanite and set displacement height from HDA interface parameter
i@unreal_nanite_enabled = 1;
f@unreal_material_parameter_magnitude = `ch('../height')`;
f@unreal_material_parameter_center = 0;
// Force full mesh preview in Unreal viewport by disabling proxy static meshes
i@unreal_uproperty_bOverrideGlobalProxyStaticMeshSettings = 1;
i@unreal_uproperty_bEnableProxyStaticMeshOverride = 0;