diff options
Diffstat (limited to 'world/chunk')
-rw-r--r-- | world/chunk/Chunk.gd | 32 | ||||
-rw-r--r-- | world/chunk/Chunk.tscn | 46 | ||||
-rw-r--r-- | world/chunk/ChunkGen.gd | 123 | ||||
-rw-r--r-- | world/chunk/ChunkLoader.gd | 131 | ||||
-rw-r--r-- | world/chunk/helper/ChunkedCSGMesh.gd | 6 | ||||
-rw-r--r-- | world/chunk/helper/ChunkedMeshInstance.gd | 6 | ||||
-rw-r--r-- | world/chunk/helper/ChunkedSprite3D.gd | 6 |
7 files changed, 350 insertions, 0 deletions
diff --git a/world/chunk/Chunk.gd b/world/chunk/Chunk.gd new file mode 100644 index 0000000..4356caf --- /dev/null +++ b/world/chunk/Chunk.gd @@ -0,0 +1,32 @@ +extends Area + +enum LOD {DISTANCE,MID,CLOSE} +var lod = -1 + +onready var lod_distance = $"gen_tree/lod_distance" +onready var lod_mid = $"gen_tree/lod_mid" +onready var lod_close = $"gen_tree/lod_close" + +onready var _collision_enabled = lod_close.collision_layer + +func _ready(): + lod_update() + +func lod_update(): + match lod: + LOD.CLOSE: + lod_distance.visible = false + lod_mid.visible = true + lod_close.collision_layer = _collision_enabled + LOD.MID: + lod_distance.visible = false + lod_mid.visible = true + lod_close.collision_layer = 0 + LOD.DISTANCE: + lod_distance.visible = true + lod_mid.visible = false + lod_close.collision_layer = 0 + _: + lod_distance.visible = true + lod_mid.visible = false + lod_close.collision_layer = 0 diff --git a/world/chunk/Chunk.tscn b/world/chunk/Chunk.tscn new file mode 100644 index 0000000..5377ae9 --- /dev/null +++ b/world/chunk/Chunk.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=7 format=2] + +[ext_resource path="res://world/chunk/Chunk.gd" type="Script" id=1] +[ext_resource path="res://world/chunk/helper/ChunkedCSGMesh.gd" type="Script" id=2] + +[sub_resource type="SphereShape" id=1] +radius = 50.0 + +[sub_resource type="SpatialMaterial" id=3] +flags_unshaded = true +params_cull_mode = 2 +albedo_color = Color( 0.937255, 0, 1, 1 ) + +[sub_resource type="PlaneMesh" id=2] +size = Vector2( 100, 100 ) + +[sub_resource type="PlaneMesh" id=4] +size = Vector2( 98, 98 ) + +[node name="Chunk" type="Area"] +collision_layer = 32 +collision_mask = 0 +script = ExtResource( 1 ) + +[node name="Size" type="CollisionShape" parent="."] +shape = SubResource( 1 ) + +[node name="Border" type="CSGMesh" parent="."] +material_override = SubResource( 3 ) +script = ExtResource( 2 ) +_mesh = SubResource( 2 ) + +[node name="CSGMesh" type="CSGMesh" parent="Border"] +operation = 2 +script = ExtResource( 2 ) +_mesh = SubResource( 4 ) + +[node name="gen_tree" type="Spatial" parent="."] + +[node name="lod_distance" type="Spatial" parent="gen_tree"] + +[node name="lod_mid" type="Spatial" parent="gen_tree"] + +[node name="lod_close" type="StaticBody" parent="gen_tree"] +collision_layer = 8 +collision_mask = 0 diff --git a/world/chunk/ChunkGen.gd b/world/chunk/ChunkGen.gd new file mode 100644 index 0000000..994d624 --- /dev/null +++ b/world/chunk/ChunkGen.gd @@ -0,0 +1,123 @@ +extends Node + +onready var noise = OpenSimplexNoise.new() +onready var rng = RandomNumberGenerator.new() +var _seed + +var monuments = [] + +func _ready(): + #randomize() + #self._seed = randi() + self._seed = 0 + self.noise.seed = self._seed + self.noise.seed = 0 + self.noise.period = 16 + self.noise.persistence = 0 + self.rng.seed = self._seed + +func setup_monuments(): + monuments.push_back(DockMonument.new(Transform(Basis().rotated(Vector3.UP,0.0),Vector3(0.0,0.0,-600.0)))) + monuments.push_back(DockMonument.new(Transform(Basis().rotated(Vector3.UP,PI),Vector3(0.0,0.0,600.0)))) + +func rng_2dv(coords:Vector2): + self.rng.seed = hash(coords) + +func gen_chunk(chunk,dummy=false): + if dummy: + return + var monument = get_monument_at_chunk(ChunkLoader.v2_coords(chunk.transform.origin)) + if monument != null: + gen_monument(chunk, monument) + else: + gen_distance(chunk,chunk.get_node("gen_tree/lod_distance")) + gen_mid(chunk,chunk.get_node("gen_tree/lod_mid")) + gen_close(chunk,chunk.get_node("gen_tree/lod_close")) + +func get_monument_at_chunk(chunk_coords:Vector2): + for monument in monuments: + if monument.is_chunk_in_monument(chunk_coords): + return monument + return null + +func gen_monument(chunk, monument): + var offset_to_origin = monument.origin_chunk - ChunkLoader.v2_coords(chunk.transform.origin) + var inst = monument.scene.instance() + inst.transform.origin = ChunkLoader.v3_coords(offset_to_origin) + inst.transform.basis = monument.xform.basis + chunk.add_child(inst) + +func gen_distance(chunk,tree): + gen_lowres_rocks(chunk,tree) + +func gen_mid(chunk,tree): + gen_rocks(chunk,tree) + +func gen_close(chunk,tree): + gen_rock_hitboxes(chunk,tree) + +func iterate_chunk(chunk,tree,step:Vector2,cb:FuncRef,data): + var chunk_half_size = ChunkLoader.world.chunk_half_size + var chunk_size_rounded = Vector2(chunk_half_size,chunk_half_size).snapped(step) + for x in range(-chunk_size_rounded.x,chunk_size_rounded.x,step.x): + for y in range(-chunk_size_rounded.y,chunk_size_rounded.y,step.y): + cb.call_func(chunk,tree,Vector2(x,y),data) + +func make_multimesh(name,cnt,mesh): + var multi_inst = MultiMeshInstance.new() + multi_inst.name = name + var multi = MultiMesh.new() + multi_inst.multimesh = multi + multi.transform_format = MultiMesh.TRANSFORM_3D + multi.instance_count = cnt + multi.visible_instance_count = 0 + multi.mesh = mesh + return multi_inst + +var rock_mesh = preload("res://world/obstacles/Rock_mesh.tres") +var rock_mat = preload("res://world/obstacles/Rock_mat.tres") +func gen_rocks(chunk,tree): + rock_mesh.material = rock_mat + var multi = make_multimesh("rocks",200,rock_mesh) + tree.add_child(multi) + iterate_chunk(chunk, multi.multimesh, Vector2(10.0,10.0), funcref(self,"rock_rng"), [0.4,0.6,1.0,4.0,1.0,funcref(self,"make_rock")]) + +var rock_lowres_mesh = preload("res://world/obstacles/Rock_lowres_mesh.tres") +func gen_lowres_rocks(chunk,tree): + rock_lowres_mesh.material = rock_mat + var multi = make_multimesh("rocks",50,rock_lowres_mesh) + tree.add_child(multi) + iterate_chunk(chunk, multi.multimesh, Vector2(20.0,20.0), funcref(self,"rock_rng"), [0.4,0.6,2.0,8.0,0.5,funcref(self,"make_rock")]) + +var rock_coll = preload("res://world/obstacles/Rock_coll.tres") +func gen_rock_hitboxes(chunk,tree): + iterate_chunk(chunk, tree, Vector2(20.0,20.0), funcref(self,"rock_rng"), [0.4,0.6,1.0,4.0,1.0,funcref(self,"make_rock_hitbox")]) + +func rock_rng(chunk,rocks,chunk_coords:Vector2,data): + var coords = ChunkLoader.v3_coords(chunk_coords) + var basis = Basis() + var world_coords = chunk_coords + ChunkLoader.v2_coords(chunk.transform.origin) + self.rng_2dv(world_coords) + var rng_val = self.rng.randf_range(0.0,TAU) + basis = Basis(Vector3.UP,rng_val) * basis + var noise_val = self.noise.get_noise_2dv(world_coords * 0.125) + if noise_val < data[0]: + return + noise_val = min(noise_val,data[1]) + noise_val -= data[0] + noise_val /= data[1] - data[0] + noise_val = lerp(data[2],data[3],noise_val) + basis = basis.scaled(Vector3.ONE*noise_val) + basis = basis.scaled(Vector3(1.0,data[4],1.0)) + coords.y += (noise_val*data[4]*5.0)-5.0 + data[5].call_func(rocks,Transform(basis,coords)) + +func make_rock(rocks,xform:Transform): + rocks.visible_instance_count += 1 + rocks.set_instance_transform(rocks.visible_instance_count-1, xform) + +func make_rock_hitbox(rocks,xform:Transform): + var shape = CollisionShape.new() + shape.shape = rock_coll + shape.transform = xform + rocks.add_child(shape) diff --git a/world/chunk/ChunkLoader.gd b/world/chunk/ChunkLoader.gd new file mode 100644 index 0000000..0ea422a --- /dev/null +++ b/world/chunk/ChunkLoader.gd @@ -0,0 +1,131 @@ +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: + if monument.loaded_chunk == null: + monument.loaded_chunk = coords diff --git a/world/chunk/helper/ChunkedCSGMesh.gd b/world/chunk/helper/ChunkedCSGMesh.gd new file mode 100644 index 0000000..2b8c6db --- /dev/null +++ b/world/chunk/helper/ChunkedCSGMesh.gd @@ -0,0 +1,6 @@ +extends CSGMesh + +export(Mesh) var _mesh + +func _ready(): + self.mesh = _mesh diff --git a/world/chunk/helper/ChunkedMeshInstance.gd b/world/chunk/helper/ChunkedMeshInstance.gd new file mode 100644 index 0000000..dde7eda --- /dev/null +++ b/world/chunk/helper/ChunkedMeshInstance.gd @@ -0,0 +1,6 @@ +extends MeshInstance + +export(Mesh) var _mesh + +func _ready(): + self.mesh = _mesh diff --git a/world/chunk/helper/ChunkedSprite3D.gd b/world/chunk/helper/ChunkedSprite3D.gd new file mode 100644 index 0000000..c9e0be9 --- /dev/null +++ b/world/chunk/helper/ChunkedSprite3D.gd @@ -0,0 +1,6 @@ +extends Sprite3D + +export(Texture) var _tex + +func _ready(): + self.texture = _tex |