From 51a0664a1c2265adddbf4c42d4eda747c8ea8aaf Mon Sep 17 00:00:00 2001
From: dusoleil <howcansocksbereal@gmail.com>
Date: Mon, 19 Sep 2022 10:32:24 -0400
Subject: Add Levels of Detail per Chunk

---
 world/chunk/Chunk.gd       | 37 ++++++++++++++------
 world/chunk/Chunk.tscn     | 10 ++++++
 world/chunk/ChunkGen.gd    | 87 +++++++++++++++++++++++++++++++---------------
 world/chunk/ChunkLoader.gd | 32 +++++++++++++----
 4 files changed, 120 insertions(+), 46 deletions(-)

(limited to 'world/chunk')

diff --git a/world/chunk/Chunk.gd b/world/chunk/Chunk.gd
index 1860c84..4356caf 100644
--- a/world/chunk/Chunk.gd
+++ b/world/chunk/Chunk.gd
@@ -3,15 +3,30 @@ 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():
-    return
-    for obj in $"gen_tree".get_children():
-        match lod:
-            LOD.CLOSE:
-                obj.lod_close()
-            LOD.MID:
-                obj.lod_mid()
-            LOD.DISTANCE:
-                obj.lod_distance()
-            _:
-                obj.lod_distance()
+    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
index 4ce985b..5377ae9 100644
--- a/world/chunk/Chunk.tscn
+++ b/world/chunk/Chunk.tscn
@@ -34,3 +34,13 @@ _mesh = SubResource( 2 )
 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
index 75d3736..e71cc57 100644
--- a/world/chunk/ChunkGen.gd
+++ b/world/chunk/ChunkGen.gd
@@ -25,10 +25,18 @@ func rng_2dv(coords:Vector2):
     self.rng.seed = hash(coords)
 
 func gen_chunk(chunk):
-    var gen_tree = Spatial.new()
-    gen_tree.name = "gen_tree"
-    chunk.add_child(gen_tree)
-    gen_rocks(chunk)
+    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 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 v2_coords(coords:Vector3):
     return Vector2(coords.x,coords.z)
@@ -36,15 +44,43 @@ func v2_coords(coords:Vector3):
 func v3_coords(coords:Vector2):
     return Vector3(coords.x,0.0,coords.y)
 
-func iterate_chunk(chunk,step:Vector2,cb:FuncRef):
+func iterate_chunk(chunk,tree,step:Vector2,cb:FuncRef,data):
     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))
+            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 make_rock(chunk,chunk_coords:Vector2):
+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 = v3_coords(chunk_coords)
     var basis = Basis()
     var world_coords = chunk_coords + v2_coords(chunk.transform.origin)
@@ -52,28 +88,23 @@ func make_rock(chunk,chunk_coords:Vector2):
     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 < 0.4:
+    if noise_val < data[0]:
         return
-    noise_val = min(noise_val,0.6)
-    noise_val -= 0.4
-    noise_val /= 0.2
-    noise_val = lerp(1.0,4.0,noise_val)
+    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)
-    coords.y += (noise_val*5.0)-5.0
-    var gen_tree = chunk.get_node("gen_tree")
-    var rocks = gen_tree.get_node("rocks").multimesh
+    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, Transform(basis,coords))
+    rocks.set_instance_transform(rocks.visible_instance_count-1, xform)
 
-func gen_rocks(chunk):
-    var multi_inst = MultiMeshInstance.new()
-    multi_inst.name = "rocks"
-    var multi = MultiMesh.new()
-    multi_inst.multimesh = multi
-    chunk.get_node("gen_tree").add_child(multi_inst)
-    multi.transform_format = MultiMesh.TRANSFORM_3D
-    multi.instance_count = 1000
-    multi.visible_instance_count = 0
-    multi.mesh = rock_mesh
-    rock_mesh.material = rock_mat
-    iterate_chunk(chunk, Vector2(10.0,10.0), funcref(self,"make_rock"))
+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
index 2e01bca..42e40bb 100644
--- a/world/chunk/ChunkLoader.gd
+++ b/world/chunk/ChunkLoader.gd
@@ -3,13 +3,13 @@ extends Node
 var world = null
 
 var Chunk = preload("res://world/chunk/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
+var chunk_to_len
 
 func _ready():
     chunk_thread.start(self, "chunk_loader")
@@ -21,8 +21,11 @@ func _exit_tree():
     mtx.unlock()
     chunk_thread.wait_to_finish()
 
-func add_chunk(coords:Vector2):
-    chunks[coords] = coords.length()
+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 free_chunk(coords:Vector2):
     if !loaded_chunks.has(coords):
@@ -33,23 +36,35 @@ func free_chunk(coords:Vector2):
     loaded_chunks.erase(coords)
 
 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)
+            if gen_coords.length() > gen_radius:
+                continue
+            var coords = chunk_coords(gen_coords + gen_center)
+            chunks[coords] = coords.length()
     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]:
+            var chunk_len = chunks[chunk]
+            if closest_unloaded_chunk == null || closest_len > chunk_len:
                 closest_unloaded_chunk = chunk
-                closest_len = chunks[chunk]
+                closest_len = chunk_len
     if closest_unloaded_chunk != null:
         mtx.lock()
         if chunk_to_load == null:
             chunk_to_load = closest_unloaded_chunk
+            chunk_to_len = closest_len
             sgnl.post()
         mtx.unlock()
     for chunk in loaded_chunks.keys():
         if !chunks.has(chunk):
             free_chunk(chunk)
-    chunks = {}
 
 func chunk_loader():
     while true:
@@ -57,15 +72,18 @@ func chunk_loader():
         mtx.lock()
         var x = exit
         var coords = chunk_to_load
+        var owo = chunk_to_len
         chunk_to_load = null
         mtx.unlock()
         if x:
             break
         var chunk = Chunk.instance()
         chunk.transform.origin = Vector3(coords.x,0.0,coords.y)
+        owo -= world.chunk_size
+        chunk.lod = 2 if owo <= world.lod_close else 1 if owo <= world.lod_mid else 0
         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)
+    loaded_chunks[v2_coords(chunk.transform.origin)] = weakref(chunk)
-- 
cgit v1.2.3