Skip to content

Commit d169ec1

Browse files
authored
Fix block face checks and explicitly define normals in voxel demo (#1182)
1 parent fad2469 commit d169ec1

File tree

7 files changed

+87
-60
lines changed

7 files changed

+87
-60
lines changed

3d/voxel/menu/debug.gd

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func _process(_delta: float) -> void:
1212
text += "\nEffective render distance: " + str(voxel_world.effective_render_distance)
1313
text += "\nLooking: " + _cardinal_string_from_radians(player.transform.basis.get_euler().y)
1414
text += "\nMemory: " + "%3.0f" % (OS.get_static_memory_usage() / 1048576.0) + " MiB"
15-
text += "\nFPS: " + str(Engine.get_frames_per_second())
15+
text += "\nFPS: " + String.num_uint64(Engine.get_frames_per_second())
1616

1717

1818
# Avoids the problem of showing more digits than needed or available.

3d/voxel/menu/options/option_buttons.gd

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ extends Control
77

88
func _ready() -> void:
99
render_distance_slider.value = Settings.render_distance
10-
render_distance_label.text = "Render distance: " + str(Settings.render_distance)
10+
render_distance_label.text = "Render distance: " + String.num_int64(Settings.render_distance)
1111
fog_checkbox.button_pressed = Settings.fog_enabled
1212

1313

1414
func _on_RenderDistanceSlider_value_changed(value: float) -> void:
1515
Settings.render_distance = int(value)
16-
render_distance_label.text = "Render distance: " + str(value)
16+
render_distance_label.text = "Render distance: " + String.num_int64(Settings.render_distance)
1717
Settings.save_settings()
1818

1919

3d/voxel/player/player.tscn

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[gd_scene load_steps=6 format=3 uid="uid://1s4asqpay67m"]
22

3-
[ext_resource type="Script" path="res://player/player.gd" id="1"]
3+
[ext_resource type="Script" uid="uid://rm45k07vw817" path="res://player/player.gd" id="1"]
44
[ext_resource type="Texture2D" uid="uid://d3f34krqfgdjd" path="res://world/textures/texture_sheet.png" id="2"]
55

66
[sub_resource type="CylinderShape3D" id="1"]

3d/voxel/project.godot

-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ pick_block={
147147
[physics]
148148

149149
common/physics_ticks_per_second=120
150-
3d/physics_engine="Bullet"
151150
3d/default_gravity=20.0
152151

153152
[rendering]

3d/voxel/settings.gd

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
extends Node
22

3-
var render_distance := 7
4-
var fog_enabled := true
3+
var render_distance: int = 7
4+
var fog_enabled: bool = true
55

6-
var fog_distance := 32.0 # Not saved, only used during runtime.
7-
var world_type := 0 # Not saved, only used during runtime.
6+
var fog_distance: float = 32.0 # Not saved, only used during runtime.
7+
var world_type: int = 0 # Not saved, only used during runtime.
88

9-
var _save_path := "user://settings.json"
9+
var _save_path: String = "user://settings.json"
1010

1111
func _enter_tree() -> void:
1212
if FileAccess.file_exists(_save_path):

3d/voxel/world/chunk.gd

+66-43
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ const TEXTURE_SHEET_WIDTH = 8
99

1010
const CHUNK_LAST_INDEX = CHUNK_SIZE - 1
1111
const TEXTURE_TILE_SIZE = 1.0 / TEXTURE_SHEET_WIDTH
12+
const DIRECTIONS: Array[Vector3i] = [Vector3i.LEFT, Vector3i.RIGHT, Vector3i.DOWN, Vector3i.UP, Vector3i.FORWARD, Vector3i.BACK]
1213

1314
var data := {}
1415
var chunk_position := Vector3i()
16+
var is_initial_mesh_generated: bool = false
1517

1618
var _thread: Thread
1719

1820
@onready var voxel_world := get_parent()
1921

22+
2023
func _ready() -> void:
2124
transform.origin = Vector3(chunk_position * CHUNK_SIZE)
2225
name = str(chunk_position)
@@ -27,7 +30,14 @@ func _ready() -> void:
2730

2831
# We can only add colliders in the main thread due to physics limitations.
2932
_generate_chunk_collider()
30-
# However, we can use a thread for mesh generation.
33+
34+
35+
func try_initial_generate_mesh(all_chunks: Dictionary[Vector3i, Chunk]) -> void:
36+
# We can use a thread for mesh generation.
37+
for dir in DIRECTIONS:
38+
if not all_chunks.has(chunk_position + dir):
39+
return
40+
is_initial_mesh_generated = true
3141
_thread = Thread.new()
3242
_thread.start(_generate_chunk_mesh)
3343

@@ -73,7 +83,6 @@ func _generate_chunk_mesh() -> void:
7383
_draw_block_mesh(surface_tool, block_position, block_id)
7484

7585
# Create the chunk's mesh from the SurfaceTool data.
76-
surface_tool.generate_normals()
7786
surface_tool.generate_tangents()
7887
surface_tool.index()
7988
var array_mesh := surface_tool.commit()
@@ -91,10 +100,10 @@ func _draw_block_mesh(surface_tool: SurfaceTool, block_sub_position: Vector3i, b
91100

92101
# Bush blocks get drawn in their own special way.
93102
if block_id == 27 or block_id == 28:
94-
_draw_block_face(surface_tool, [verts[2], verts[0], verts[7], verts[5]], uvs)
95-
_draw_block_face(surface_tool, [verts[7], verts[5], verts[2], verts[0]], uvs)
96-
_draw_block_face(surface_tool, [verts[3], verts[1], verts[6], verts[4]], uvs)
97-
_draw_block_face(surface_tool, [verts[6], verts[4], verts[3], verts[1]], uvs)
103+
_draw_block_face(surface_tool, [verts[2], verts[0], verts[7], verts[5]], uvs, Vector3(-1, 0, 1).normalized())
104+
_draw_block_face(surface_tool, [verts[7], verts[5], verts[2], verts[0]], uvs, Vector3(1, 0, -1).normalized())
105+
_draw_block_face(surface_tool, [verts[3], verts[1], verts[6], verts[4]], uvs, Vector3(1, 0, 1).normalized())
106+
_draw_block_face(surface_tool, [verts[6], verts[4], verts[3], verts[1]], uvs, Vector3(-1, 0, -1).normalized())
98107
return
99108

100109
# 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
112121
bottom_uvs = top_uvs
113122

114123
# Main rendering code for normal blocks.
115-
var other_block_position := block_sub_position + Vector3i.LEFT
124+
#var other_block_position := block_sub_position
116125
var other_block_id := 0
117-
if other_block_position.x == -1:
118-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
119-
elif data.has(other_block_position):
120-
other_block_id = data[other_block_position]
126+
if block_sub_position.x == 0:
127+
var other_sub_pos: Vector3i = Vector3i(15, block_sub_position.y, block_sub_position.z)
128+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.LEFT, other_sub_pos)
129+
else:
130+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.LEFT
131+
if data.has(other_block_sub_pos):
132+
other_block_id = data[other_block_sub_pos]
121133
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
122-
_draw_block_face(surface_tool, [verts[2], verts[0], verts[3], verts[1]], uvs)
134+
_draw_block_face(surface_tool, [verts[2], verts[0], verts[3], verts[1]], uvs, Vector3.LEFT)
123135

124-
other_block_position = block_sub_position + Vector3i.RIGHT
125136
other_block_id = 0
126-
if other_block_position.x == CHUNK_SIZE:
127-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
128-
elif data.has(other_block_position):
129-
other_block_id = data[other_block_position]
137+
if block_sub_position.x == CHUNK_SIZE - 1:
138+
var other_sub_pos: Vector3i = Vector3i(0, block_sub_position.y, block_sub_position.z)
139+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.RIGHT, other_sub_pos)
140+
else:
141+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.RIGHT
142+
if data.has(other_block_sub_pos):
143+
other_block_id = data[other_block_sub_pos]
130144
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
131-
_draw_block_face(surface_tool, [verts[7], verts[5], verts[6], verts[4]], uvs)
145+
_draw_block_face(surface_tool, [verts[7], verts[5], verts[6], verts[4]], uvs, Vector3.RIGHT)
132146

133-
other_block_position = block_sub_position + Vector3i.FORWARD
134147
other_block_id = 0
135-
if other_block_position.z == -1:
136-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
137-
elif data.has(other_block_position):
138-
other_block_id = data[other_block_position]
148+
if block_sub_position.z == 0:
149+
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, block_sub_position.y, CHUNK_SIZE - 1)
150+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.FORWARD, other_sub_pos)
151+
else:
152+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.FORWARD
153+
if data.has(other_block_sub_pos):
154+
other_block_id = data[other_block_sub_pos]
139155
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
140-
_draw_block_face(surface_tool, [verts[6], verts[4], verts[2], verts[0]], uvs)
156+
_draw_block_face(surface_tool, [verts[6], verts[4], verts[2], verts[0]], uvs, Vector3.FORWARD)
141157

142-
other_block_position = block_sub_position + Vector3i.BACK
143158
other_block_id = 0
144-
if other_block_position.z == CHUNK_SIZE:
145-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
146-
elif data.has(other_block_position):
147-
other_block_id = data[other_block_position]
159+
if block_sub_position.z == CHUNK_SIZE - 1:
160+
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, block_sub_position.y, 0)
161+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.BACK, other_sub_pos)
162+
else:
163+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.BACK
164+
if data.has(other_block_sub_pos):
165+
other_block_id = data[other_block_sub_pos]
148166
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
149-
_draw_block_face(surface_tool, [verts[3], verts[1], verts[7], verts[5]], uvs)
167+
_draw_block_face(surface_tool, [verts[3], verts[1], verts[7], verts[5]], uvs, Vector3.BACK)
150168

151-
other_block_position = block_sub_position + Vector3i.DOWN
152169
other_block_id = 0
153-
if other_block_position.y == -1:
154-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
155-
elif data.has(other_block_position):
156-
other_block_id = data[other_block_position]
170+
if block_sub_position.y == 0:
171+
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, CHUNK_SIZE - 1, block_sub_position.z)
172+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.DOWN, other_sub_pos)
173+
else:
174+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.DOWN
175+
if data.has(other_block_sub_pos):
176+
other_block_id = data[other_block_sub_pos]
157177
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
158-
_draw_block_face(surface_tool, [verts[4], verts[5], verts[0], verts[1]], bottom_uvs)
178+
_draw_block_face(surface_tool, [verts[4], verts[5], verts[0], verts[1]], bottom_uvs, Vector3.DOWN)
159179

160-
other_block_position = block_sub_position + Vector3i.UP
161180
other_block_id = 0
162-
if other_block_position.y == CHUNK_SIZE:
163-
other_block_id = voxel_world.get_block_global_position(other_block_position + chunk_position * CHUNK_SIZE)
164-
elif data.has(other_block_position):
165-
other_block_id = data[other_block_position]
181+
if block_sub_position.y == CHUNK_SIZE - 1:
182+
var other_sub_pos: Vector3i = Vector3i(block_sub_position.x, 0, block_sub_position.z)
183+
other_block_id = voxel_world.get_block_in_chunk(chunk_position + Vector3i.UP, other_sub_pos)
184+
else:
185+
var other_block_sub_pos: Vector3i = block_sub_position + Vector3i.UP
186+
if data.has(other_block_sub_pos):
187+
other_block_id = data[other_block_sub_pos]
166188
if block_id != other_block_id and Chunk.is_block_transparent(other_block_id):
167-
_draw_block_face(surface_tool, [verts[2], verts[3], verts[6], verts[7]], top_uvs)
189+
_draw_block_face(surface_tool, [verts[2], verts[3], verts[6], verts[7]], top_uvs, Vector3.UP)
168190

169191

170-
func _draw_block_face(surface_tool: SurfaceTool, verts: Array[Vector3], uvs: Array[Vector2]) -> void:
192+
func _draw_block_face(surface_tool: SurfaceTool, verts: Array[Vector3], uvs: Array[Vector2], normal: Vector3) -> void:
193+
surface_tool.set_normal(normal)
171194
surface_tool.set_uv(uvs[1]); surface_tool.add_vertex(verts[1])
172195
surface_tool.set_uv(uvs[2]); surface_tool.add_vertex(verts[2])
173196
surface_tool.set_uv(uvs[3]); surface_tool.add_vertex(verts[3])

3d/voxel/world/voxel_world.gd

+12-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extends Node
33

44
const CHUNK_MIDPOINT = Vector3(0.5, 0.5, 0.5) * Chunk.CHUNK_SIZE
55
const CHUNK_END_SIZE = Chunk.CHUNK_SIZE - 1
6+
const DIRECTIONS: Array[Vector3i] = [Vector3i.LEFT, Vector3i.RIGHT, Vector3i.DOWN, Vector3i.UP, Vector3i.FORWARD, Vector3i.BACK]
67

78
var render_distance: int:
89
set(value):
@@ -16,7 +17,7 @@ var _old_player_chunk := Vector3i()
1617
var _generating := true
1718
var _deleting := false
1819

19-
var _chunks := {}
20+
var _chunks: Dictionary[Vector3i, Chunk] = {}
2021

2122
@onready var player: CharacterBody3D = $"../Player"
2223

@@ -50,6 +51,13 @@ func _process(_delta: float) -> void:
5051
chunk.chunk_position = chunk_position
5152
_chunks[chunk_position] = chunk
5253
add_child(chunk)
54+
chunk.try_initial_generate_mesh(_chunks)
55+
for dir in DIRECTIONS:
56+
var neighbor: Chunk = _chunks.get(chunk_position + dir)
57+
if neighbor != null and not neighbor.is_initial_mesh_generated:
58+
neighbor.try_initial_generate_mesh(_chunks)
59+
# Generate at most one chunk per frame in terms of data/colliders.
60+
# Mesh generation is threaded so it's ok that the above may generate multiple meshes.
5361
return
5462

5563
# If we didn't generate any chunks (and therefore didn't return), what next?
@@ -61,14 +69,11 @@ func _process(_delta: float) -> void:
6169
_generating = false
6270

6371

64-
func get_block_global_position(block_global_position: Vector3i) -> int:
65-
var chunk_position := Vector3i((block_global_position / Chunk.CHUNK_SIZE))
72+
func get_block_in_chunk(chunk_position: Vector3i, block_sub_position: Vector3i) -> int:
6673
if _chunks.has(chunk_position):
6774
var chunk: Chunk = _chunks[chunk_position]
68-
var sub_position := Vector3i(Vector3(block_global_position).posmod(Chunk.CHUNK_SIZE))
69-
if chunk.data.has(sub_position):
70-
return chunk.data[sub_position]
71-
75+
if chunk.data.has(block_sub_position):
76+
return chunk.data[block_sub_position]
7277
return 0
7378

7479

0 commit comments

Comments
 (0)