Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Density-function nodes

Verified against Minecraft 26.2 · Reference · the thirty-four node types a worldgen/density_function file may name, what each one takes, and what the per-chunk rewrite turns it into.

Density functions is the lecture: three forms of one graph, two rewrites, and the six caches. This is the catalogue behind it — the table you would pause the video to read.

DensityFunctions.bootstrap registers every entry below into BuiltInRegistries.DENSITY_FUNCTION_TYPE, in this order, under the minecraft namespace. That registry is built-in and frozen at startup, which is why adding a new kind of node takes code while adding a new graph takes a JSON file.

34 — registered node types (DensityFunctions.bootstrap): four by name, then six markers, nine more by name, seven mapped transforms, four arithmetic, and four last.

The table

children counts the density-function slots; each one accepts an id string, an inline object or a bare number, because every child slot is typed DensityFunction.CODEC.

idclasschildrenother fieldswhat it computes
blend_alphaDensityFunctions.BlendAlpha0constant 1.0 as data — a placeholder the chunk swaps out
blend_offsetDensityFunctions.BlendOffset0constant 0.0 as data — likewise a placeholder
beardifierDensityFunctions.BeardifierMarker0constant 0.0 as data — the structure-terrain placeholder
old_blended_noiseBlendedNoise0xz_scale, y_scale, xz_factor, y_factor, smear_scale_multiplierthe pre-1.18 terrain noise, decoded unseeded
interpolatedDensityFunctions.Marker1delegates — requests cell-corner interpolation
flat_cacheDensityFunctions.Marker1delegates — requests a quart-resolution 2-D cache
cache_2dDensityFunctions.Marker1delegates — requests a one-entry XZ memo
cache_onceDensityFunctions.Marker1delegates — requests reuse within one interpolation step
cache_all_in_cellDensityFunctions.Marker1delegates — requests a whole-cell block cache
blend_densityDensityFunctions.Marker1delegates — requests old-terrain density blending
noiseDensityFunctions.Noise0noise, xz_scale, y_scalesamples a NormalNoise at the scaled position
end_islandsDensityFunctions.EndIslandDensityFunction0the End’s simplex island field, as a density
shifted_noiseDensityFunctions.ShiftedNoise3noise, xz_scale, y_scalesamples noise at position × scale plus three offsets
range_choiceDensityFunctions.RangeChoice3min_inclusive, max_exclusiveone of two branches, by whether the input is in range
interval_selectDensityFunctions.IntervalSelect1 + a listthresholdsthe branch whose ascending threshold the input first falls below
shift_aDensityFunctions.ShiftA0argument (a noise)domain warp read at x, 0, z
shift_bDensityFunctions.ShiftB0argument (a noise)domain warp read at z, x, 0
shiftDensityFunctions.Shift0argument (a noise)domain warp read at x, y, z
clampDensityFunctions.Clamp1min, maxthe child, clamped
absDensityFunctions.Mapped1absolute value
squareDensityFunctions.Mapped1the child squared
cubeDensityFunctions.Mapped1the child cubed
half_negativeDensityFunctions.Mapped1identity above zero, halved below
quarter_negativeDensityFunctions.Mapped1identity above zero, quartered below
invertDensityFunctions.Mapped1the reciprocal
squeezeDensityFunctions.Mapped1clamp to ±1, then a soft odd cubic
addDensityFunctions.Ap2 or DensityFunctions.MulOrAdd2the sum
mulDensityFunctions.Ap2 or DensityFunctions.MulOrAdd2the product, short-circuiting on an exact zero
minDensityFunctions.Ap22the minimum, skipping the second child when the first is already below its bound
maxDensityFunctions.Ap22the maximum, with the symmetric skip
splineDensityFunctions.Splineinside the splinesplinea CubicSpline whose coordinates are themselves density functions
constantDensityFunctions.Constant0argumenta fixed value
y_clamped_gradientDensityFunctions.YClampedGradient0from_y, to_y, from_value, to_valueblock Y mapped onto a value range
find_top_surfaceDensityFunctions.FindTopSurface2lower_bound, cell_heightsteps down in strides until the density goes positive, and returns that Y

Where one class serves several ids. The six markers are all DensityFunctions.Marker, a record of a DensityFunctions.Marker.Type and a wrapped function; the seven transforms are all DensityFunctions.Mapped; the four arithmetic ids share DensityFunctions.TwoArgumentSimpleFunction. In each case the enum constant carries its own codec, and the node’s codec() returns its type’s — which is how a re-serialised graph comes back with the right id. DensityFunctions.MulOrAdd is the specialisation DensityFunctions.TwoArgumentSimpleFunction.create picks when the id is add or mul and one argument folded to a DensityFunctions.Constant, so add in the JSON may come back as either class.

What the caches become

NoiseChunk.wrapNew is the per-chunk rewrite. A marker is a request; this is what is installed instead. All six replacements implement DensityFunctions.MarkerOrMarked, so they still report their marker type and would re-serialise unchanged.

marker typeinstalledkeyed on
DensityFunctions.Marker.Type.InterpolatedNoiseChunk.NoiseInterpolatornothing — two slices of cell-corner values, and eight corners loaded per cell. Serves a foreign context by delegating to the wrapped function; only a sample whose context is the NoiseChunk throws outside the loop
DensityFunctions.Marker.Type.FlatCacheNoiseChunk.FlatCacheposition, at quart resolution: one array entry per 4×4 block column group, filled at construction
DensityFunctions.Marker.Type.Cache2DNoiseChunk.Cache2Dposition, one entry — the packed XZ of the last sample
DensityFunctions.Marker.Type.CacheOnceNoiseChunk.CacheOncea counterNoiseChunk.interpolationCounter for the scalar, a second counter for the array form
DensityFunctions.Marker.Type.CacheAllInCellNoiseChunk.CacheAllInCellthe cell — one array entry per block in the cell, Y stored inverted
DensityFunctions.Marker.Type.BlendDensityNoiseChunk.BlendDensity, or nothing at all if the level’s Blender is empty, in which case the marker is replaced by its own childnot cached

The same rewrite resolves three singletons by object identity: DensityFunctions.BlendAlpha and DensityFunctions.BlendOffset become flat caches the NoiseChunk constructor has already filled (or survive as the constants 1.0 and 0.0 when there is no blending to do), and DensityFunctions.BeardifierMarker becomes this chunk’s Beardifier. And DensityFunctions.HolderHolder — the in-memory stand-in for an id reference, which is not registered and has no codec — is resolved to its value once instead of on every sample.

Bounds

Every node answers DensityFunction.minValue and DensityFunction.maxValue without a position. The arithmetic family — the two-argument nodes, the mapped ones and clamp — stores its bounds as record components filled once at construction, and so does BlendedNoise; a few answer with literals of their own and the rest delegate to their input or walk their list again on each call. The rules worth knowing:

The arithmetic bounds are sign-aware and eager: mul takes the four cross products and picks by the signs of the operands’ ends, and min and max take the element-wise minimum and maximum of the ends. Building a min or a max over two ranges that cannot overlap logs a warning and proceeds. DensityFunctions.Mapped.create transforms the child’s two endpoints, with abs and square clamping the minimum up to zero and invert reporting ±infinity whenever the child’s range straddles zero. clamp is the clearest of the nodes whose bounds are not derived from a child: its record components are literally named minValue and maxValue, so the codec’s min and max fields are the interface’s bound methods. shifted_noise takes its bounds from the noise and ignores all three of its children, and blend_density reports infinity whatever its child says.

Three nodes report bounds that are not densities or not final. DensityFunctions.Marker passes its child’s bounds through except when its type is DensityFunctions.Marker.Type.BlendDensity, where it reports ±infinity — the one place a marker is not transparent. DensityFunctions.HolderHolder reports ±infinity while its holder is unbound, which is what lets forward references parse. And DensityFunctions.FindTopSurface reports its lower bound and its upper bound’s maximum, which are Y coordinates — this node’s range is on a different scale from every other node in the table.

One more, on the unseeded graph: DensityFunction.NoiseHolder answers a maximum of 2.0 while its NormalNoise is still null. Every one of the sixty-three shipped noise definitions computes a maximum between 2.57 and 7.32 once seeded, so a freshly parsed router reports noise bounds that are too narrow, and seeding widens them.

What vanilla actually uses

Thirty-five JSON files ship under worldgen/density_function — four at the top level plus the per-dimension directories — and between them they use twenty-five of the thirty-four ids. Five more appear only inline, in the seven Registries.NOISE_SETTINGS files: blend_density and squeeze in all seven, and square, invert and find_top_surface in the three overworld variants.

That leaves four ids vanilla data never writes. constant is never written as a typed object, because a bare number is one. cache_all_in_cell and beardifier are added in code, by NoiseChunk’s constructor, around the router’s final density. And shift — the three-dimensional domain warp — is used by nothing: DensityFunctions.shift has no callers anywhere in the decompile, and no shipped file names the id. DensityFunctions.ShiftA and DensityFunctions.ShiftB cover the two two-dimensional warps vanilla wants.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.