Randoloot/scripts/planets/surface_generator.gd
2026-10-18 08:43:54 +02:00

94 lines
4.4 KiB
GDScript

extends RefCounted
const LANDSCAPE := preload("res://scripts/planets/surface_landscape.gd")
const GRID := 96
const CELL := 40.0
const WORLD_SIZE := GRID * CELL
static func generate(seed_value: int, profile: Dictionary) -> Dictionary:
var rng := RandomNumberGenerator.new()
rng.seed = seed_value
var landscape := LANDSCAPE.choose(seed_value,profile)
var noise := FastNoiseLite.new()
noise.seed = seed_value
noise.frequency = 0.0008 + posmod(seed_value,8)*0.0001
noise.fractal_octaves = 4
var humidity := FastNoiseLite.new()
humidity.seed = seed_value ^ 0x464f5245
humidity.frequency = 0.003
var heights := PackedFloat32Array()
heights.resize(GRID * GRID)
for y in GRID:
for x in GRID:
var point := Vector2(x, y) * CELL - Vector2.ONE * WORLD_SIZE * 0.5
var h := clampf(0.5 + noise.get_noise_2d(point.x, point.y) * 0.6, 0.0, 1.0)
if int(landscape.index) == 2:
h = 0.35 + noise.get_noise_2d(point.x,point.y)*0.45
elif int(landscape.index) == 3:
h = 0.43 + absf(noise.get_noise_2d(point.x,point.y))*0.95
if point.length() < 200:
h = lerpf(0.5, h, smoothstep(110, 200, point.length()))
heights[y * GRID + x] = h
var trees: Array[Dictionary] = []
var mountains: Array[Dictionary] = []
var rocks: Array[Dictionary] = []
for y in range(2, GRID - 2, 2):
for x in range(2, GRID - 2, 2):
var point := Vector2(x, y) * CELL - Vector2.ONE * WORLD_SIZE * 0.5 + Vector2(rng.randf_range(-25, 25), rng.randf_range(-25, 25))
var h: float = heights[y * GRID + x]
if point.length() < 190:
continue
if h > 0.66 and rng.randf() < 0.65:
mountains.append({"position": point, "size": rng.randf_range(110, 210), "rotation": rng.randf_range(-PI, PI)})
elif h > 0.28 and h < 0.65 and humidity.get_noise_2d(point.x, point.y) > -0.12 and rng.randf() < float(landscape.density):
trees.append({"position": point, "size": rng.randf_range(52, 92), "rotation": rng.randf_range(-PI, PI), "shade": rng.randf_range(0.75, 1.1)})
for index in 180:
var point := Vector2(rng.randf_range(-1750,1750),rng.randf_range(-1750,1750))
var gx := clampi(int((point.x+1920)/CELL),0,GRID-1)
var gy := clampi(int((point.y+1920)/CELL),0,GRID-1)
if point.length() > 200 and heights[gy*GRID+gx] > 0.30:
rocks.append({"position":point,"size":rng.randf_range(14,45),"rotation":rng.randf_range(-PI,PI)})
var pois: Array[Dictionary] = []
for index in 8:
var kind: String = ["mine", "ruins", "deposit"][index % 3]
var chosen := Vector2.ZERO
var found := false
for attempt in 1500:
var x := rng.randi_range(5, GRID - 6)
var y := rng.randi_range(5, GRID - 6)
var h: float = heights[y * GRID + x]
var point := Vector2(x, y) * CELL - Vector2.ONE * WORLD_SIZE * 0.5
if h < 0.29 or h > 0.71 or point.length() < 400:
continue
var clear := true
for other in pois:
if point.distance_to(other.position) < 420:
clear = false
if clear:
chosen = point
found = true
break
if not found:
# A lander can cross every terrain; a remote fallback never traps it.
chosen = Vector2.from_angle(index * TAU / 8) * 1250
pois.append({"id": index, "kind": kind, "position": chosen, "stage": 0, "complete": false, "discovered": false})
# Clear vegetation around explorable structures so entrances stay readable.
for poi in pois:
trees = trees.filter(func(prop): return prop.position.distance_to(poi.position) > 100)
mountains = mountains.filter(func(prop): return prop.position.distance_to(poi.position) > 120)
rocks = rocks.filter(func(prop): return prop.position.distance_to(poi.position) > 65)
var data := {"seed":seed_value,"heights":heights,"height_texture":LANDSCAPE.height_texture(heights),"landscape":landscape,"rocks":rocks,"trees":trees,"mountains":mountains,"pois":pois,"profile":profile.duplicate(true)}
preload("res://scripts/planets/surface_scenery.gd").generate(data,rng)
preload("res://scripts/planets/surface_landmarks.gd").generate(data)
preload("res://scripts/planets/surface_details.gd").generate(data)
return data
static func terrain_texture(data: Dictionary) -> ImageTexture:
# Only the minimap is baked. The world uses full-resolution GPU terrain shading.
var image := Image.create(256,256,false,Image.FORMAT_RGB8)
for y in 256:
for x in 256:
var gx := clampi(int(float(x)/256*GRID),0,GRID-1)
var gy := clampi(int(float(y)/256*GRID),0,GRID-1)
image.set_pixel(x,y,LANDSCAPE.color_at(data.heights[gy*GRID+gx],0.0,data.landscape))
return ImageTexture.create_from_image(image)