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/World.gd                        | 37 ++++++---------
 world/World.tscn                      |  4 +-
 world/chunk/Chunk.gd                  | 37 ++++++++++-----
 world/chunk/Chunk.tscn                | 10 ++++
 world/chunk/ChunkGen.gd               | 87 ++++++++++++++++++++++++-----------
 world/chunk/ChunkLoader.gd            | 32 ++++++++++---
 world/obstacles/Rock.gd               | 18 --------
 world/obstacles/Rock.tscn             | 32 -------------
 world/obstacles/Rock_coll.tres        |  4 ++
 world/obstacles/Rock_lowres_mesh.tres |  4 ++
 10 files changed, 143 insertions(+), 122 deletions(-)
 delete mode 100644 world/obstacles/Rock.gd
 delete mode 100644 world/obstacles/Rock.tscn
 create mode 100644 world/obstacles/Rock_coll.tres
 create mode 100644 world/obstacles/Rock_lowres_mesh.tres

diff --git a/world/World.gd b/world/World.gd
index 2c8d822..04c3ef1 100644
--- a/world/World.gd
+++ b/world/World.gd
@@ -4,11 +4,13 @@ export(NodePath) var traveler = null
 onready var _traveler = get_node(traveler)
 export var acceleration = 10.0
 
-onready var lod_distance = $"ChunkRenderDistance"
-onready var lod_mid = $"ChunkRenderMid"
-onready var lod_close = $"ChunkRenderClose"
+onready var chunk_render_distance = $"ChunkRenderDistance"
+onready var chunk_render_mid = $"ChunkRenderMid"
+onready var chunk_render_close = $"ChunkRenderClose"
 
-onready var chunk_render_distance = lod_distance.get_node("Radius").shape.radius
+onready var lod_distance = chunk_render_distance.get_node("Radius").shape.radius
+onready var lod_mid = chunk_render_mid.get_node("Radius").shape.radius
+onready var lod_close = chunk_render_close.get_node("Radius").shape.radius
 
 onready var chunk_half_size = $"Chunks/Chunk/Size".shape.radius
 onready var chunk_size = chunk_half_size * 2.0
@@ -33,7 +35,7 @@ func _on_ChunkRenderClose_area_entered(area:Area):
         area.lod_update()
 
 func _on_ChunkRenderDistance_area_exited(area:Area):
-    ChunkLoader.free_chunk(chunk_coords(v2_coords(area.transform.origin)))
+    free_chunk(area)
 
 func _on_ChunkRenderMid_area_exited(area:Area):
     area.lod = area.LOD.DISTANCE
@@ -52,26 +54,13 @@ func travel(direction:Vector3):
     $"Chunks".add_central_force(direction*acceleration)
 
 func _physics_process(_state):
-    chunk_process()
+    ChunkLoader.chunk_update()
     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()
+func free_chunk(chunk):
+    var coords = chunk.transform.origin
+    coords = Vector2(coords.x,coords.z)
+    coords = coords.snapped(Vector2(chunk_size,chunk_size))
+    ChunkLoader.free_chunk(coords)
diff --git a/world/World.tscn b/world/World.tscn
index 81b2201..647f47b 100644
--- a/world/World.tscn
+++ b/world/World.tscn
@@ -4,10 +4,10 @@
 [ext_resource path="res://world/chunk/Chunk.tscn" type="PackedScene" id=3]
 
 [sub_resource type="SphereShape" id=24]
-radius = 500.0
+radius = 1000.0
 
 [sub_resource type="SphereShape" id=25]
-radius = 300.0
+radius = 500.0
 
 [sub_resource type="SphereShape" id=26]
 radius = 150.0
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)
diff --git a/world/obstacles/Rock.gd b/world/obstacles/Rock.gd
deleted file mode 100644
index 577e626..0000000
--- a/world/obstacles/Rock.gd
+++ /dev/null
@@ -1,18 +0,0 @@
-extends StaticBody
-
-onready var collision_enabled = self.collision_layer
-
-func lod_distance():
-    $"MeshInstance".visible = false
-    $"MeshInstance2".visible = true
-    self.collision_layer = 0
-
-func lod_mid():
-    $"MeshInstance".visible = true
-    $"MeshInstance2".visible = false
-    self.collision_layer = 0
-
-func lod_close():
-    $"MeshInstance".visible = true
-    $"MeshInstance2".visible = false
-    self.collision_layer = collision_enabled
diff --git a/world/obstacles/Rock.tscn b/world/obstacles/Rock.tscn
deleted file mode 100644
index 819e9a8..0000000
--- a/world/obstacles/Rock.tscn
+++ /dev/null
@@ -1,32 +0,0 @@
-[gd_scene load_steps=7 format=2]
-
-[ext_resource path="res://world/obstacles/Rock.gd" type="Script" id=1]
-[ext_resource path="res://world/chunk/helper/ChunkedMeshInstance.gd" type="Script" id=2]
-[ext_resource path="res://world/obstacles/Rock_mesh.tres" type="PrismMesh" id=3]
-[ext_resource path="res://world/obstacles/Rock_mat.tres" type="Material" id=4]
-
-[sub_resource type="ConvexPolygonShape" id=2]
-points = PoolVector3Array( 2.5, -5, 2.5, 2.5, -5, -2.5, -2.5, -5, -2.5, -2.5, -5, 2.5, 0, 5, 2.5, 0, 5, -2.5 )
-
-[sub_resource type="CubeMesh" id=9]
-size = Vector3( 5, 10, 5 )
-
-[node name="Rock" type="StaticBody"]
-collision_layer = 8
-collision_mask = 0
-script = ExtResource( 1 )
-
-[node name="CollisionShape" type="CollisionShape" parent="."]
-shape = SubResource( 2 )
-
-[node name="MeshInstance" type="MeshInstance" parent="."]
-visible = false
-material_override = ExtResource( 4 )
-script = ExtResource( 2 )
-_mesh = ExtResource( 3 )
-
-[node name="MeshInstance2" type="MeshInstance" parent="."]
-visible = false
-material_override = ExtResource( 4 )
-script = ExtResource( 2 )
-_mesh = SubResource( 9 )
diff --git a/world/obstacles/Rock_coll.tres b/world/obstacles/Rock_coll.tres
new file mode 100644
index 0000000..b06d080
--- /dev/null
+++ b/world/obstacles/Rock_coll.tres
@@ -0,0 +1,4 @@
+[gd_resource type="ConvexPolygonShape" format=2]
+
+[resource]
+points = PoolVector3Array( 2.5, -5, 2.5, 2.5, -5, -2.5, -2.5, -5, -2.5, -2.5, -5, 2.5, 0, 5, 2.5, 0, 5, -2.5 )
diff --git a/world/obstacles/Rock_lowres_mesh.tres b/world/obstacles/Rock_lowres_mesh.tres
new file mode 100644
index 0000000..910d7f6
--- /dev/null
+++ b/world/obstacles/Rock_lowres_mesh.tres
@@ -0,0 +1,4 @@
+[gd_resource type="CubeMesh" format=2]
+
+[resource]
+size = Vector3( 5, 10, 5 )
-- 
cgit v1.2.3