Skip to content

Instantly share code, notes, and snippets.

@nitori
Last active January 26, 2026 05:30
Show Gist options
  • Select an option

  • Save nitori/89bbc17799f9e0ad7f7fdafe8135cbe6 to your computer and use it in GitHub Desktop.

Select an option

Save nitori/89bbc17799f9e0ad7f7fdafe8135cbe6 to your computer and use it in GitHub Desktop.
example python implementation of a hytale world gen v2 density graph (noise2d at base height), for my personal understanding.
from perlin_noise import PerlinNoise
from dataclasses import dataclass
@dataclass
class CurvePoint:
from_value: float
to_value: float
def manual_curve_maker(*points: CurvePoint):
pts = sorted(points, key=lambda p: p.from_value)
# chatgpt generated.
def manual_curve(value: float):
if value <= pts[0].from_value:
return pts[0].to_value
if value >= pts[-1].from_value:
return pts[-1].to_value
for a, b in zip(pts, pts[1:]):
if a.from_value <= value <= b.from_value:
t = (value - a.from_value) / (b.from_value - a.from_value)
return a.to_value + t * (b.to_value - a.to_value)
return pts[-1].to_value
return manual_curve
def noise2d_density_maker(
seed,
base_frequency=1.0,
base_octaves=1,
base_lacunarity=1.0,
base_persistence=1.0,
):
perlin = PerlinNoise(seed=seed)
def noise2d(
coords: tuple[float, ...] | list[float],
frequency=base_frequency,
octaves=base_octaves,
lacunarity=base_lacunarity,
persistence=base_persistence,
) -> float:
total = 0.0
total_amplitude = 0.0
amplitude = 1.0
for _ in range(octaves):
value = perlin.noise([coord * frequency for coord in coords])
total += value * amplitude
total_amplitude += amplitude
frequency *= lacunarity
amplitude *= persistence
return total / total_amplitude
def noise2d_density(x, _y, z):
return noise2d([x, z])
return noise2d_density
def base_height_density_maker(base):
def base_height_density(_x, y, _z):
return y - base
return base_height_density
def curve_mapper_density_maker(input_density, curve):
def curve_mapper_density(x, y, z):
return curve(input_density(x, y, z))
return curve_mapper_density
def sum_density_maker(*densities):
def sum_density(x, y, z):
total = 0.0
for density in densities:
total += density(x, y, z)
return total
return sum_density
def invert_density_maker(density):
def invert_density(x, y, z):
return density(x, y, z) * -1
return invert_density
def main():
"""
Rough density graph outline used in Hytale node editor:
SumDensity(
CurveMapperDensity(
SimplexNoise2DDensity(),
ManualCurve(
points=[
CurvePoint(-1, 0),
CurvePoint(1, 20)
]
)
),
InvertDensity(BaseHeightDensity(base=100))
)
"""
noise2d_density = noise2d_density_maker(1234, 1 / 8, 1, 2.0, 0.5)
# using base=90 here, to make the pattern more visible in the output.
base_height_density = base_height_density_maker(base=90)
# should be CurvePoint(-1, 0) and CurvePoint(1, 20)
# but this perlin noise implementation doesn't really reach anywhere close to -1 and 1
manual_curve = manual_curve_maker(CurvePoint(-0.6, 0), CurvePoint(0.6, 20))
curve_mapper_density = curve_mapper_density_maker(noise2d_density, manual_curve)
sum_density = sum_density_maker(
curve_mapper_density,
invert_density_maker(base_height_density)
)
offx = 0
offz = 0
width = 40
height = 20
# output a (x, z) grid of y-coordinates at which level the first air block is.
for z in range(offz, offz + height):
for x in range(offx, offx + width):
air_at = -1
for y in range(80, 120): # should be range(0, 320) but limited for "performance" here.
v = sum_density(x, y, z)
if v < 0.0:
air_at = y
break
print(f'{air_at:3d}', end=' ')
print()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment