Last active
January 26, 2026 05:30
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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