1class_name Chunk 2extends StaticBody 3# These chunks are instanced and given data by VoxelWorld. 4# After that, chunks finish setting themselves up in the _ready() function. 5# If a chunk is changed, its "regenerate" method is called. 6 7const CHUNK_SIZE = 16 # Keep in sync with TerrainGenerator. 8const TEXTURE_SHEET_WIDTH = 8 9 10const CHUNK_LAST_INDEX = CHUNK_SIZE - 1 11const TEXTURE_TILE_SIZE = 1.0 / TEXTURE_SHEET_WIDTH 12 13var data = {} 14var chunk_position = Vector3() # TODO: Vector3i 15 16var _thread 17 18onready var voxel_world = get_parent() 19 20 21func _ready(): 22 transform.origin = chunk_position * CHUNK_SIZE 23 name = str(chunk_position) 24 if Settings.world_type == 0: 25 data = TerrainGenerator.random_blocks() 26 else: 27 data = TerrainGenerator.flat(chunk_position) 28 29 # We can only add colliders in the main thread due to physics limitations. 30 _generate_chunk_collider() 31 # However, we can use a thread for mesh generation. 32 _thread = Thread.new() 33 _thread.start(self, "_generate_chunk_mesh") 34 35 36func regenerate(): 37 # Clear out all old nodes first. 38 for c in get_children(): 39 remove_child(c) 40 c.queue_free() 41 42 # Then generate new ones. 43 _generate_chunk_collider() 44 _generate_chunk_mesh(0) 45 46 47func _generate_chunk_collider(): 48 if data.empty(): 49 # Avoid errors caused by StaticBody not having colliders. 50 _create_block_collider(Vector3.ZERO) 51 collision_layer = 0 52 collision_mask = 0 53 return 54 55 # For each block, generate a collider. Ensure collision layers are enabled. 56 collision_layer = 0xFFFFF 57 collision_mask = 0xFFFFF 58 for block_position in data.keys(): 59 var block_id = data[block_position] 60 if block_id != 27 and block_id != 28: 61 _create_block_collider(block_position) 62 63 64func _generate_chunk_mesh(_this_argument_exists_due_to_bug_9924): 65 if data.empty(): 66 return 67 68 var surface_tool = SurfaceTool.new() 69 surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES) 70 71 # For each block, add data to the SurfaceTool and generate a collider. 72 for block_position in data.keys(): 73 var block_id = data[block_position] 74 _draw_block_mesh(surface_tool, block_position, block_id) 75 76 # Create the chunk's mesh from the SurfaceTool data. 77 surface_tool.generate_normals() 78 surface_tool.generate_tangents() 79 surface_tool.index() 80 var array_mesh = surface_tool.commit() 81 var mi = MeshInstance.new() 82 mi.mesh = array_mesh 83 mi.material_override = preload("res://world/textures/material.tres") 84 add_child(mi) 85 86 87func _draw_block_mesh(surface_tool, block_sub_position, block_id): 88 var verts = calculate_block_verts(block_sub_position) 89 var uvs = calculate_block_uvs(block_id) 90 var top_uvs = uvs 91 var bottom_uvs = uvs 92 93 # Bush blocks get drawn in their own special way. 94 if block_id == 27 or block_id == 28: 95 _draw_block_face(surface_tool, [verts[2], verts[0], verts[7], verts[5]], uvs) 96 _draw_block_face(surface_tool, [verts[7], verts[5], verts[2], verts[0]], uvs) 97 _draw_block_face(surface_tool, [verts[3], verts[1], verts[6], verts[4]], uvs) 98 _draw_block_face(surface_tool, [verts[6], verts[4], verts[3], verts[1]], uvs) 99 return 100 101 # Allow some blocks to have different top/bottom textures. 102 if block_id == 3: # Grass. 103 top_uvs = calculate_block_uvs(0) 104 bottom_uvs = calculate_block_uvs(2) 105 elif block_id == 5: # Furnace. 106 top_uvs = calculate_block_uvs(31) 107 bottom_uvs = top_uvs 108 elif block_id == 12: # Log. 109 top_uvs = calculate_block_uvs(30) 110 bottom_uvs = top_uvs 111 elif block_id == 19: # Bookshelf. 112 top_uvs = calculate_block_uvs(4) 113 bottom_uvs = top_uvs 114 115 # Main rendering code for normal blocks. 116 var other_block_position = block_sub_position + Vector3.LEFT 117 var other_block_id = 0 118 if other_block_position.x == -1: 119 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 120 elif data.has(other_block_position): 121 other_block_id = data[other_block_position] 122 if block_id != other_block_id and is_block_transparent(other_block_id): 123 _draw_block_face(surface_tool, [verts[2], verts[0], verts[3], verts[1]], uvs) 124 125 other_block_position = block_sub_position + Vector3.RIGHT 126 other_block_id = 0 127 if other_block_position.x == CHUNK_SIZE: 128 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 129 elif data.has(other_block_position): 130 other_block_id = data[other_block_position] 131 if block_id != other_block_id and is_block_transparent(other_block_id): 132 _draw_block_face(surface_tool, [verts[7], verts[5], verts[6], verts[4]], uvs) 133 134 other_block_position = block_sub_position + Vector3.FORWARD 135 other_block_id = 0 136 if other_block_position.z == -1: 137 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 138 elif data.has(other_block_position): 139 other_block_id = data[other_block_position] 140 if block_id != other_block_id and is_block_transparent(other_block_id): 141 _draw_block_face(surface_tool, [verts[6], verts[4], verts[2], verts[0]], uvs) 142 143 other_block_position = block_sub_position + Vector3.BACK 144 other_block_id = 0 145 if other_block_position.z == CHUNK_SIZE: 146 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 147 elif data.has(other_block_position): 148 other_block_id = data[other_block_position] 149 if block_id != other_block_id and is_block_transparent(other_block_id): 150 _draw_block_face(surface_tool, [verts[3], verts[1], verts[7], verts[5]], uvs) 151 152 other_block_position = block_sub_position + Vector3.DOWN 153 other_block_id = 0 154 if other_block_position.y == -1: 155 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 156 elif data.has(other_block_position): 157 other_block_id = data[other_block_position] 158 if block_id != other_block_id and is_block_transparent(other_block_id): 159 _draw_block_face(surface_tool, [verts[4], verts[5], verts[0], verts[1]], bottom_uvs) 160 161 other_block_position = block_sub_position + Vector3.UP 162 other_block_id = 0 163 if other_block_position.y == CHUNK_SIZE: 164 other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE) 165 elif data.has(other_block_position): 166 other_block_id = data[other_block_position] 167 if block_id != other_block_id and is_block_transparent(other_block_id): 168 _draw_block_face(surface_tool, [verts[2], verts[3], verts[6], verts[7]], top_uvs) 169 170 171func _draw_block_face(surface_tool, verts, uvs): 172 surface_tool.add_uv(uvs[1]); surface_tool.add_vertex(verts[1]) 173 surface_tool.add_uv(uvs[2]); surface_tool.add_vertex(verts[2]) 174 surface_tool.add_uv(uvs[3]); surface_tool.add_vertex(verts[3]) 175 176 surface_tool.add_uv(uvs[2]); surface_tool.add_vertex(verts[2]) 177 surface_tool.add_uv(uvs[1]); surface_tool.add_vertex(verts[1]) 178 surface_tool.add_uv(uvs[0]); surface_tool.add_vertex(verts[0]) 179 180 181func _create_block_collider(block_sub_position): 182 var collider = CollisionShape.new() 183 collider.shape = BoxShape.new() 184 collider.shape.extents = Vector3.ONE / 2 185 collider.transform.origin = block_sub_position + Vector3.ONE / 2 186 add_child(collider) 187 188 189static func calculate_block_uvs(block_id): 190 # This method only supports square texture sheets. 191 var row = block_id / TEXTURE_SHEET_WIDTH 192 var col = block_id % TEXTURE_SHEET_WIDTH 193 194 return [ 195 TEXTURE_TILE_SIZE * Vector2(col, row), 196 TEXTURE_TILE_SIZE * Vector2(col, row + 1), 197 TEXTURE_TILE_SIZE * Vector2(col + 1, row), 198 TEXTURE_TILE_SIZE * Vector2(col + 1, row + 1), 199 ] 200 201 202static func calculate_block_verts(block_position): 203 return [ 204 Vector3(block_position.x, block_position.y, block_position.z), 205 Vector3(block_position.x, block_position.y, block_position.z + 1), 206 Vector3(block_position.x, block_position.y + 1, block_position.z), 207 Vector3(block_position.x, block_position.y + 1, block_position.z + 1), 208 Vector3(block_position.x + 1, block_position.y, block_position.z), 209 Vector3(block_position.x + 1, block_position.y, block_position.z + 1), 210 Vector3(block_position.x + 1, block_position.y + 1, block_position.z), 211 Vector3(block_position.x + 1, block_position.y + 1, block_position.z + 1), 212 ] 213 214 215static func is_block_transparent(block_id): 216 return block_id == 0 or (block_id > 25 and block_id < 30) 217