extends Node var world = null var Chunk = preload("res://world/chunk/Chunk.tscn") var loaded_chunks = {} var freed_chunks = {} var chunk_thread = Thread.new() var mtx = Mutex.new() var sgnl = Semaphore.new() var exit = false var chunk_to_load = null var chunk_to_len var chunk_to_dummy func _ready(): chunk_thread.start(self, "chunk_loader") func _exit_tree(): mtx.lock() exit = true sgnl.post() mtx.unlock() chunk_thread.wait_to_finish() func v2_coords(coords:Vector3): return Vector2(coords.x,coords.z) func chunk_coords(coords:Vector2): return coords.snapped(Vector2(world.chunk_size,world.chunk_size)) func v3_coords(coords:Vector2): return Vector3(coords.x,0.0,coords.y) func free_chunk(coords:Vector2): var monument = ChunkGen.get_monument_at_chunk(coords) if monument != null && monument.loaded_chunk != null && monument.loaded_chunk == coords: return if !loaded_chunks.has(coords): return freed_chunks[coords] = loaded_chunks[coords] loaded_chunks.erase(coords) func clean_chunks(): var cleaned = {} for chunk in freed_chunks.keys(): var c = freed_chunks[chunk].get_ref() if c == null: cleaned[chunk] = c continue c.queue_free() for chunk in cleaned.keys(): freed_chunks.erase(chunk) func chunk_update(): var chunks = {} var gen_center = -v2_coords(world.get_node("Chunks").transform.origin) var gen_radius = world.lod_distance var gen_radius_rounded = stepify(gen_radius, world.chunk_size) for x in range(-gen_radius_rounded, gen_radius_rounded+world.chunk_size, world.chunk_size): for y in range(-gen_radius_rounded, gen_radius_rounded+world.chunk_size, world.chunk_size): var gen_coords = Vector2(x,y) var coords_len = gen_coords.length() if coords_len > gen_radius: continue var coords = chunk_coords(gen_coords + gen_center) chunks[coords] = coords_len var closest_unloaded_chunk = null var closest_len = 0.0 for chunk in chunks.keys(): if !loaded_chunks.has(chunk): var chunk_len = chunks[chunk] if closest_unloaded_chunk == null || closest_len > chunk_len: closest_unloaded_chunk = chunk closest_len = chunk_len if closest_unloaded_chunk != null: var dummy = false var monument = ChunkGen.get_monument_at_chunk(closest_unloaded_chunk) if monument != null && monument.loaded_chunk != null: dummy = true mtx.lock() if chunk_to_load == null: chunk_to_load = closest_unloaded_chunk chunk_to_len = closest_len chunk_to_dummy = dummy sgnl.post() mtx.unlock() for monument in ChunkGen.monuments: if monument.loaded_chunk != null: var loaded = false for chunk in chunks.keys(): if monument.chunks.has(chunk): loaded = true break if !loaded: monument.loaded_chunk = null for chunk in loaded_chunks.keys(): if !chunks.has(chunk): free_chunk(chunk) clean_chunks() func chunk_loader(): while true: sgnl.wait() mtx.lock() var x = exit var coords = chunk_to_load var lod = chunk_to_len var dummy = chunk_to_dummy mtx.unlock() if x: break var chunk = Chunk.instance() chunk.transform.origin = Vector3(coords.x,0.0,coords.y) lod -= world.chunk_size var tmp = world.get_node("Chunk") chunk.lod = tmp.LOD.CLOSE if lod <= world.lod_close else tmp.LOD.MID if lod <= world.lod_mid else tmp.LOD.DISTANCE ChunkGen.gen_chunk(chunk,dummy) self.call_deferred("finish_chunk", chunk) func finish_chunk(chunk): mtx.lock() chunk_to_load = null mtx.unlock() world.get_node("Chunks").add_child(chunk) var coords = v2_coords(chunk.transform.origin) loaded_chunks[coords] = weakref(chunk) var monument = ChunkGen.get_monument_at_chunk(coords) if monument != null && monument.loaded_chunk == null: monument.loaded_chunk = coords