diff options
-rw-r--r-- | block.gd | 5 | ||||
-rw-r--r-- | chunk.gd | 308 | ||||
-rw-r--r-- | global.gd | 2 | ||||
-rw-r--r-- | main.gd | 90 | ||||
-rw-r--r-- | main.tscn | 4 | ||||
-rw-r--r-- | player.gd | 23 |
6 files changed, 276 insertions, 156 deletions
@@ -225,7 +225,8 @@ var BLOCK_TYPES = { }, } -var position: Vector3 +var position: Vector3i +var global_position: Vector3i var type: Type @@ -241,7 +242,7 @@ func add_face(surface_tool: SurfaceTool, face: Face): for idx in FACES[face].size(): var triangle = FACES[face][idx] var triangle_positions = triangle.map(func(item): - return (item * (BLOCK_SIZE * 0.5)) + position + (BLOCK_SIZE * 0.5) + return (item * (BLOCK_SIZE * 0.5)) + Vector3(global_position) + (BLOCK_SIZE * 0.5) ) var uvs = FACE_UVS[face][idx] @@ -10,142 +10,176 @@ var mesh: ArrayMesh = ArrayMesh.new() func generate_block_data(row: int, column: int, depth: int, empty: bool = false): - Global.mutex.lock() - grid_position = Vector3i(row, column, depth) blocks = [] + blocks.resize(Global.CHUNK_SIZE.x * Global.CHUNK_SIZE.y * Global.CHUNK_SIZE.z) + for x in Global.CHUNK_SIZE.x: - blocks.append([]) - for y in Global.CHUNK_SIZE.y: - blocks[x].append([]) - for z in Global.CHUNK_SIZE.z: - var block_position = Vector3i( - grid_position.x * Global.CHUNK_SIZE.x + x, - grid_position.y * Global.CHUNK_SIZE.y + y, - grid_position.z * Global.CHUNK_SIZE.z + z - )# * 2 - var value = noise.get_noise_3d(block_position.x, block_position.y, block_position.z) * 100 + for z in Global.CHUNK_SIZE.z: + var block_position := Vector3i(x, 0, z) + var block_global_position := Vector3i( + grid_position.x * Global.CHUNK_SIZE.x + block_position.x, + 0, + grid_position.z * Global.CHUNK_SIZE.z + block_position.z + ) + var height := Global.CHUNK_SIZE.y + + if grid_position.y >= Global.GROUND_LEVEL: + var noise_2d := noise.get_noise_2d( + block_global_position.x, + block_global_position.z + ) * 100 + height = round(clamp(abs(noise_2d), 0, Global.CHUNK_SIZE.y)) + + for y in Global.CHUNK_SIZE.y: + block_position.y = y + block_global_position. y = grid_position.y * Global.CHUNK_SIZE.y + block_position.y + + var value = noise.get_noise_3d( + block_global_position.x, + block_global_position.y, + block_global_position.z + ) * 100 - var type = Block.Type.AIR - if value > 4: - type = Block.Type.GRASS - if value < -4: - type = Block.Type.STONE - #if value < -16: - #type = Block.Type.DIRT - if value < -17 and value > -20: - type = Block.Type.SAND - if abs(value) > 21 and abs(value) < 21.25: - type = [Block.Type.STONE, Block.Type.YELLOW_STONE, Block.Type.BLUE_STONE, Block.Type.RED_STONE, Block.Type.BLACK_STONE].pick_random() - #print(value) + var type := Block.Type.AIR + if height < y: + type = Block.Type.AIR + else: + if value > -15: + type = Block.Type.GRASS + elif value > -20: + type = Block.Type.STONE + #if value > 4: + #type = Block.Type.GRASS + #if value < -4: + #type = Block.Type.STONE + ##if value < -16: + ##type = Block.Type.DIRT + #if value < -17 and value > -20: + #type = Block.Type.SAND + #if abs(value) > 21 and abs(value) < 21.25: + #type = [Block.Type.STONE, Block.Type.YELLOW_STONE, Block.Type.BLUE_STONE, Block.Type.RED_STONE, Block.Type.BLACK_STONE].pick_random() + ##print(value) var block = Block.new() block.position = block_position + block.global_position = block_global_position block.type = type - if empty: + if empty or Global.MAX_HEIGHT < block.global_position.y: block.type = Block.Type.AIR - blocks[x][y].append(block) - - Global.mutex.unlock() + var index := x + Global.CHUNK_SIZE.x * y + Global.CHUNK_SIZE.x * Global.CHUNK_SIZE.y * z + blocks[index] = block func generate_mesh(): - Global.mutex.lock() - var material := preload("res://new_shader_material.tres") var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) st.set_material(material) - for x in blocks.size(): - for y in blocks[x].size(): - for z in blocks[x][y].size(): - var block: Block = blocks[x][y][z] - - if block.type == Block.Type.AIR: - continue - - var block_front: Block - if block_exists(x, y, z + 1): - block_front = blocks[x][y][z + 1] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(0, 0, 1)): - var chunk_front := Global.chunks[grid_position + Vector3i(0, 0, 1)] - block_front = chunk_front.blocks[x][y][0] - - var block_back: Block - if block_exists(x, y, z - 1): - block_back = blocks[x][y][z - 1] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(0, 0, -1)): - var chunk_back := Global.chunks[grid_position + Vector3i(0, 0, -1)] - block_back = chunk_back.blocks[x][y][Global.CHUNK_SIZE.z - 1] - - var block_right: Block - if block_exists(x + 1, y, z): - block_right = blocks[x + 1][y][z] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(1, 0, 0)): - var chunk_right := Global.chunks[grid_position + Vector3i(1, 0, 0)] - block_right = chunk_right.blocks[0][y][z] - - var block_left: Block - if block_exists(x - 1, y, z): - block_left = blocks[x - 1][y][z] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(-1, 0, 0)): - var chunk_left := Global.chunks[grid_position + Vector3i(-1, 0, 0)] - block_left = chunk_left.blocks[Global.CHUNK_SIZE.x - 1][y][z] - - var block_top: Block - if block_exists(x, y + 1, z): - block_top = blocks[x][y + 1][z] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(0, 1, 0)): - var chunk_top := Global.chunks[grid_position + Vector3i(0, 1, 0)] - #if chunk_top.is_inside_tree(): - block_top = chunk_top.blocks[x][0][z] - - var block_bottom: Block - if block_exists(x, y - 1, z): - block_bottom = blocks[x][y - 1][z] - elif is_chunk_border(x, y, z) and chunk_exists(grid_position + Vector3i(0, -1, 0)): - var chunk_bottom := Global.chunks[grid_position + Vector3i(0, -1, 0)] - #if chunk_bottom.is_inside_tree(): - block_bottom = chunk_bottom.blocks[x][Global.CHUNK_SIZE.y - 1][z] - - if not block.type == Block.Type.AIR: - if block.type == Block.Type.GRASS: - if block_top and block_top.type != Block.Type.AIR: - block.type = Block.Type.DIRT - - var rng = RandomNumberGenerator.new() - rng.seed = randi() - st.set_color(Color(rng.randf(), rng.randf(), rng.randf())) - - st.set_uv(Vector2(0, 0)) - - if not block_front or block_front.type == Block.Type.AIR: - block.add_face(st, Block.Face.FRONT) - - if not block_back or block_back.type == Block.Type.AIR: - block.add_face(st, Block.Face.BACK) - - if not block_right or block_right.type == Block.Type.AIR: - block.add_face(st, Block.Face.RIGHT) - - if not block_left or block_left.type == Block.Type.AIR: - block.add_face(st, Block.Face.LEFT) - - if not block_top or block_top.type == Block.Type.AIR: - block.add_face(st, Block.Face.TOP) - - if not block_bottom or block_bottom.type == Block.Type.AIR: - block.add_face(st, Block.Face.BOTTOM) + for index in blocks.size(): + var block: Block = blocks[index] + if block.type == Block.Type.AIR: + continue + + var block_front: Block + if is_chunk_border_face(block.position, Block.Face.BACK): + if chunk_exists(grid_position + Vector3i(0, 0, 1)): + var chunk_front := Global.chunks[grid_position + Vector3i(0, 0, 1)] + if chunk_front.is_inside_tree(): + block_front = chunk_front.get_block(Vector3i(block.position.x, block.position.y, 0)) + else: + block_front = get_block(block.position + Vector3i(0, 0, 1)) + + var block_back: Block + if is_chunk_border_face(block.position, Block.Face.FRONT): + if chunk_exists(grid_position + Vector3i(0, 0, -1)): + var chunk_back := Global.chunks[grid_position + Vector3i(0, 0, -1)] + if chunk_back.is_inside_tree(): + block_back = chunk_back.get_block(Vector3i( + block.position.x, block.position.y, Global.CHUNK_SIZE.z - 1) + ) + else: + block_back = get_block(block.position + Vector3i(0, 0, -1)) + + var block_right: Block + if is_chunk_border_face(block.position, Block.Face.RIGHT): + if chunk_exists(grid_position + Vector3i(1, 0, 0)): + var chunk_right := Global.chunks[grid_position + Vector3i(1, 0, 0)] + if chunk_right.is_inside_tree(): + block_right = chunk_right.get_block(Vector3i(0, block.position.y, block.position.z)) + else: + block_right = get_block(block.position + Vector3i(1, 0, 0)) + + var block_left: Block + if is_chunk_border_face(block.position, Block.Face.LEFT): + if chunk_exists(grid_position + Vector3i(-1, 0, 0)): + var chunk_left := Global.chunks[grid_position + Vector3i(-1, 0, 0)] + if chunk_left.is_inside_tree(): + block_left = chunk_left.get_block(Vector3i( + Global.CHUNK_SIZE.x - 1, block.position.y, block.position.z) + ) + else: + block_left = get_block(block.position + Vector3i(-1, 0, 0)) + + var block_top: Block + if is_chunk_border_face(block.position, Block.Face.TOP): + if chunk_exists(grid_position + Vector3i(0, 1, 0)): + var chunk_top := Global.chunks[grid_position + Vector3i(0, 1, 0)] + if chunk_top.is_inside_tree(): + block_top = chunk_top.get_block(Vector3i(block.position.x, 0, block.position.z)) + else: + block_top = get_block(block.position + Vector3i(0, 1, 0)) + + var block_bottom: Block + if is_chunk_border_face(block.position, Block.Face.BOTTOM): + if chunk_exists(grid_position + Vector3i(0, -1, 0)): + var chunk_bottom := Global.chunks[grid_position + Vector3i(0, -1, 0)] + if chunk_bottom.is_inside_tree(): + block_bottom = chunk_bottom.get_block(Vector3i( + block.position.x, Global.CHUNK_SIZE.y - 1, block.position.z) + ) + else: + block_bottom = get_block(block.position + Vector3i(0, -1, 0)) + + if not block.type == Block.Type.AIR: + if block.type == Block.Type.GRASS: + if block_top and block_top.type != Block.Type.AIR: + block.type = Block.Type.DIRT + elif block.type == Block.Type.DIRT: + if not block_top or block_top.type == Block.Type.AIR: + block.type = Block.Type.GRASS + + var rng = RandomNumberGenerator.new() + rng.seed = randi() + st.set_color(Color(rng.randf(), rng.randf(), rng.randf())) + + st.set_uv(Vector2(0, 0)) + + if not block_front or block_front.type == Block.Type.AIR: + block.add_face(st, Block.Face.FRONT) + + if not block_back or block_back.type == Block.Type.AIR: + block.add_face(st, Block.Face.BACK) + + if not block_right or block_right.type == Block.Type.AIR: + block.add_face(st, Block.Face.RIGHT) + + if not block_left or block_left.type == Block.Type.AIR: + block.add_face(st, Block.Face.LEFT) + + if not block_top or block_top.type == Block.Type.AIR: + block.add_face(st, Block.Face.TOP) + + if not block_bottom or block_bottom.type == Block.Type.AIR: + block.add_face(st, Block.Face.BOTTOM) #st.generate_normals() mesh = st.commit() - - Global.mutex.unlock() func update_mesh() -> void: @@ -159,7 +193,8 @@ func update_mesh() -> void: func add_to(node: Node) -> void: - node.add_child(self) + name = "Chunk_%s_%s_%s" % [grid_position.x, grid_position.y, grid_position.z] + node.add_child(self, true) update_mesh() @@ -172,19 +207,32 @@ func generate_chunk(row: int, column: int, depth: int): func add_block(block_position: Vector3, block_type: Block.Type = Block.Type.GRASS) -> void: var block = Block.new() - block.position = Vector3i( - grid_position.x * Global.CHUNK_SIZE.x + int(block_position.x), - grid_position.y * Global.CHUNK_SIZE.y + int(block_position.y), - grid_position.z * Global.CHUNK_SIZE.z + int(block_position.z) + block.position = Vector3i(block_position) + block.global_position = Vector3i( + grid_position.x * Global.CHUNK_SIZE.x + block.position.x, + grid_position.y * Global.CHUNK_SIZE.y + block.position.y, + grid_position.z * Global.CHUNK_SIZE.z + block.position.z ) block.type = block_type - blocks[block_position.x][block_position.y][block_position.z] = block + #blocks[block_position.x][block_position.y][block_position.z] = block + set_block(block.position, block) #generate_mesh.call_deferred() func remove_block(block_position: Vector3) -> void: - blocks[block_position.x][block_position.y][block_position.z].type = Block.Type.AIR + #blocks[block_position.x][block_position.y][block_position.z].type = Block.Type.AIR + var block := get_block(block_position) + block.type = Block.Type.AIR + set_block(block.position, block) + print(block_position) + + +func get_block(p: Vector3i) -> Block: + return blocks[p.x + Global.CHUNK_SIZE.x * p.y + Global.CHUNK_SIZE.x * Global.CHUNK_SIZE.y * p.z] + +func set_block(p: Vector3i, block: Block) -> void: + blocks[p.x + Global.CHUNK_SIZE.x * p.y + Global.CHUNK_SIZE.x * Global.CHUNK_SIZE.y * p.z] = block static func chunk_exists(chunk_position: Vector3i): @@ -194,13 +242,29 @@ static func chunk_exists(chunk_position: Vector3i): func block_exists(x: int, y: int, z: int): return x >= 0 and blocks.size() - 1 >= x and y >= 0 and blocks[x].size() - 1 >= y and z >= 0 and blocks[x][y].size() - 1 >= z -func is_chunk_border(x: int, y: int, z: int): - return x == 0 or x == Global.CHUNK_SIZE.x - 1 or y == 0 or y == Global.CHUNK_SIZE.y - 1 or z == 0 or z == Global.CHUNK_SIZE.z - 1 +func is_chunk_border(p: Vector3i): + return p.x == 0 or p.x == Global.CHUNK_SIZE.x - 1 or p.y == 0 or p.y == Global.CHUNK_SIZE.y - 1 or p.z == 0 or p.z == Global.CHUNK_SIZE.z - 1 + +func is_chunk_border_face(block_position: Vector3i, face: Block.Face) -> bool: + if face == Block.Face.FRONT: + return block_position.z == 0 + elif face == Block.Face.BACK: + return block_position.z == Global.CHUNK_SIZE.z - 1 + elif face == Block.Face.LEFT: + return block_position.x == 0 + elif face == Block.Face.RIGHT: + return block_position.x == Global.CHUNK_SIZE.z - 1 + elif face == Block.Face.BOTTOM: + return block_position.y == 0 + elif face == Block.Face.TOP: + return block_position.y == Global.CHUNK_SIZE.y - 1 + + return false func get_bordering_chunks(block_position: Vector3i) -> Array[Vector3i]: var bordering_chunks := [] as Array[Vector3i] - if not is_chunk_border(block_position.x, block_position.y, block_position.z): + if not is_chunk_border(block_position): return bordering_chunks if block_position.z == 0: @@ -6,3 +6,5 @@ var mutex := Mutex.new() var chunks: Dictionary[Vector3i, Chunk] = {} var CHUNK_SIZE := Vector3i(16, 16, 16) +var GROUND_LEVEL := 0 +var MAX_HEIGHT := 15 @@ -4,39 +4,80 @@ extends Node3D @onready var noise := FastNoiseLite.new() +var chunks_to_generate: Array[Vector3i] = [] var chunks_to_update: Array[Vector3i] = [] -var active_task_id: int = -1 +var active_generate_task_id: int = -1 +var active_update_task_id: int = -1 + +var player_last_chunk := Vector3i.ZERO +var chunk_generation_radius := Vector3i(2, 2, 2) + +@onready var chunks_container := $Chunks func _ready() -> void: Global.player = $Player noise.seed = randi() - #var world_size := Vector3i(4, 2, 4) - # - #for column in range(world_size.y, 0, -1): - #for row in range(- world_size.x * 0.5, world_size.x * 0.5): - #for depth in range(- world_size.z * 0.5, world_size.z * 0.5): - #chunks_to_update.append(Vector3i(row, column, depth)) - chunks_to_update.append(Vector3i(0, 0, 0)) - chunks_to_update.append(Vector3i(0, 1, 0)) - #chunks_to_update.append(Vector3i(0, 2, 0)) + var world_size := Vector3i(2, 1, 2) + + for column in range(world_size.y, 0, -1): + for row in range(- world_size.x * 0.5, world_size.x * 0.5): + for depth in range(- world_size.z * 0.5, world_size.z * 0.5): + chunks_to_generate.append(Vector3i(row, column, depth)) + + # TODO: 2*1*2 worldgen on main thread + # TODO: show loading anim. how? + # TODO: worldgen not on main thread. + # TODO: get number of chunks to be gen + # TODO: ++number and show progress % func _process(_delta: float) -> void: - if chunks_to_update.size() > 0: - if active_task_id == -1: - active_task_id = WorkerThreadPool.add_group_task(update_chunk_task, chunks_to_update.size()) + if chunks_to_generate.size() > 0: + if active_generate_task_id == -1: + active_generate_task_id = WorkerThreadPool.add_task(generate_chunk_task.bind(0)) else: - if WorkerThreadPool.is_group_task_completed(active_task_id): - WorkerThreadPool.wait_for_group_task_completion(active_task_id) - chunks_to_update.clear() - active_task_id = -1 + if WorkerThreadPool.is_task_completed(active_generate_task_id): + WorkerThreadPool.wait_for_task_completion(active_generate_task_id) + chunks_to_generate.remove_at(0) + active_generate_task_id = -1 - # TODO: get player current chunk - # TODO: get all chunk positions in radius - # TODO: add missing chunks. remove surplus chunks - + if chunks_to_update.size() > 0: + if active_update_task_id == -1: + active_update_task_id = WorkerThreadPool.add_group_task(update_chunk_task, chunks_to_update.size()) + else: + if WorkerThreadPool.is_group_task_completed(active_update_task_id): + WorkerThreadPool.wait_for_group_task_completion(active_update_task_id) + chunks_to_update.remove_at(0) + active_update_task_id = -1 + + +func _physics_process(_delta: float) -> void: + if player_last_chunk != Global.player.current_chunk_position: + player_last_chunk = Global.player.current_chunk_position + var chunks_in_radius := [] + for x in range(- chunk_generation_radius.x * 0.5, chunk_generation_radius.x * 0.5 + 1): + for y in range(- chunk_generation_radius.y * 0.5, chunk_generation_radius.y * 0.5 + 1): + for z in range(- chunk_generation_radius.z * 0.5, chunk_generation_radius.z * 0.5 + 1): + var chunk_to_check := Global.player.current_chunk_position + Vector3i(x, y, z) + chunks_in_radius.append(chunk_to_check) + + # generate new or re-generate existing nodes + for p in chunks_in_radius: + if not Chunk.chunk_exists(p): + chunks_to_generate.append(p) + elif not chunks_container.has_node("Chunk_%s_%s_%s" % [p.x, p.y, p.z]): + chunks_to_generate.append(p) + + # remove surplus nodes + for node: Chunk in chunks_container.get_children(): + if not chunks_in_radius.has(node.grid_position): + chunks_container.remove_child(node) + + +func generate_chunk_task(index: int) -> void: + update_chunk(chunks_to_generate[index]) func update_chunk_task(index: int) -> void: update_chunk(chunks_to_update[index]) @@ -46,12 +87,17 @@ func update_chunk(chunk_position: Vector3i) -> void: var chunk: Chunk = Global.chunks[chunk_position] chunk.generate_mesh() chunk.update_mesh.call_deferred() + + (func(): + if not chunks_container.has_node("Chunk_%s_%s_%s" % [chunk_position.x, chunk_position.y, chunk_position.z]): + chunk.add_to(chunks_container) + ).call_deferred() else: var chunk := create_chunk(chunk_position) chunk.generate_block_data(chunk_position.x, chunk_position.y, chunk_position.z) chunk.generate_mesh() - chunk.add_to.call_deferred(self) + chunk.add_to.call_deferred(chunks_container) func create_chunk(chunk_position: Vector3i) -> Chunk: @@ -74,8 +74,10 @@ script = ExtResource("2_4jusq") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] +[node name="Chunks" type="Node3D" parent="."] + [node name="Player" type="CharacterBody3D" parent="." groups=["player"]] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 253.937, 2) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8, 16, 2) script = ExtResource("3_tn7lj") [node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] @@ -4,7 +4,9 @@ extends CharacterBody3D var selection_block: MeshInstance3D var selection_block_data: Block -var previous_looked_at_block_position: Vector3 = Vector3.ZERO +var previous_looked_at_block_position: Vector3i = Vector3i.ZERO + +var current_chunk_position: Vector3i const SPEED = 5.0#10.0#20.0 const JUMP_VELOCITY = 5.0#10.0#20.0 @@ -13,7 +15,7 @@ var acc = 0 func _ready() -> void: - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + #Input.mouse_mode = Input.MOUSE_MODE_CAPTURED selection_block = MeshInstance3D.new() selection_block.visible = false @@ -24,9 +26,10 @@ func _ready() -> void: selection_block_data = Block.new() selection_block_data.type = Block.Type.SELECTION - #set_physics_process(false) - #await get_tree().create_timer(3.0).timeout - #set_physics_process(true) + current_chunk_position = Chunk.global_to_chunk(global_position, Vector3.ZERO) + self.set_physics_process(false) + await get_tree().create_timer(3.0).timeout + self.set_physics_process(true) func _input(event: InputEvent) -> void: @@ -82,6 +85,8 @@ func _process(_delta: float) -> void: func _physics_process(delta: float) -> void: + current_chunk_position = Chunk.global_to_chunk(global_position, Vector3.ZERO) + # Add the gravity. if not is_on_floor(): velocity += get_gravity() * delta * 1.5 @@ -121,13 +126,13 @@ func maybe_update_selection_block(collision_point: Vector3) -> void: if Chunk.chunk_exists(chunk_idx): var chunk: Chunk = Global.chunks[chunk_idx] - var block: Block = chunk.blocks[block_chunk_idx.x][block_chunk_idx.y][block_chunk_idx.z] + var block: Block = chunk.get_block(block_chunk_idx) selection_block.visible = true - if previous_looked_at_block_position != block.position: - update_selection_block(block.position) - previous_looked_at_block_position = block.position + if previous_looked_at_block_position != block.global_position: + update_selection_block(block.global_position) + previous_looked_at_block_position = block.global_position func update_selection_block(new_position: Vector3): |