extends SceneTree const MAP_SIZE := 512 const HALF_SIZE := MAP_SIZE / 2 const HEIGHT_SCALE := 40.0 const DATA_DIRECTORY := "res://terrain/jajce/data" const TEXTURE_DIRECTORY := "res://terrain/jajce/textures" func _initialize() -> void: call_deferred("_generate") func _generate() -> void: DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(TEXTURE_DIRECTORY)) _generate_texture("grass_albedo.png", Color("#516f36"), Color("#79924c"), 17) _generate_texture("soil_albedo.png", Color("#65513b"), Color("#92734d"), 29) _generate_texture("rock_albedo.png", Color("#55534f"), Color("#89847b"), 43) _generate_neutral_normal() var terrain := Terrain3D.new() terrain.region_size = 256 root.add_child(terrain) await process_frame var height_map := Image.create_empty(MAP_SIZE, MAP_SIZE, false, Image.FORMAT_RF) var noise := FastNoiseLite.new() noise.seed = 1943 noise.frequency = 0.018 noise.fractal_octaves = 4 noise.fractal_gain = 0.45 for image_z in MAP_SIZE: for image_x in MAP_SIZE: var world_x := float(image_x - HALF_SIZE) var world_z := float(image_z - HALF_SIZE) var distance := Vector2(world_x, world_z).length() var outside_play_area := smoothstep(20.0, 52.0, distance) var ridge := 28.0 * _elliptical_peak(world_x, world_z, -72.0, 18.0, 72.0, 48.0) var eastern_hill := 15.0 * _elliptical_peak(world_x, world_z, 105.0, 65.0, 110.0, 90.0) var southern_hill := ( 11.0 * _elliptical_peak(world_x, world_z, -35.0, -125.0, 130.0, 85.0) ) var river_trench := -5.0 * exp(-pow((world_x - 25.0) / 18.0, 2.0)) var broad_slope := maxf(0.0, (-world_x - 30.0) * 0.035) var detail := noise.get_noise_2d(world_x, world_z) * 2.4 var height := ( (ridge + eastern_hill + southern_hill + river_trench + broad_slope + detail) * outside_play_area ) height_map.set_pixel(image_x, image_z, Color(height / HEIGHT_SCALE, 0.0, 0.0, 1.0)) terrain.data.import_images( [height_map, null, null], Vector3(-HALF_SIZE, 0.0, -HALF_SIZE), 0.0, HEIGHT_SCALE ) terrain.data.save_directory(DATA_DIRECTORY) print("[TerrainGenerator] Generated bounded Jajce terrain and textures") quit(0) func _elliptical_peak( x: float, z: float, center_x: float, center_z: float, width: float, depth: float ) -> float: return exp(-(pow((x - center_x) / width, 2.0) + pow((z - center_z) / depth, 2.0))) func _generate_texture( file_name: String, dark_color: Color, light_color: Color, seed_value: int ) -> void: var noise := FastNoiseLite.new() noise.seed = seed_value noise.frequency = 0.035 noise.fractal_octaves = 3 var image := Image.create_empty(256, 256, false, Image.FORMAT_RGBA8) for y in image.get_height(): for x in image.get_width(): var value := noise.get_noise_2d(x, y) * 0.5 + 0.5 var color := dark_color.lerp(light_color, value) color.a = value image.set_pixel(x, y, color) image.generate_mipmaps() var path := TEXTURE_DIRECTORY.path_join(file_name) var error := image.save_png(path) if error != OK: push_error("Could not write terrain texture: " + path) func _generate_neutral_normal() -> void: var image := Image.create_empty(256, 256, false, Image.FORMAT_RGBA8) image.fill(Color(0.5, 0.5, 1.0, 0.86)) image.generate_mipmaps() var path := TEXTURE_DIRECTORY.path_join("neutral_normal.png") var error := image.save_png(path) if error != OK: push_error("Could not write terrain normal texture: " + path)