summaryrefslogtreecommitdiffstats
path: root/world/chunk
diff options
context:
space:
mode:
Diffstat (limited to 'world/chunk')
-rw-r--r--world/chunk/Chunk.gd32
-rw-r--r--world/chunk/Chunk.tscn46
-rw-r--r--world/chunk/ChunkGen.gd123
-rw-r--r--world/chunk/ChunkLoader.gd131
-rw-r--r--world/chunk/helper/ChunkedCSGMesh.gd6
-rw-r--r--world/chunk/helper/ChunkedMeshInstance.gd6
-rw-r--r--world/chunk/helper/ChunkedSprite3D.gd6
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