You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
12 KiB
437 lines
12 KiB
extends Control
|
|
|
|
@export var world_width = 1024
|
|
@export var world_height = 512
|
|
|
|
@export var min_continent = 5
|
|
@export var max_continent = 5
|
|
@export var min_area = 0.5
|
|
@export var max_area = 2.0
|
|
@export var min_ratio = -1.0
|
|
@export var max_ratio = 1.0
|
|
@export var ocean_ratio = 0
|
|
@export var nbr_rect_position = 10000
|
|
@export var max_intersection_area = 1000.0
|
|
|
|
@export var mountain_frequency = 1.5 # higher number means less mountains
|
|
@export var world_elevation = 10
|
|
@export var details = 2
|
|
@export var coasts_thickness = 0.055
|
|
|
|
|
|
var heightmap: Array
|
|
var oceanmap: Array
|
|
var coastmap: Array
|
|
var slopemap: Array
|
|
|
|
var rivers: Array
|
|
|
|
var continents: Array
|
|
var nbr_continent
|
|
var list_continents = []
|
|
var list_rect_continents = []
|
|
|
|
var blocs: Array
|
|
var mountain_blocs: Array
|
|
|
|
var noise = FastNoiseLite.new()
|
|
|
|
func _ready():
|
|
Global.world.width = world_width
|
|
Global.world.height = world_height
|
|
|
|
noise.seed = randi()
|
|
noise.fractal_lacunarity = details
|
|
noise.frequency = 0.005
|
|
|
|
init_maps()
|
|
init_blocs()
|
|
create_continents()
|
|
# pangea()
|
|
for continent in continents:
|
|
fill_continent(continent)
|
|
|
|
set_oceans()
|
|
set_coasts()
|
|
|
|
# set_slope()
|
|
# for x in 50000:
|
|
# erode(Vector2i(randi_range(0, world_width), randi_range(0, world_height)))
|
|
|
|
fix_heightmap()
|
|
|
|
# set_rivers()
|
|
|
|
set_blocs()
|
|
|
|
Global.world.heightMap = heightmap
|
|
Global.world.blocs = blocs
|
|
|
|
get_tree().change_scene_to_file("scenes/Game.tscn")
|
|
|
|
func init_maps():
|
|
for x in world_width:
|
|
heightmap.append([])
|
|
oceanmap.append([])
|
|
coastmap.append([])
|
|
slopemap.append([])
|
|
for y in world_height:
|
|
heightmap[x].append([])
|
|
heightmap[x][y] = 0
|
|
|
|
oceanmap[x].append([])
|
|
oceanmap[x][y] = 0
|
|
|
|
coastmap[x].append([])
|
|
coastmap[x][y] = 0
|
|
|
|
slopemap[x].append([])
|
|
slopemap[x][y] = 0
|
|
|
|
func create_continents():
|
|
var america = Rect2i(Vector2i(0, 0), Vector2i(world_width / 4, world_height))
|
|
continents.append(america)
|
|
|
|
var antarctica = Rect2i(Vector2i(world_width / 4, world_height / 2), Vector2i(3 * world_width / 4, world_height / 2))
|
|
continents.append(antarctica)
|
|
|
|
var africa = Rect2i(Vector2i(world_width / 4, 3 * world_height / 8), Vector2i(3 * world_width / 8, 3 * world_height / 8))
|
|
continents.append(africa)
|
|
|
|
var europa = Rect2i(Vector2i(3 * world_width / 8, 10), Vector2i(world_width / 4, world_height / 2))
|
|
continents.append(europa)
|
|
|
|
var asia = Rect2i(Vector2i(4 * world_width / 9, world_height / 8), Vector2i(world_width / 2, 3 * world_height / 8))
|
|
continents.append(asia)
|
|
|
|
func pangea():
|
|
var pangea = Rect2i(Vector2i(0, 0), Vector2i(world_width, world_height))
|
|
continents.append(pangea)
|
|
|
|
# var rng = RandomNumberGenerator.new()
|
|
#
|
|
# var continents_area = world_width * world_height * (1.0 - ocean_ratio)
|
|
# var total_area = 0.0
|
|
# var list_other_continents = []
|
|
#
|
|
# rng.randomize()
|
|
#
|
|
# nbr_continent = rng.randi_range(min_continent,max_continent)
|
|
#
|
|
# for i in nbr_continent:
|
|
# var continent = {}
|
|
# continent["area"] = rng.randf_range(min_area,max_area)
|
|
# continent["ratio"] = exp(rng.randf_range(min_ratio,max_ratio))
|
|
# continent["width"] = 0.0
|
|
# continent["height"] = 0.0
|
|
# continent["x"] = 0.0
|
|
# continent["y"] = 0.0
|
|
# total_area += continent["area"]
|
|
# list_continents.append(continent)
|
|
#
|
|
# # print("Total area : %f\n" % [total_area])
|
|
#
|
|
# for continent in list_continents:
|
|
# continent["area"] = (continent["area"] * continents_area / total_area)
|
|
# continent["height"] = sqrt(continent["area"] / continent["ratio"])
|
|
# continent["width"] = continent["area"] / continent["height"]
|
|
# var rect_continent
|
|
# var selected_rect_continent
|
|
# var min_intersection_area = 99999999.99
|
|
#
|
|
# for i in nbr_rect_position:
|
|
# var intersection_area = 0.0
|
|
#
|
|
# var x = rng.randf_range(0.0,world_width - continent["width"])
|
|
# var y = rng.randf_range(0.0,world_height - continent["height"])
|
|
#
|
|
# rect_continent = Rect2(x, y, continent["width"], continent["height"])
|
|
#
|
|
# for other_continent in list_other_continents:
|
|
# var rect_other_continent = Rect2(other_continent["x"], other_continent["y"], other_continent["width"], other_continent["height"])
|
|
# if rect_continent.intersects(rect_other_continent):
|
|
# # print("Ok intersection\n")
|
|
# intersection_area += rect_continent.intersection(rect_other_continent).get_area()
|
|
#
|
|
# if intersection_area < min_intersection_area:
|
|
# min_intersection_area = intersection_area
|
|
# continent["x"] = x
|
|
# continent["y"] = y
|
|
#
|
|
# list_other_continents.append(continent)
|
|
# continents.append(Rect2(continent["x"], continent["y"], continent["width"], continent["height"]))
|
|
|
|
|
|
func fill_continent(continent: Rect2i):
|
|
for x in continent.size.x:
|
|
for y in continent.size.y:
|
|
# set rectange radius
|
|
var center = Vector2(float(continent.size.x) / 2.0, float(continent.size.y) / 2.0)
|
|
var distance_x = abs(float(x) - center.x) / center.x
|
|
var distance_y = abs(float(y) - center.y) / center.y
|
|
var distance = sqrt(pow(distance_x, 2) + pow(distance_y, 2))
|
|
|
|
distance = remap(distance, 0, sqrt(2), 0, 1) * 1.2
|
|
|
|
# set height
|
|
var height = noise.get_noise_2d(x, y)
|
|
height = remap(height, -1, 1, 0, 1)
|
|
height -= distance
|
|
|
|
if height > 0:
|
|
var current_height = heightmap[continent.position.x + x][continent.position.y + y]
|
|
height = pow(height * 1.2, mountain_frequency)
|
|
height = remap(height, 0, 1, 1, world_elevation)
|
|
heightmap[continent.position.x + x][continent.position.y + y] = max(current_height, height)
|
|
if height > heightmap[continent.position.x + x][continent.position.y + y]:
|
|
heightmap[continent.position.x + x][continent.position.y + y] = height
|
|
elif height > 0 - coasts_thickness:
|
|
coastmap[x + continent.position.x][y + continent.position.y] = 1
|
|
|
|
#func sampleNormal(position: Vector2i):
|
|
# var left = heightmap[position.x - 1][position.y]
|
|
# var right = heightmap[position.x + 1][position.y]
|
|
# var top = heightmap[position.x][position.y - 1]
|
|
# var bottom = heightmap[position.x][position.y + 1]
|
|
#
|
|
## Vec3 normal = Vec3(2*(R-L), 2*(B-T), -4).Normalize();
|
|
## return Vector3i(2 * heightmap)
|
|
# pass
|
|
|
|
func erode(position: Vector2i):
|
|
const dropsPerCell = .4
|
|
const erosionRate = 1
|
|
const depositionRate = 0.7
|
|
const speed = .15
|
|
const friction = .7
|
|
const radius = .8
|
|
const maxIterations = 80
|
|
const iterationScale = .04
|
|
|
|
|
|
var ox = (randf() * 2 - 1) * radius # The X offset
|
|
var oy = (randf() * 2 - 1) * radius # The Y offset
|
|
var sediment = 0 # The amount of carried sediment
|
|
var xp = position.x # The previous X position
|
|
var yp = position.y # The previous Y position
|
|
var vx = 0 # The horizontal velocity
|
|
var vy = 0 # The vertical velocity
|
|
|
|
for i in maxIterations:
|
|
# Get the surface normal of the terrain at the current location
|
|
# var surfaceNormal = heightMap.sampleNormal(x + ox, y + oy)
|
|
var surfaceNormal = normal(Vector2i(position.x + ox, position.y + oy))
|
|
|
|
# If the terrain is flat, stop simulating, the snowball cannot roll any further
|
|
if (surfaceNormal.y == 1):
|
|
break
|
|
|
|
# Calculate the deposition and erosion rate
|
|
var deposit = sediment * depositionRate * surfaceNormal.y
|
|
var erosion = erosionRate * (1 - surfaceNormal.y) * min(1, i * iterationScale)
|
|
|
|
# print(deposit)
|
|
# print(erosion)
|
|
# Change the sediment on the place this snowball came from
|
|
# print(heightmap[xp][yp])
|
|
# heightmap[xp][yp] += deposit - erosion
|
|
heightmap[xp][yp] = 0
|
|
# print(heightmap[xp][yp])
|
|
sediment += erosion - deposit;
|
|
|
|
vx = friction * vx + surfaceNormal.x * speed
|
|
vy = friction * vy + surfaceNormal.z * speed
|
|
xp = position.x
|
|
yp = position.y
|
|
position.x += vx
|
|
position.y += vy
|
|
pass
|
|
|
|
|
|
|
|
func set_oceans():
|
|
var stack = []
|
|
var first_coord = Vector2i(0, 0)
|
|
|
|
stack.append(first_coord)
|
|
while stack.size():
|
|
var coord = stack.pop_back()
|
|
oceanmap[coord.x][coord.y] = 1
|
|
for neighbour in neighbours(coord).values():
|
|
if heightmap[neighbour.x][neighbour.y] == 0 and oceanmap[neighbour.x][neighbour.y] == 0:
|
|
stack.append(neighbour)
|
|
|
|
for x in world_width:
|
|
for y in world_height:
|
|
if heightmap[x][y] == 0 and not oceanmap[x][y]:
|
|
heightmap[x][y] = 2
|
|
|
|
|
|
func set_coasts():
|
|
for x in world_width:
|
|
for y in world_height:
|
|
if coastmap[x][y] and heightmap[x][y] == 0:
|
|
heightmap[x][y] = 1
|
|
pass
|
|
|
|
func neighbours(coord: Vector2i):
|
|
var neighbours: Dictionary
|
|
if coord.x + 1 < world_width:
|
|
neighbours.right = Vector2i(coord.x + 1, coord.y)
|
|
|
|
if coord.y + 1 < world_height :
|
|
neighbours.bottom = Vector2i(coord.x, coord.y + 1)
|
|
|
|
if coord.x - 1 >= 0:
|
|
neighbours.left = Vector2i(coord.x - 1, coord.y)
|
|
|
|
if coord.y - 1 >= 0:
|
|
neighbours.top = Vector2i(coord.x, coord.y - 1)
|
|
|
|
return neighbours
|
|
|
|
func normal(coord: Vector2i):
|
|
var neighbours = neighbours(coord)
|
|
if neighbours.size() < 4:
|
|
return Vector3(0, 1, 0)
|
|
# var normal = Vector3(2 * (neighbours.right - neighbours.left), 2 * (neighbours.bottom - neighbours.top), -4)
|
|
var normal = Vector3(
|
|
heightmap[neighbours.left.x][neighbours.left.y] - heightmap[neighbours.right.x][neighbours.right.y],
|
|
2,
|
|
heightmap[neighbours.top.x][neighbours.top.y] - heightmap[neighbours.bottom.x][neighbours.bottom.y]).normalized()
|
|
return normal
|
|
|
|
func set_slope():
|
|
for x in world_width:
|
|
for y in world_height:
|
|
var direction = null
|
|
var min_height = heightmap[x][y]
|
|
|
|
if x + 1 < world_width:
|
|
if heightmap[x + 1][y] < min_height:
|
|
min_height = heightmap[x + 1][y]
|
|
direction = "right"
|
|
|
|
if y + 1 < world_height:
|
|
if heightmap[x][y + 1] < min_height:
|
|
min_height = heightmap[x][y + 1]
|
|
direction = "bottom"
|
|
|
|
if x - 1 < world_width:
|
|
if heightmap[x - 1][y] < min_height:
|
|
min_height = heightmap[x - 1][y]
|
|
direction = "left"
|
|
|
|
if y - 1 < world_height:
|
|
if heightmap[x][y - 1] < min_height:
|
|
min_height = heightmap[x][y - 1]
|
|
direction = "top"
|
|
|
|
slopemap[x][y] = direction
|
|
|
|
func set_rivers():
|
|
for i in 20:
|
|
var good_river = false
|
|
while not good_river:
|
|
var river_path = []
|
|
var reached = false
|
|
var coord = mountain_blocs[randi_range(0, mountain_blocs.size()-1)]
|
|
var old_direction
|
|
while not reached:
|
|
var direction = slopemap[coord.x][coord.y]
|
|
|
|
if heightmap[coord.x][coord.y] == 1 or direction == opposite_direction(old_direction):
|
|
direction = old_direction
|
|
old_direction = direction
|
|
|
|
river_path.append({"coord": coord, "thickness": 5, "direction": direction})
|
|
|
|
if direction == "right":
|
|
coord = Vector2(coord.x + 1, coord.y)
|
|
elif direction == "bottom":
|
|
coord = Vector2(coord.x, coord.y + 1)
|
|
elif direction == "left":
|
|
coord = Vector2(coord.x - 1, coord.y)
|
|
elif direction == "top":
|
|
coord = Vector2(coord.x, coord.y - 1)
|
|
else:
|
|
reached = true
|
|
if (
|
|
heightmap[river_path.back().coord.x][river_path.back().coord.y] == 0 and
|
|
heightmap[river_path.back().coord.x] != heightmap[river_path.front().coord.x] and
|
|
heightmap[river_path.back().coord.y] != heightmap[river_path.front().coord.y]
|
|
):
|
|
good_river = true
|
|
rivers.append(river_path)
|
|
|
|
for river in rivers:
|
|
var coord: Vector2
|
|
for bloc in river:
|
|
for i in bloc.thickness:
|
|
if perpendicular_direction(bloc.direction) == "right":
|
|
coord = Vector2(bloc.coord.x + i, bloc.coord.y)
|
|
elif perpendicular_direction(bloc.direction) == "bottom":
|
|
coord = Vector2(bloc.coord.x, bloc.coord.y + i)
|
|
elif perpendicular_direction(bloc.direction) == "left":
|
|
coord = Vector2(bloc.coord.x - i, bloc.coord.y)
|
|
elif perpendicular_direction(bloc.direction) == "top":
|
|
coord = Vector2(bloc.coord.x, bloc.coord.y - i)
|
|
if blocs[coord.x][coord.y].type != Global.blocs.WATER:
|
|
# heightmap[coord.x][coord.y] -= 1
|
|
blocs[coord.x][coord.y].type = Global.blocs.WATER
|
|
|
|
func opposite_direction(direction):
|
|
if direction == "top":
|
|
return("bottom")
|
|
if direction == "bottom":
|
|
return "top"
|
|
if direction == "left":
|
|
return "right"
|
|
if direction == "right":
|
|
return "left"
|
|
return null
|
|
|
|
func perpendicular_direction(direction):
|
|
if direction == "top":
|
|
return("left")
|
|
if direction == "bottom":
|
|
return "right"
|
|
if direction == "left":
|
|
return "top"
|
|
if direction == "right":
|
|
return "bottom"
|
|
return null
|
|
|
|
|
|
func fix_heightmap():
|
|
for x in world_width:
|
|
for y in world_height:
|
|
heightmap[x][y] = ceil(heightmap[x][y])
|
|
# if heightmap[x][y] > 4:
|
|
# mountain_blocs.append(Vector2i(x, y))
|
|
|
|
func init_blocs():
|
|
for x in world_width:
|
|
blocs.append([])
|
|
for y in world_height:
|
|
var bloc = {
|
|
"type": 0,
|
|
"entity": -1
|
|
}
|
|
|
|
blocs[x].append(bloc)
|
|
|
|
func set_blocs():
|
|
for x in world_width:
|
|
for y in world_height:
|
|
if not blocs[x][y].type:
|
|
if heightmap[x][y] > 0:
|
|
blocs[x][y].type = Global.blocs.SAND
|
|
if heightmap[x][y] > 1:
|
|
blocs[x][y].type = Global.blocs.GRASS
|
|
if heightmap[x][y] > 10:
|
|
blocs[x][y].type = Global.blocs.STONE
|
|
if heightmap[x][y] > 15:
|
|
blocs[x][y].type = Global.blocs.SNOW
|
|
|
|
return blocs
|
|
|