PCG nodes
Reference for the six custom PCG nodes Agentic Biomes adds - pins, every setting with its default and range, exact behaviour, and which shipped graphs use each node.
PCG nodes
Agentic Biomes adds six nodes to the PCG graph editor. Five of them filter or reshape point data (height bands, curvature, cliff faces, fog). The sixth, Biome Sampling Domain, bounds the sampling area of a biome region. They live in the AgenticBiomes runtime module.
Open a PCG graph, right-click, and search for the node title. The palette category is the engine's name for the node type: Point Ops for the five point nodes and Spatial for Biome Sampling Domain.
| Node | Palette title | Category | Used by shipped graphs |
|---|---|---|---|
UPCGHeightGradientSettings | Height Gradient | Point Ops | SG_AT_EffectScatter, SG_S_HeightGradientCpp |
UPCGCurvatureFilterSettings | Curvature Filter | Point Ops | SG_AT_CurvatureScatter, SG_S_CurvatureFilter |
UPCGCliffPlaceSettings | Cliff Place | Point Ops | SG_AT_Cliffs_Simple, SG_Sampler_CliffWalls_LS, SG_Sampler_CliffWalls_Landscape |
UPCGCliffHeightSettings | Cliff Height | Point Ops | none |
UPCGFogPlacementSettings | Fog Placement | Point Ops | SG_AT_FogScatter |
UPCGBiomeSamplingDomainSettings | Biome Sampling Domain | Spatial | SG_AT_TerrainSurface |
The shipped graphs are in /AgenticBiomes/PCG/Generators/ and /AgenticBiomes/PCG/SubGraphs/.
In the tables below, the first name is the label Details shows (the engine humanizes the C++ name; for a bool, bUseFogPlacement shows as Use Fog Placement). The C++ name is in backticks. Every setting on the five point nodes can also be driven from a graph parameter through its override pin.
Behaviour shared by the point nodes
- Pins. Each point node has one input pin,
In, and one output pin,Out, both point data. Height Gradient, Cliff Height, Cliff Place and Fog Placement also accept spatial data that can be converted to points. Curvature Filter only accepts data that is already points. An input the node cannot use is skipped, and most nodes log an Error for it. - Empty input. An input with zero points is passed through unchanged by every node except Curvature Filter, which produces no output for it.
- Execution. Cliff Place and Fog Placement run on the main thread only. The other point nodes are not restricted to it.
- Deterministic thinning. Where a node thins points by probability, it compares the probability with a number derived from the point's world position: the X, Y and Z coordinates are rounded to whole centimetres, hashed, and reduced to 16 bits. The same point at the same position always gets the same answer, so a regeneration does not reshuffle the scatter. The values are quantised: positions less than a centimetre apart can land on the same number, and there are 65,536 levels.
- Terrain-only traces. Cliff Place and Fog Placement trace against the level's terrain, on either backend (Landscape or MeshTerrain), and ignore PCG-spawned scatter. Results do not depend on which generator ran first. When the terrain cannot be resolved, both log an Error with the reason and emit no points rather than pass unfiltered points on.
Height Gradient
Thins, scales or moves points according to how high they sit, relative to the other points in the same input.
How it works. The node finds the lowest and highest Z in the input and maps each point's Z to a height between 0 and 1. Points outside HeightLow to HeightHigh, plus the Falloff margin on each side, get density 0. Inside the band, density follows the chosen curve from DensityAtBase (at HeightLow) to DensityAtTop (at HeightHigh), and never drops below MinDensity. In the two falloff margins, density is DensityAtBase or DensityAtTop multiplied by a smoothstep ramp that reaches 0 at the outer edge. MinDensity does not apply in the margins.
With bRemoveZeroDensity on, points with density 0 or less are removed, and points with density below 1 are kept with a probability equal to their density (deterministic, see above). Surviving points get that density, limited to 0 to 1, written to $Density.
Heights are normalised over the input the node receives, not over the whole map. In a partitioned or tiled graph, each input set has its own lowest and highest Z, so the same terrain can be thinned differently per tile. If every point has the same Z, the input passes through unchanged. With the defaults (HeightLow 0, HeightHigh 1) the band spans the whole input, so Falloff has no visible effect until you narrow the band.
Settings
| Setting | Type | Default | Range | Meaning |
|---|---|---|---|---|
Use Height Gradient bUseHeightGradient | bool | true | Master switch. When off, all points pass through unchanged. | |
Height Low HeightLow | float | 0 | 0 to 1 | Bottom of the band. |
Height High HeightHigh | float | 1 | 0 to 1 | Top of the band. If it is not greater than HeightLow, the node logs a Warning and passes the input through unchanged. |
Curve Type CurveType | enum | Linear | Shape of the gradient inside the band. See the curve table. | |
Density At Base DensityAtBase | float | 0 | Density at HeightLow. | |
Density At Top DensityAtTop | float | 1 | Density at HeightHigh. | |
Falloff Falloff | float | 0.2 | 0 to 0.5 | Width of the smoothstep margin outside the band, in normalised height. 0 gives a hard cutoff. |
Min Density MinDensity | float | 0.1 | 0 to 1 | Floor for density inside the band. |
Invert bInvert | bool | false | Reverses the gradient direction within the band. | |
Terrace Count TerraceCount | int | 4 | 2 to 16 | Number of shelves. Only editable when Curve Type is Terrace. |
Terrace Sharpness TerraceSharpness | float | 1.0 | 0.5 to 8.0 | Narrowness of each shelf peak. Only editable when Curve Type is Terrace. |
Remove Zero Density bRemoveZeroDensity | bool | true | Remove points with density 0 or less, and thin the rest by density. When off, no point is removed by this node and points outside the band keep density 0. | |
Apply Scale bApplyScale | bool | false | Multiply each surviving point's scale by a height-based factor. | |
Scale At Base ScaleAtBase | float | 1.0 | 0.01 to 5 | Scale factor at HeightLow. Needs Apply Scale. |
Scale At Top ScaleAtTop | float | 1.0 | 0.01 to 5 | Scale factor at HeightHigh. Needs Apply Scale. |
Apply Offset bApplyOffset | bool | false | Move each surviving point by a height-based vector. | |
Offset At Base OffsetAtBase | vector | 0, 0, 0 | Offset at HeightLow. Needs Apply Offset. | |
Offset At Top OffsetAtTop | vector | 0, 0, 0 | Offset at HeightHigh. Needs Apply Offset. |
Details groups these under Height Gradient, Height Scale and Height Offset. Scale and offset use the same curve as density, and inside the falloff margins they blend from no change (scale 1, offset zero) to the base or top value. With Invert on, the base and top values swap in the margins and the band parameter is flipped inside the band.
Curve types
T is the position inside the band, 0 at HeightLow and 1 at HeightHigh.
| Curve | Value | Effect |
|---|---|---|
| Linear | T | Even gradient. |
| EaseIn | T squared | Sparse at the base, concentrated toward the top. |
| EaseOut | 1 minus (1 minus T) squared | Dense at the base, thinning toward the top. |
| SCurve | smoothstep of T | Smooth transitions at both ends. |
| Exponential | T cubed | Very sparse at the base, steep toward the top. |
| Bell | 4 times T times (1 minus T) | Peak in the middle, sparse at the edges. |
| Terrace | Bell shape repeated TerraceCount times, raised to TerraceSharpness | Repeating shelves. Higher sharpness gives narrower shelves. |
Curvature Filter
Keeps points that sit where the terrain bends: hilltops, ridge lines, terraces. Use it to scatter things only on curved ground.
How it works. For each point the node traces down at the point and at four neighbours, north, south, east and west, each FeatureScale away. If any of the five traces misses, the point is dropped. Curvature is the absolute difference between the average neighbour height and the centre height, divided by FeatureScale, clamped to 0 to 1. A point is kept when its curvature is at least Threshold. With bWriteDensity on, the curvature is written to the point's density, so a later density filter can use it.
Settings
| Setting | Type | Default | Range | Meaning |
|---|---|---|---|---|
Feature Scale FeatureScale | float | 300 | 50 to 5000 | Distance in cm from the point to each neighbour sample. Small values pick up tight bends, large values broad hills. |
Threshold Threshold | float | 0.05 | 0 to 1 | Minimum curvature to keep a point. 0 keeps every point that has all five traces. |
Write Density bWriteDensity | bool | true | Write the curvature to point density. When off, density is left unchanged. |
All three are in the Curvature Filter category.
This node does not use the terrain-only trace. It casts a plain world line trace on the World Static channel with complex collision, from 50,000 cm above to 50,000 cm below the height of each sample. Anything with collision on that channel, including meshes spawned by other graphs that ran earlier, can be hit instead of the ground. It needs a world; without one it logs an Error and returns no output.
Cliff Place
Measures how tall the cliff face is at each point, drops points on cliffs outside a height range, and turns the survivors to face outward from the rock.
How it works. For each point the node traces the terrain from 1,000 cm above to 5,000 cm below to get the surface normal. Points with no terrain hit are dropped. It then walks along the horizontal direction of the normal in 200 cm steps, up to SearchRadius in each direction, tracing the terrain at each step. A walk stops early when the surface becomes nearly flat (normal Z above 0.95). The cliff height is the absolute Z difference between the highest and lowest point reached. Points shorter than MinCliffHeight or taller than MaxCliffHeight are dropped.
With bAlignToSurface on, each kept point is snapped to the terrain hit and rotated so its X axis points out of the cliff, with pitch set to 0. LocationOffset is applied in that cliff-face frame, then a random rotation between RotationMin and RotationMax is composed in the same frame, and finally YawOffset and RollOffset are added. The random rotation is seeded from the point's rounded position, so it is the same every run. Scale is kept. With bAlignToSurface off, kept points keep their original transform and only the height filter applies.
Settings
| Setting | Type | Default | Range | Meaning |
|---|---|---|---|---|
Search Radius SearchRadius | float | 2000 | at least 200 | Longest horizontal walk along the cliff face, in cm. Larger values measure taller cliffs but cost more traces. The tooltip suggests 1000 to 3000. |
Min Cliff Height MinCliffHeight | float | 0 | at least 0 | Shortest cliff to keep, in cm. |
Max Cliff Height MaxCliffHeight | float | 100000 | at least 0 | Tallest cliff to keep, in cm. |
Align To Surface bAlignToSurface | bool | true | Rotate points to the cliff face. | |
Rotation Min RotationMin | rotator | 0, 0, 0 | Lower bound of the random rotation, in degrees, in the cliff-face frame. | |
Rotation Max RotationMax | rotator | pitch 0, yaw 360, roll 0 | Upper bound of the random rotation. The default spins each point randomly around its yaw. | |
Yaw Offset YawOffset | float | 0 | Extra yaw after alignment, in degrees. | |
Roll Offset RollOffset | float | 0 | Extra roll after alignment, in degrees. | |
Location Offset LocationOffset | vector | 0, 0, 0 | Offset in the cliff-face frame, in cm. X runs along the outward normal, so a negative X pushes the point into the rock. Y runs along the face, Z is up. |
SearchRadius is in the category CliffPlace and the two height limits are in CliffPlace|Height Filter. Align To Surface and the five rotation and offset settings are all in CliffPlace|Rotation; those five are editable only when Align To Surface is on.
Failure behaviour
- If the terrain cannot be resolved, the node logs an Error and emits nothing. It fails closed on purpose, because passing the input on would scatter cliff-only meshes over flat ground.
- If the PCG context has no world, the node logs an Error and passes its input through unchanged.
Cliff Height
Filters points by the height of the cliff they belong to, without any traces.
How it works. The node builds a 3D spatial hash with cells SearchRadius wide. Two points are connected when they are within SearchRadius of each other in 3D, and connected points form one group (union-find). The group's cliff height is its highest Z minus its lowest Z, and every point in the group shares it. Points in a group whose height is below MinCliffHeight or above MaxCliffHeight are removed.
Use several graph instances with different height ranges to put small rocks on ledges and large rocks on big walls. The node only filters. It does not write the height to an attribute.
Settings
| Setting | Type | Default | Range | Meaning |
|---|---|---|---|---|
Search Radius SearchRadius | float | 2000 | at least 200 | Distance in cm within which two points are treated as the same cliff. |
Min Cliff Height MinCliffHeight | float | 0 | at least 0 | Groups shorter than this are removed. |
Max Cliff Height MaxCliffHeight | float | 100000 | at least 0 | Groups taller than this are removed. Set very high to disable. |
Search Radius is in the category Cliff Height, and the two height limits are in Cliff Height|Filter.
No shipped graph uses Cliff Height; Cliff Place does the same height filtering and adds rotation. The Details tooltip on SearchRadius talks about neighbours in an XY radius, but the node measures distance in 3D, so points on very different levels of the same wall can fall in different groups.
Fog Placement
Decides where fog cards go, from the shape of the terrain, the height, or both.
How it works. The node first finds the lowest and highest Z of the input and maps each point to a height from 0 to 1. Points outside HeightMin to HeightMax are dropped. For each remaining point it traces NumSamples neighbours spaced evenly on a circle of radius AnalysisRadius, and computes the topographic position index, TPI: the point's Z minus the average Z of the neighbours that were hit. At least NumSamples divided by 2 (rounded down) of the samples must hit terrain, otherwise the point is dropped (this removes points near the terrain edge). A negative TPI means the point sits in a bowl or valley, a positive one that it sits on a ridge.
Probability depends on the mode:
| Mode | Probability |
|---|---|
| Valley | 1 / (1 + exp(TPI * TPISteepness * 1000)). About 0.5 at TPI 0, approaching 1 for negative TPI. |
| Orographic | exp(-(h - CloudBaseHeight)^2 / (2 * CloudBandWidth^2)), where h is the normalised height. A bell around CloudBaseHeight. |
| Combined | The larger of the two. This is the default. |
Points with probability below MinProbability are removed. With bRemoveLowProbability on, the rest are thinned with the deterministic position hash, so a point with probability 0.5 survives about half the time. Survivors get the probability written to $Density. If WindStrength is above 0, each survivor is moved in X and Y along the normalised WindDirection by WindStrength times its probability.
With the default TPISteepness, the valley probability swings from near 1 to near 0 within a couple of centimetres of TPI either side of zero, so in practice it acts as a bowl-or-ridge test. Lower the steepness to widen the transition.
TPIThreshold is declared, shown in Details and logged, but the probability calculation does not read it. Changing it has no effect on the result. Use TPISteepness, MinProbability and the height band instead.
Settings
| Setting | Type | Default | Range | Meaning |
|---|---|---|---|---|
Use Fog Placement bUseFogPlacement | bool | true | Master switch. When off, all points pass through unchanged. | |
Mode Mode | enum | Combined | Valley, Orographic, Combined | How probability is computed. |
Analysis Radius AnalysisRadius | float | 5000 | at least 500 | Radius of the neighbour circle, in cm. Larger finds broader valleys. |
Num Samples NumSamples | int | 8 | 4 to 16 | Neighbour traces per point. |
TPI Threshold TPIThreshold | float | -500 | Not used by the calculation, see above. | |
TPI Steepness TPISteepness | float | 0.002 | 0.0001 to 0.01 | Steepness of the valley sigmoid. |
Cloud Base Height CloudBaseHeight | float | 0.6 | 0 to 1 | Centre of the orographic band, as a fraction of the input's height range. |
Cloud Band Width CloudBandWidth | float | 0.15 | 0.01 to 0.5 | Width of the orographic band, in the same units. |
Height Min HeightMin | float | 0 | 0 to 1 | Points below this normalised height are removed. |
Height Max HeightMax | float | 1 | 0 to 1 | Points above this normalised height are removed. |
Min Probability MinProbability | float | 0.3 | 0 to 1 | Points below this probability are removed. |
Remove Low Probability bRemoveLowProbability | bool | true | Thin points by probability, using the position hash. | |
Wind Direction WindDirection | vector 2D | 1, 0 | Direction fog is pushed, in the XY plane. Normalised internally. | |
Wind Strength WindStrength | float | 0 | at least 0 | Maximum displacement in cm. 0 turns the wind off. |
Trace Start Offset TraceStartOffset | float | 1000 | at least 0 | Height above each sample point at which its trace starts, in cm. |
Trace Distance TraceDistance | float | 15000 | at least 100 | Length of each downward trace, in cm. It must reach below the terrain. |
Landscape Only bLandscapeOnly | bool | true | Accept only terrain hits for the neighbour heights, on either backend. When off, any other world geometry counts too; PCG-spawned scatter is ignored either way. The name is kept so existing graphs keep working. |
Details groups these under the categories Fog Placement, Fog Placement|TPI, Fog Placement|Orographic, Fog Placement|Filtering, Fog Placement|Wind and Fog Placement|Trace. Neighbour traces use the Visibility channel.
Failure behaviour
- With Use Fog Placement off, or with an input whose points all share one Z, the input passes through unchanged.
- If the PCG context has no world, the node logs an Error and passes its input through unchanged.
- If the terrain cannot be resolved, the node logs an Error and emits nothing.
Biome Sampling Domain
Gives a biome region's PCG graph a stable footprint and a bounded sampling volume. Biome Build stamps each region with a sampling domain and a proven scatter support margin, and this node reads them. You normally meet it inside the shipped subgraph SG_AT_TerrainSurface rather than adding it yourself.
Pins
| Pin | Direction | Type | Notes |
|---|---|---|---|
In | input | Spline | Required. Exactly one spline: the footprint rectangle. |
Out | output | Spline | The footprint, rebuilt to the stamped domain when the region has one. |
Bounds | output | Volume | The volume native sampling should stay inside. |
The node has no settings.
What it does
- It finds the original execution source. That must be a biome region (
AAgenticBiomeRegion) or a non-partitioned volume. - If the region has a finite
SamplingDomainBounds, it rewrites the four control points of the input spline to a rectangle centred on the domain, with the domain's extent. The corner order is (+X, +Y), (+X, -Y), (-X, -Y), (-X, +Y). - It builds the
Boundsvolume. If the region has a sampling domain, its scatter support is resolved (bScatterSupportResolved) and its marginScatterSupportMarginCmis zero or more, the volume is the execution bounds expanded by that margin. Otherwise the volume is the bounds of the (possibly rebuilt) spline.
The region's SamplingDomainBounds, bScatterSupportResolved, ScatterSupportMarginCm and ScatterSupportSource are read-only values under the category Biome|Provenance in the region's Details.
Input rules
The input must be a closed spline of exactly four linear points forming a rectangle in the XY plane: finite values, equal opposite edges, perpendicular edges, constant Z. For a stamped region the corners must also be in the order given above. The node does not reorder control points.
Refusals
The node is not cached and runs on the main thread. Each refusal below logs an Error and cancels the execution of the graph.
| Refusal | Meaning |
|---|---|
| Missing execution source | There is no valid execution source, or its original source cannot be resolved. |
| Unsupported source | The source is not a biome region or a non-partitioned volume, or it is a partitioned or local source without a stamped region. |
| Invalid domain | The region's sampling domain is not finite, or a partitioned source has none. Rebuild the biome map. |
| Invalid support | A partitioned source has no valid resolved scatter support margin, or a region marked as resolved has a negative or non-finite margin. Rebuild, or use map-wide non-partitioned generation. |
| Wrong input | In does not carry exactly one spline. |
| Bad footprint | The spline is not the rectangle described above, or the corner order is wrong for a stamped region. |
| Bad metadata keys | The footprint's spline metadata keys do not match its control points. |
| Bad execution bounds | The support margin is used but the execution source has no finite bounds. |
| Bad sampling bounds | The resulting volume is not finite, or has zero height. |
Limits
- Curvature Filter, Cliff Place and Fog Placement trace the world, so they cost traces per point (one to five for Curvature Filter, up to
NumSamplesfor Fog Placement, and a walk of traces for Cliff Place). Cliff Height uses no traces. The plugin source contains no measured timings, and none are given here. - Height Gradient and Fog Placement normalise height over the points they receive. Feed them whole sets if you want results that do not vary with tile boundaries.
Need help? Join the Insodimension Discord. Related pages: Terraformer, Biome asset, Runtime ground cover.
Biome Asset Reference
Every editable field of the Agentic Biome data asset (UAgenticBiome) and the structs, enums and generator asset it nests, with types, defaults and backend notes
Console variables
The three agenticbiomes.GcGuard console variables, the engine PCG and garbage-collection variables the plugin reads, and where the plugin's other settings live.