summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordusoleil <howcansocksbereal@gmail.com>2022-09-18 07:45:13 -0400
committerdusoleil <howcansocksbereal@gmail.com>2022-09-18 07:45:13 -0400
commit3ac2fb62b67b6446ab31b2455a4cb74069f1c54f (patch)
tree4764fa39466d7fd0d4ac9133e5a237dd1576a487
parent5923cd97dfafd7fee2784a91e81d7d55ed28448e (diff)
downloadgodot_wildjam_49-3ac2fb62b67b6446ab31b2455a4cb74069f1c54f.tar.gz
godot_wildjam_49-3ac2fb62b67b6446ab31b2455a4cb74069f1c54f.zip
Add ChunkLoader to World Generation
Break up world gen into chunk and offload generating them to a Chunk Loader. The World figures out which chunks should be loaded and gives this to the Chunk Loader. The Chunk Loader frees any chunks out of render distance. It also picks an unloaded chunk and gives it to a worker thread to generate it.
-rw-r--r--Chunk.tscn19
-rw-r--r--ChunkGen.gd35
-rw-r--r--ChunkLoader.gd66
-rw-r--r--ChunkedCSGMesh.gd6
-rw-r--r--ChunkedMeshInstance.gd6
-rw-r--r--ChunkedSprite3D.gd6
-rw-r--r--World.gd60
-rw-r--r--World.tscn22
-rw-r--r--project.godot4
9 files changed, 169 insertions, 55 deletions
diff --git a/Chunk.tscn b/Chunk.tscn
index 0d0771e..0437a7e 100644
--- a/Chunk.tscn
+++ b/Chunk.tscn
@@ -1,33 +1,34 @@
-[gd_scene load_steps=6 format=2]
+[gd_scene load_steps=7 format=2]
-[ext_resource path="res://Chunk.gd" type="Script" id=1]
+[ext_resource path="res://ChunkedCSGMesh.gd" type="Script" id=3]
[sub_resource type="SphereShape" id=1]
radius = 50.0
-[sub_resource type="PlaneMesh" id=2]
-size = Vector2( 100, 100 )
-
[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="."]
-mesh = SubResource( 2 )
-material = SubResource( 3 )
+material_override = SubResource( 3 )
+script = ExtResource( 3 )
+_mesh = SubResource( 2 )
[node name="CSGMesh" type="CSGMesh" parent="Border"]
operation = 2
-mesh = SubResource( 4 )
+script = ExtResource( 3 )
+_mesh = SubResource( 4 )
diff --git a/ChunkGen.gd b/ChunkGen.gd
new file mode 100644
index 0000000..6e1f0eb
--- /dev/null
+++ b/ChunkGen.gd
@@ -0,0 +1,35 @@
+extends Node
+
+var chunk_half_size setget _set_chunk_size
+var chunk_size
+
+func _set_chunk_size(val):
+ chunk_half_size = val
+ chunk_size = val * 2.0
+
+onready var noise = OpenSimplexNoise.new()
+
+func _ready():
+ randomize()
+ noise.seed = randi()
+ noise.octaves = 4
+ noise.period = 64
+ noise.persistence = 0.001
+ noise.lacunarity = 2.0
+
+func gen_chunk(chunk):
+ var gen_tree = Spatial.new()
+ gen_tree.name = "gen_tree"
+ chunk.add_child(gen_tree)
+
+func v2_coords(coords:Vector3):
+ return Vector2(coords.x,coords.z)
+
+func v3_coords(coords:Vector2):
+ return Vector3(coords.x,0.0,coords.y)
+
+func iterate_chunk(chunk,step:Vector2,cb:FuncRef):
+ 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,Vector2(x,y))
diff --git a/ChunkLoader.gd b/ChunkLoader.gd
new file mode 100644
index 0000000..90cac4f
--- /dev/null
+++ b/ChunkLoader.gd
@@ -0,0 +1,66 @@
+extends Node
+
+var world = null
+
+var Chunk = preload("res://Chunk.tscn")
+var chunks = {}
+var loaded_chunks = {}
+var chunk_thread = Thread.new()
+var mtx = Mutex.new()
+var sgnl = Semaphore.new()
+var exit = false
+var chunk_to_load = null
+
+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 add_chunk(coords:Vector2):
+ chunks[coords] = coords.length()
+
+func chunk_update():
+ var closest_unloaded_chunk = null
+ var closest_len = 0.0
+ for chunk in chunks.keys():
+ if !loaded_chunks.has(chunk):
+ if closest_unloaded_chunk == null || closest_len > chunks[chunk]:
+ closest_unloaded_chunk = chunk
+ closest_len = chunks[chunk]
+ if closest_unloaded_chunk != null:
+ mtx.lock()
+ if chunk_to_load == null:
+ chunk_to_load = closest_unloaded_chunk
+ sgnl.post()
+ mtx.unlock()
+ for chunk in loaded_chunks.keys():
+ if !chunks.has(chunk):
+ var c = loaded_chunks[chunk].get_ref()
+ if c != null:
+ c.queue_free()
+ loaded_chunks.erase(chunk)
+ chunks = {}
+
+func chunk_loader():
+ while true:
+ sgnl.wait()
+ mtx.lock()
+ var x = exit
+ var coords = chunk_to_load
+ chunk_to_load = null
+ mtx.unlock()
+ if x:
+ break
+ var chunk = Chunk.instance()
+ chunk.transform.origin = Vector3(coords.x,0.0,coords.y)
+ ChunkGen.gen_chunk(chunk)
+ self.call_deferred("finish_chunk", chunk)
+
+func finish_chunk(chunk):
+ world.get_node("Chunks").add_child(chunk)
+ loaded_chunks[Vector2(chunk.transform.origin.x,chunk.transform.origin.z)] = weakref(chunk)
diff --git a/ChunkedCSGMesh.gd b/ChunkedCSGMesh.gd
new file mode 100644
index 0000000..2b8c6db
--- /dev/null
+++ b/ChunkedCSGMesh.gd
@@ -0,0 +1,6 @@
+extends CSGMesh
+
+export(Mesh) var _mesh
+
+func _ready():
+ self.mesh = _mesh
diff --git a/ChunkedMeshInstance.gd b/ChunkedMeshInstance.gd
new file mode 100644
index 0000000..dde7eda
--- /dev/null
+++ b/ChunkedMeshInstance.gd
@@ -0,0 +1,6 @@
+extends MeshInstance
+
+export(Mesh) var _mesh
+
+func _ready():
+ self.mesh = _mesh
diff --git a/ChunkedSprite3D.gd b/ChunkedSprite3D.gd
new file mode 100644
index 0000000..c9e0be9
--- /dev/null
+++ b/ChunkedSprite3D.gd
@@ -0,0 +1,6 @@
+extends Sprite3D
+
+export(Texture) var _tex
+
+func _ready():
+ self.texture = _tex
diff --git a/World.gd b/World.gd
index da811a8..97f0a09 100644
--- a/World.gd
+++ b/World.gd
@@ -4,45 +4,17 @@ export(NodePath) var traveler = null
onready var _traveler = get_node(traveler)
export var acceleration = 10.0
-onready var Chunk = preload("res://Chunk.tscn")
-onready var chunk_size = $"Chunks/Chunk/Size".shape.radius*2.0
-var chunks = {}
+onready var chunk_render_distance = $"ChunkRenderDistance/Radius".shape.radius
-func v2_coords(coords:Vector3):
- return Vector2(coords.x,coords.z)
-
-func v3_coords(coords:Vector2):
- return Vector3(coords.x,0.0,coords.y)
+onready var chunk_half_size = $"Chunks/Chunk/Size".shape.radius
+onready var chunk_size = chunk_half_size * 2.0
-func chunk_coords(coords:Vector2):
- return coords.snapped(Vector2(chunk_size,chunk_size))
-
-func gen_chunk(coords:Vector2):
- var chunk = Chunk.instance()
- chunk.transform.origin = v3_coords(coords)
- $"Chunks".add_child(chunk)
- chunks[coords] = chunk
-
-func gen_chunks():
- var gen_center = -v2_coords($"Chunks".transform.origin)
- var gen_radius = $"ChunkRenderDistance/Radius".shape.radius
- var gen_radius_rounded = stepify(gen_radius, chunk_size)
- for x in range(-gen_radius_rounded, gen_radius_rounded+chunk_size, chunk_size):
- for y in range(-gen_radius_rounded, gen_radius_rounded+chunk_size, chunk_size):
- var gen_coords = Vector2(x,y)
- if gen_coords.length() > gen_radius:
- continue
- var coords = chunk_coords(gen_coords + gen_center)
- if !chunks.has(coords):
- gen_chunk(coords)
+func _ready():
+ ChunkLoader.world = self
+ ChunkGen.chunk_half_size = self.chunk_half_size
func _on_ChunkRenderDistance_area_exited(area:Area):
- gen_chunks()
area.queue_free()
- chunks.erase(v2_coords(area.transform.origin))
-
-func _ready():
- gen_chunks()
func travel(direction:Vector3):
var heading = _traveler.global_transform.basis
@@ -53,6 +25,26 @@ func travel(direction:Vector3):
$"Chunks".add_central_force(direction*acceleration)
func _physics_process(_state):
+ chunk_process()
var stick = Input.get_axis("ship_down","ship_up")
if stick != 0.0:
self.travel(Vector3.FORWARD*stick)
+
+func v2_coords(coords:Vector3):
+ return Vector2(coords.x,coords.z)
+
+func chunk_coords(coords:Vector2):
+ return coords.snapped(Vector2(chunk_size,chunk_size))
+
+func chunk_process():
+ var gen_center = -v2_coords($"Chunks".transform.origin)
+ var gen_radius = chunk_render_distance
+ var gen_radius_rounded = stepify(gen_radius, chunk_size)
+ for x in range(-gen_radius_rounded, gen_radius_rounded+chunk_size, chunk_size):
+ for y in range(-gen_radius_rounded, gen_radius_rounded+chunk_size, chunk_size):
+ var gen_coords = Vector2(x,y)
+ if gen_coords.length() > gen_radius:
+ continue
+ var coords = chunk_coords(gen_coords + gen_center)
+ ChunkLoader.add_chunk(coords)
+ ChunkLoader.chunk_update()
diff --git a/World.tscn b/World.tscn
index db93160..3bdf371 100644
--- a/World.tscn
+++ b/World.tscn
@@ -1,17 +1,24 @@
-[gd_scene load_steps=5 format=2]
+[gd_scene load_steps=9 format=2]
[ext_resource path="res://World.gd" type="Script" id=1]
[ext_resource path="res://Chunk.tscn" type="PackedScene" id=3]
-[sub_resource type="BoxShape" id=23]
-
[sub_resource type="SphereShape" id=24]
-radius = 300.0
+radius = 500.0
+
+[sub_resource type="BoxShape" id=23]
[node name="World" type="Spatial"]
script = ExtResource( 1 )
acceleration = 50.0
+[node name="ChunkRenderDistance" type="Area" parent="."]
+collision_layer = 0
+collision_mask = 32
+
+[node name="Radius" type="CollisionShape" parent="ChunkRenderDistance"]
+shape = SubResource( 24 )
+
[node name="Chunks" type="RigidBody" parent="."]
collision_layer = 0
collision_mask = 0
@@ -29,11 +36,4 @@ shape = SubResource( 23 )
visible = false
collision_layer = 0
-[node name="ChunkRenderDistance" type="Area" parent="."]
-collision_layer = 0
-collision_mask = 32
-
-[node name="Radius" type="CollisionShape" parent="ChunkRenderDistance"]
-shape = SubResource( 24 )
-
[connection signal="area_exited" from="ChunkRenderDistance" to="." method="_on_ChunkRenderDistance_area_exited"]
diff --git a/project.godot b/project.godot
index 34e666b..a22cfe0 100644
--- a/project.godot
+++ b/project.godot
@@ -27,7 +27,8 @@ config/icon="res://icon.png"
[autoload]
Settings="*res://singletons/settings.gd"
-WorldGenRNG="*res://WorldGenRNG.gd"
+ChunkLoader="*res://ChunkLoader.gd"
+ChunkGen="*res://ChunkGen.gd"
[display]
@@ -121,5 +122,6 @@ common/enable_pause_aware_picking=true
[rendering]
+threads/thread_model=2
quality/filters/msaa=3
environment/default_environment="res://default_env.tres"