Fix block face checks and explicitly define normals in voxel demo (#1182)

This commit is contained in:
Aaron Franke
2025-04-02 10:41:10 -05:00
committed by GitHub
parent fad2469531
commit d169ec17e2
7 changed files with 87 additions and 60 deletions

View File

@@ -12,7 +12,7 @@ func _process(_delta: float) -> void:
text += "\nEffective render distance: " + str(voxel_world.effective_render_distance)
text += "\nLooking: " + _cardinal_string_from_radians(player.transform.basis.get_euler().y)
text += "\nMemory: " + "%3.0f" % (OS.get_static_memory_usage() / 1048576.0) + " MiB"
text += "\nFPS: " + str(Engine.get_frames_per_second())
text += "\nFPS: " + String.num_uint64(Engine.get_frames_per_second())
# Avoids the problem of showing more digits than needed or available.

View File

@@ -7,13 +7,13 @@ extends Control
func _ready() -> void:
render_distance_slider.value = Settings.render_distance
render_distance_label.text = "Render distance: " + str(Settings.render_distance)
render_distance_label.text = "Render distance: " + String.num_int64(Settings.render_distance)
fog_checkbox.button_pressed = Settings.fog_enabled
func _on_RenderDistanceSlider_value_changed(value: float) -> void:
Settings.render_distance = int(value)
render_distance_label.text = "Render distance: " + str(value)
render_distance_label.text = "Render distance: " + String.num_int64(Settings.render_distance)
Settings.save_settings()

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=6 format=3 uid="uid://1s4asqpay67m"]
[ext_resource type="Script" path="res://player/player.gd" id="1"]
[ext_resource type="Script" uid="uid://rm45k07vw817" path="res://player/player.gd" id="1"]
[ext_resource type="Texture2D" uid="uid://d3f34krqfgdjd" path="res://world/textures/texture_sheet.png" id="2"]
[sub_resource type="CylinderShape3D" id="1"]

View File

@@ -147,7 +147,6 @@ pick_block={
[physics]
common/physics_ticks_per_second=120
3d/physics_engine="Bullet"
3d/default_gravity=20.0
[rendering]

View File

@@ -1,12 +1,12 @@
extends Node
var render_distance := 7
var fog_enabled := true
var render_distance: int = 7
var fog_enabled: bool = true
var fog_distance := 32.0 # Not saved, only used during runtime.
var world_type := 0 # Not saved, only used during runtime.
var fog_distance: float = 32.0 # Not saved, only used during runtime.
var world_type: int = 0 # Not saved, only used during runtime.
var _save_path := "user://settings.json"
var _save_path: String = "user://settings.json"
func _enter_tree() -> void:
if FileAccess.file_exists(_save_path):

View File

@@ -9,14 +9,17 @@ const TEXTURE_SHEET_WIDTH = 8
const CHUNK_LAST_INDEX = CHUNK_SIZE - 1
const TEXTURE_TILE_SIZE = 1.0 / TEXTURE_SHEET_WIDTH
const DIRECTIONS: Array[Vector3i] = [Vector3i.LEFT, Vector3i.RIGHT, Vector3i.DOWN, Vector3i.UP, Vector3i.FORWARD, Vector3i.BACK]
var data := {}
var chunk_position := Vector3i()
var is_initial_mesh_generated: bool = false
var _thread: Thread
@onready var voxel_world := get_parent()
func _ready() -> void:
transform.origin = Vector3(chunk_position * CHUNK_SIZE)
name = str(chunk_position)
@@ -27,7 +30,14 @@ func _ready() -> void:
# We can only add colliders in the main thread due to physics limitations.
_generate_chunk_collider()
# However, we can use a thread for mesh generation.
func try_initial_generate_mesh(all_chunks: Dictionary[Vector3i, Chunk]) -> void:
# We can use a thread for mesh generation.
for dir in DIRECTIONS:
if not all_chunks.has(chunk_position + dir):
return
is_initial_mesh_generated = true
_thread = Thread.new()
_thread.start(_generate_chunk_mesh)
@@ -73,7 +83,6 @@ func _generate_chunk_mesh() -> void:
_draw_block_mesh(surface_tool, block_position, block_id)
# Create the chunk's mesh from the SurfaceTool data.
surface_tool.generate_normals()
surface_tool.generate_tangents()
surface_tool.index()
var array_mesh := surface_tool.commit()
@@ -91,10 +100,10 @@ func _draw_block_mesh(surface_tool: SurfaceTool, block_sub_position: Vector3i, b
# Bush blocks get drawn in their own special way.
if block_id == 27 or block_id == 28:
_draw_block_face(surface_tool, [verts[2], verts[0], verts[7], verts[5]], uvs)
_draw_block_face(surface_tool, [verts[7], verts[5], verts[2], verts[0]], uvs)
_draw_block_face(surface_tool, [verts[3], verts[1], verts[6], verts[4]], uvs)
_draw_block_face(surface_tool, [verts[6], verts[4], verts[3], verts[1]], uvs)
_draw_block_face(surface_tool, [verts[2], verts[0], verts[7], verts[5]], uvs, Vector3(-1, 0, 1).normalized())
_draw_block_face(surface_tool, [verts[7], verts[5], verts[2], verts[0]], uvs, Vector3(1, 0, -1).normalized())
_draw_block_face(surface_tool, [verts[3], verts[1], verts[6], verts[4]], uvs, Vector3(1, 0, 1).normalized())
_draw_block_face(surface_tool, [verts[6], verts[4], verts[3], verts[1]], uvs, Vector3(-1, 0, -1).normalized())
return
# Allow some blocks to have different top/bottom textures.
@@ -112,62 +121,76 @@ func _draw_block_mesh(surface_tool: SurfaceTool, block_sub_position: Vector3i, b
bottom_uvs = top_uvs
# Main rendering code for normal blocks.
var other_block_position := block_sub_position + Vector3i.LEFT
#var other_block_position := block_sub_position
var other_block_id := 0
if other_block_position.x == -1:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.x == 0:
var other_sub_pos: Vector3i = Vector3i(15, block_sub_position.y, block_sub_position.z)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.LEFT, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.LEFT
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[2], verts[0], verts[3], verts[1]], uvs)
_draw_block_face(surface_tool, [verts[2], verts[0], verts[3], verts[1]], uvs, Vector3.LEFT)
other_block_position = block_sub_position + Vector3i.RIGHT
other_block_id = 0
if other_block_position.x == CHUNK_SIZE:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.x == CHUNK_SIZE - 1:
var other_sub_pos: Vector3i = Vector3i(0, block_sub_position.y, block_sub_position.z)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.RIGHT, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.RIGHT
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[7], verts[5], verts[6], verts[4]], uvs)
_draw_block_face(surface_tool, [verts[7], verts[5], verts[6], verts[4]], uvs, Vector3.RIGHT)
other_block_position = block_sub_position + Vector3i.FORWARD
other_block_id = 0
if other_block_position.z == -1:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.z == 0:
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, block_sub_position.y, CHUNK_SIZE - 1)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.FORWARD, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.FORWARD
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[6], verts[4], verts[2], verts[0]], uvs)
_draw_block_face(surface_tool, [verts[6], verts[4], verts[2], verts[0]], uvs, Vector3.FORWARD)
other_block_position = block_sub_position + Vector3i.BACK
other_block_id = 0
if other_block_position.z == CHUNK_SIZE:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.z == CHUNK_SIZE - 1:
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, block_sub_position.y, 0)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.BACK, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.BACK
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[3], verts[1], verts[7], verts[5]], uvs)
_draw_block_face(surface_tool, [verts[3], verts[1], verts[7], verts[5]], uvs, Vector3.BACK)
other_block_position = block_sub_position + Vector3i.DOWN
other_block_id = 0
if other_block_position.y == -1:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.y == 0:
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, CHUNK_SIZE - 1, block_sub_position.z)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.DOWN, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.DOWN
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[4], verts[5], verts[0], verts[1]], bottom_uvs)
_draw_block_face(surface_tool, [verts[4], verts[5], verts[0], verts[1]], bottom_uvs, Vector3.DOWN)
other_block_position = block_sub_position + Vector3i.UP
other_block_id = 0
if other_block_position.y == CHUNK_SIZE:
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
elif data.has(other_block_position):
other_block_id = data[other_block_position]
if block_sub_position.y == CHUNK_SIZE - 1:
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, 0, block_sub_position.z)
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.UP, other_sub_pos)
else:
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.UP
if data.has(other_block_sub_pos):
other_block_id = data[other_block_sub_pos]
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
_draw_block_face(surface_tool, [verts[2], verts[3], verts[6], verts[7]], top_uvs)
_draw_block_face(surface_tool, [verts[2], verts[3], verts[6], verts[7]], top_uvs, Vector3.UP)
func _draw_block_face(surface_tool: SurfaceTool, verts: Array[Vector3], uvs: Array[Vector2]) -> void:
func _draw_block_face(surface_tool: SurfaceTool, verts: Array[Vector3], uvs: Array[Vector2], normal: Vector3) -> void:
surface_tool.set_normal(normal)
surface_tool.set_uv(uvs[1]); surface_tool.add_vertex(verts[1])
surface_tool.set_uv(uvs[2]); surface_tool.add_vertex(verts[2])
surface_tool.set_uv(uvs[3]); surface_tool.add_vertex(verts[3])

View File

@@ -3,6 +3,7 @@ extends Node
const CHUNK_MIDPOINT = Vector3(0.5, 0.5, 0.5) * Chunk.CHUNK_SIZE
const CHUNK_END_SIZE = Chunk.CHUNK_SIZE - 1
const DIRECTIONS: Array[Vector3i] = [Vector3i.LEFT, Vector3i.RIGHT, Vector3i.DOWN, Vector3i.UP, Vector3i.FORWARD, Vector3i.BACK]
var render_distance: int:
set(value):
@@ -16,7 +17,7 @@ var _old_player_chunk := Vector3i()
var _generating := true
var _deleting := false
var _chunks := {}
var _chunks: Dictionary[Vector3i, Chunk] = {}
@onready var player: CharacterBody3D = $"../Player"
@@ -50,6 +51,13 @@ func _process(_delta: float) -> void:
chunk.chunk_position = chunk_position
_chunks[chunk_position] = chunk
add_child(chunk)
chunk.try_initial_generate_mesh(_chunks)
for dir in DIRECTIONS:
var neighbor: Chunk = _chunks.get(chunk_position + dir)
if neighbor != null and not neighbor.is_initial_mesh_generated:
neighbor.try_initial_generate_mesh(_chunks)
# Generate at most one chunk per frame in terms of data/colliders.
# Mesh generation is threaded so it's ok that the above may generate multiple meshes.
return
# If we didn't generate any chunks (and therefore didn't return), what next?
@@ -61,14 +69,11 @@ func _process(_delta: float) -> void:
_generating = false
func get_block_global_position(block_global_position: Vector3i) -> int:
var chunk_position := Vector3i((block_global_position / Chunk.CHUNK_SIZE))
func get_block_in_chunk(chunk_position: Vector3i, block_sub_position: Vector3i) -> int:
if _chunks.has(chunk_position):
var chunk: Chunk = _chunks[chunk_position]
var sub_position := Vector3i(Vector3(block_global_position).posmod(Chunk.CHUNK_SIZE))
if chunk.data.has(sub_position):
return chunk.data[sub_position]
if chunk.data.has(block_sub_position):
return chunk.data[block_sub_position]
return 0