extends Node3D class_name Chunk const CHUNK_SIZE: int = 16 var _blocks: PackedInt32Array = PackedInt32Array() var _mesh_instance: MeshInstance3D = null var _static_body: StaticBody3D = null static var shared_material: StandardMaterial3D = null static func _init_material() -> void: if shared_material == null: shared_material = StandardMaterial3D.new() shared_material.albedo_texture = load("res://assets/textures/block_atlas.png") shared_material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST_WITH_MIPMAPS shared_material.vertex_color_use_as_albedo = false shared_material.shading_mode = BaseMaterial3D.SHADING_MODE_PER_VERTEX func _ready() -> void: if _blocks.size() == 0: _blocks.resize(4096) func init_blocks(data: PackedInt32Array) -> void: _blocks = data func get_block(x: int, y: int, z: int) -> int: if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE: return BlockDatabase.BlockType.AIR return _blocks[x + y * CHUNK_SIZE + z * 256] func set_block(x: int, y: int, z: int, id: int) -> void: if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE: return _blocks[x + y * CHUNK_SIZE + z * 256] = id func generate_mesh() -> void: if _mesh_instance != null: _mesh_instance.queue_free() _mesh_instance = null if _static_body != null: _static_body.queue_free() _static_body = null _init_material() var tile_w: float = BlockDatabase.ATLAS_TILE_SIZE.x var tile_h: float = BlockDatabase.ATLAS_TILE_SIZE.y # Surface 0: solid blocks (atlas texture) var st: SurfaceTool = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) # Emissive surfaces: cyan (1), violet (2), vent (3) var st_cyan: SurfaceTool = SurfaceTool.new() st_cyan.begin(Mesh.PRIMITIVE_TRIANGLES) var st_violet: SurfaceTool = SurfaceTool.new() st_violet.begin(Mesh.PRIMITIVE_TRIANGLES) var st_vent: SurfaceTool = SurfaceTool.new() st_vent.begin(Mesh.PRIMITIVE_TRIANGLES) var has_faces: bool = false var has_cyan: bool = false var has_violet: bool = false var has_vent: bool = false for x: int in range(CHUNK_SIZE): for y: int in range(CHUNK_SIZE): for z: int in range(CHUNK_SIZE): var block_id: int = get_block(x, y, z) if not BlockDatabase.is_solid(block_id): continue var uv_tl: Vector2 = BlockDatabase.get_atlas_uv(block_id) var target_st: SurfaceTool = st var emit_type: int = 0 # 0=none, 1=cyan, 2=violet, 3=vent if block_id == BlockDatabase.BlockType.GLOW_CORAL_CYAN: target_st = st_cyan emit_type = 1 elif block_id == BlockDatabase.BlockType.GLOW_CORAL_VIOLET: target_st = st_violet emit_type = 2 elif block_id == BlockDatabase.BlockType.LAVA_VENT: target_st = st_vent emit_type = 3 var added: bool = false if not _is_opaque_at(x, y + 1, z): _add_face_top(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if not _is_opaque_at(x, y - 1, z): _add_face_bottom(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if not _is_opaque_at(x + 1, y, z): _add_face_right(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if not _is_opaque_at(x - 1, y, z): _add_face_left(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if not _is_opaque_at(x, y, z + 1): _add_face_front(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if not _is_opaque_at(x, y, z - 1): _add_face_back(target_st, x, y, z, uv_tl, tile_w, tile_h) added = true if added: match emit_type: 0: has_faces = true 1: has_cyan = true 2: has_violet = true 3: has_vent = true var mesh: ArrayMesh = ArrayMesh.new() if has_faces: st.generate_normals() mesh = st.commit() # surface 0 — will apply material via surface_set_material below if has_cyan: st_cyan.generate_normals() st_cyan.commit(mesh) if has_violet: st_violet.generate_normals() st_violet.commit(mesh) if has_vent: st_vent.generate_normals() st_vent.commit(mesh) if mesh.get_surface_count() == 0: return # Assign materials per surface var surf_idx: int = 0 if has_faces: mesh.surface_set_material(surf_idx, shared_material) surf_idx += 1 if has_cyan: mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(0.2, 0.8, 1.0), 3.0)) surf_idx += 1 if has_violet: mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(0.7, 0.3, 0.9), 3.0)) surf_idx += 1 if has_vent: mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(1.0, 0.5, 0.1), 3.0)) _mesh_instance = MeshInstance3D.new() _mesh_instance.mesh = mesh add_child(_mesh_instance) var shape: ConcavePolygonShape3D = ConcavePolygonShape3D.new() shape.set_faces(mesh.get_faces()) var col_shape: CollisionShape3D = CollisionShape3D.new() col_shape.shape = shape _static_body = StaticBody3D.new() _static_body.add_child(col_shape) add_child(_static_body) func _make_emissive_mat(emission_color: Color, energy: float) -> StandardMaterial3D: var mat: StandardMaterial3D = StandardMaterial3D.new() mat.albedo_color = emission_color mat.emission_enabled = true mat.emission = emission_color mat.emission_energy_multiplier = energy return mat func _is_opaque_at(x: int, y: int, z: int) -> bool: if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE: return false return BlockDatabase.is_solid(get_block(x, y, z)) # UV layout for a quad: TL, TR, BR, BL # Face vertices are added as two triangles: TL-TR-BR and TL-BR-BL func _add_face_top(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) # tri 1 st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy + 1.0, fz)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz + 1.0)) # tri 2 st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy + 1.0, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz + 1.0)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz + 1.0)) func _add_face_bottom(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx + 1.0, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy, fz)) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy, fz)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx, fy, fz)) func _add_face_right(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) st.set_uv(uv_tl) st.add_vertex(Vector3(fx + 1.0, fy, fz)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx + 1.0, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz + 1.0)) st.set_uv(uv_tl) st.add_vertex(Vector3(fx + 1.0, fy, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz + 1.0)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz)) func _add_face_left(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx, fy, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz)) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz + 1.0)) func _add_face_front(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) st.set_uv(uv_tl) st.add_vertex(Vector3(fx + 1.0, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz + 1.0)) st.set_uv(uv_tl) st.add_vertex(Vector3(fx + 1.0, fy, fz + 1.0)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz + 1.0)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz + 1.0)) func _add_face_back(st: SurfaceTool, x: int, y: int, z: int, uv_tl: Vector2, tw: float, th: float) -> void: var fx: float = float(x) var fy: float = float(y) var fz: float = float(z) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz)) st.set_uv(uv_tl + Vector2(tw, 0.0)) st.add_vertex(Vector3(fx + 1.0, fy, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz)) st.set_uv(uv_tl) st.add_vertex(Vector3(fx, fy, fz)) st.set_uv(uv_tl + Vector2(tw, th)) st.add_vertex(Vector3(fx + 1.0, fy + 1.0, fz)) st.set_uv(uv_tl + Vector2(0.0, th)) st.add_vertex(Vector3(fx, fy + 1.0, fz))