@tool extends Node var generation_thread: Thread = Thread.new() #### CHUNK GENERATION #### @export_category("CHUNK GENERATION") const CHUNK_SIZE := 256 const CHUNK_BOUNDS := 8 ## How detailed the chunk should be. This could be made a const when debug phase is finished @export_range(4,256,4) var chunk_resolution :=32: set(new_resolution): chunk_resolution = new_resolution update_all_mesh() #### WORLD GENERATION #### @export_category("WORLD GENERATION") @export_tool_button("Generate Chunks") var generate_chunk_callable = generate.bind(true) @export var noise: FastNoiseLite: set(new_noise): noise = new_noise update_all_mesh() if noise: noise.changed.connect(update_all_mesh) #change all thes meshes after eahc changes @export_range(4.0,128.0,4.0) var height := 64.0: set(new_height): height = new_height update_all_mesh() var loaded_chunks := {} # Called when the node enters the scene tree for the first time. ALSO IN EDITOR func _ready() -> void: generate(true) func generate(_ForceRegen:bool=false): print('Generation starts') generate_chunks_around(Vector3(0,0,0), _ForceRegen) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta: float) -> void: pass func generate_noise(_seed:float): noise = FastNoiseLite.new() # This function is better called in another thread, doing all the heavy process before adding it to the scene tree func generate_chunk(position: Vector3, _ForceRegen:bool = false): WorkerThreadPool.add_task(func(): var mesh_instance = MeshInstance3D.new() update_mesh(mesh_instance) mesh_instance.position = position call_deferred("add_child", mesh_instance) var chunk_coord = get_chunk_coord(position) loaded_chunks[chunk_coord] = mesh_instance print("chunk at : ",str(position),", coord ",str(chunk_coord), " loaded") ) func get_height(x:float,y:float) -> float: return noise.get_noise_2d(x,y) * height func get_normal(x:float,y:float) -> Vector3: var epsilon := CHUNK_SIZE / chunk_resolution var normal := Vector3( (get_height(x + epsilon,y) - get_height(x - epsilon, y))/(2.0 *epsilon), 1.0, (get_height(x, y + epsilon) - get_height(x, y - epsilon))/(2.0 *epsilon) ) return normal.normalized() func update_all_mesh(): for chunk in loaded_chunks: update_mesh(loaded_chunks[chunk]) func update_mesh(chunk_terrain_instance:MeshInstance3D): var plane: PlaneMesh = PlaneMesh.new() #load("res://world_gen/new_plane_mesh.tres").duplicate() plane.subdivide_depth = chunk_resolution plane.subdivide_width = chunk_resolution plane.size = Vector2(CHUNK_SIZE,CHUNK_SIZE) var plane_arrays := plane.get_mesh_arrays() var vertex_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_VERTEX] var normal_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_NORMAL] var tangent_array: PackedFloat32Array = plane_arrays[ArrayMesh.ARRAY_TANGENT] for i:int in vertex_array.size(): var vertex := vertex_array[i] # Convert local vertex coordinates to world coordinates var world_x = chunk_terrain_instance.position.x + vertex.x var world_z = chunk_terrain_instance.position.z + vertex.z var normal := Vector3.UP var tangent := Vector3.RIGHT if noise: vertex.y = get_height(world_x, world_z) normal = get_normal(world_x, world_z) tangent = normal.cross(Vector3.UP) vertex_array[i] = vertex normal_array[i] = normal tangent_array[4*i] = tangent.x tangent_array[4*i+1] = tangent.y tangent_array[4*i+2] = tangent.z tangent_array[4*i+3] = 1.0 var array_mesh := ArrayMesh.new() array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,plane_arrays) chunk_terrain_instance.mesh = array_mesh ## Convert position to chunk coordinates func get_chunk_coord(position:Vector3) -> Vector2i: var coord_chunk_x = int(floor(position.x / CHUNK_SIZE)) var coord_chunk_z = int(floor(position.z / CHUNK_SIZE)) return Vector2i(coord_chunk_x,coord_chunk_z) func generate_chunks_around(position:Vector3,ForceRegen:bool = false): # Convert position to chunk coordinates var position_to_coord := get_chunk_coord(position) print("closest chunk : "+str(Vector2i(position_to_coord.x,position_to_coord.y))) for x in range(position_to_coord.x - CHUNK_BOUNDS,position_to_coord.x + CHUNK_BOUNDS+1): for z in range(position_to_coord.y - CHUNK_BOUNDS, position_to_coord.y + CHUNK_BOUNDS+1): var chunk_coord := Vector2i(x,z) var chunck_position := Vector3i(x * CHUNK_SIZE,0,z * CHUNK_SIZE) print("trying to load chunk at coordinate : "+str(chunk_coord)) if ForceRegen: loaded_chunks.clear() for child in get_children(): child.queue_free() if not loaded_chunks.has(chunk_coord): print("generate chunk at : "+str(chunck_position)) generate_chunk(chunck_position)