Skip to content

Commit 518a515

Browse files
committed
feat: multithread view frustum culling
1 parent e2584d1 commit 518a515

File tree

6 files changed

+96
-53
lines changed

6 files changed

+96
-53
lines changed

src/chunk.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct Chunk {
3535
pub chunk_index_buffer: Option<wgpu::Buffer>,
3636
pub chunk_vertex_buffer: Option<wgpu::Buffer>,
3737
pub outside_blocks: Vec<Arc<RwLock<Block>>>,
38+
pub visible: bool,
3839
}
3940

4041
impl Chunk {
@@ -322,7 +323,8 @@ impl Chunk {
322323
}
323324
// https://www.lighthouse3d.com/tutorials/view-frustum-culling/
324325
// Note: we don't compute the top and bottom planes, only far,near,right,left
325-
pub fn is_visible(&self, player: &Player) -> bool {
326+
pub fn is_visible(&self, player: Arc<RwLock<Player>>) -> bool {
327+
let player = player.read().unwrap();
326328
let forward = player.camera.get_forward_dir();
327329
let right = player.camera.get_right_dir();
328330
let halfvside = player.camera.zfar / f32::tan(player.camera.fovy / 2.0);
@@ -423,6 +425,7 @@ impl Chunk {
423425
chunk_position_buffer,
424426
indices: 0,
425427
outside_blocks: vec![],
428+
visible: true,
426429
};
427430

428431
if !was_loaded {

src/pipeline.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ impl Pipeline {
4747
label: None,
4848
source: wgpu::ShaderSource::Wgsl(shader_source.into()),
4949
});
50-
51-
let uniforms = Uniforms::from(&state.player.camera);
50+
let camera = &state.player.read().unwrap().camera;
51+
let uniforms = Uniforms::from(camera);
5252

5353
let projection_buffer =
5454
state

src/player.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ impl Player {
4949
// Position relative to the chunk
5050
pub fn to_relative_position(&self) -> glam::Vec3 {
5151
todo!();
52-
// glam::vec3(
53-
// f32::abs(self.camera.eye.x + (CHUNK_SIZE as f32 - 1.0) % CHUNK_SIZE as f32),
54-
// self.camera.eye.y,
55-
// f32::abs(self.camera.eye.z + (CHUNK_SIZE as f32 - 1.0) % CHUNK_SIZE as f32),
56-
// )
5752
}
5853
pub fn get_collision(&self) -> crate::collision::CollisionBox {
5954
crate::collision::CollisionBox::new(
@@ -229,16 +224,16 @@ pub struct Camera {
229224

230225
impl Camera {
231226
pub fn new(surface_width: f32, surface_height: f32) -> Camera {
232-
let eye = if let Ok(eye) = Camera::load(Box::new(())) {
233-
eye
227+
let (eye, yaw, pitch) = if let Ok((eye, yaw, pitch)) = Camera::load(Box::new(())) {
228+
(eye, yaw, pitch)
234229
} else {
235-
glam::vec3(-4.0, 50.0, 4.0)
230+
(glam::vec3(-4.0, 50.0, 4.0), consts::FRAC_PI_2, 0.0)
236231
};
237232
Self {
238233
aspect_ratio: surface_width / surface_height,
239234
eye,
240-
yaw: consts::FRAC_PI_2,
241-
pitch: 0.0,
235+
yaw,
236+
pitch,
242237

243238
fovy: consts::FRAC_PI_4,
244239
znear: 0.1,
@@ -280,7 +275,10 @@ impl Saveable<glam::Vec3> for Camera {
280275
if let Ok(_) = std::fs::create_dir("data") {
281276
println!("Created dir");
282277
}
283-
let data = format!("{},{},{}", self.eye.x, self.eye.y, self.eye.z);
278+
let data = format!(
279+
"{},{},{},{},{}",
280+
self.eye.x, self.eye.y, self.eye.z, self.yaw, self.pitch
281+
);
284282

285283
let player_file_name = "data/player";
286284
std::fs::write(player_file_name, data.as_bytes())?;
@@ -289,14 +287,16 @@ impl Saveable<glam::Vec3> for Camera {
289287
}
290288
}
291289

292-
impl Loadable<glam::Vec3> for Camera {
293-
fn load(_: Box<dyn Any>) -> Result<Vec3, Box<dyn Error>> {
290+
impl Loadable<(glam::Vec3, f32, f32)> for Camera {
291+
fn load(_: Box<dyn Any>) -> Result<((Vec3, f32, f32)), Box<dyn Error>> {
294292
let data = String::from_utf8(std::fs::read("data/player")?)?;
295293
let mut data = data.split(",");
296294
let x = data.next().unwrap().parse::<f32>().unwrap();
297295
let y = data.next().unwrap().parse::<f32>().unwrap();
298296
let z = data.next().unwrap().parse::<f32>().unwrap();
297+
let yaw = data.next().unwrap().parse::<f32>().unwrap();
298+
let pitch = data.next().unwrap().parse::<f32>().unwrap();
299299

300-
return Ok(glam::vec3(x, y, z));
300+
return Ok((glam::vec3(x, y, z), yaw, pitch));
301301
}
302302
}

src/state.rs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl State {
6767
};
6868

6969
let camera = Camera::new(surface_config.width as f32, surface_config.height as f32);
70-
let player = Player {
70+
let player = Arc::new(RwLock::new(Player {
7171
camera,
7272
current_chunk: (0, 0),
7373
is_jumping: false,
@@ -76,7 +76,7 @@ impl State {
7676
facing_face: None,
7777
jump_action_start: None,
7878
is_ghost: false,
79-
};
79+
}));
8080

8181
surface.configure(&device, &surface_config);
8282
let config = Config {
@@ -113,16 +113,21 @@ impl State {
113113
}
114114
pub fn save_state(&mut self) {
115115
self.player
116+
.read()
117+
.unwrap()
116118
.camera
117119
.save()
118120
.expect("Failed to save camera state");
119121
self.world.save_state();
120122
}
121123
pub fn dispose(&mut self) {
122124
self.world.dispose();
125+
self.device.destroy();
126+
std::mem::drop(self.queue.to_owned());
123127
}
124128
pub fn handle_keypress(&mut self, event: KeyEvent, delta_time: f32) {
125129
let is_pressed: f32 = if event.state.is_pressed() { 1. } else { 0. };
130+
let mut player = self.player.write().unwrap();
126131

127132
match event {
128133
KeyEvent {
@@ -163,17 +168,17 @@ impl State {
163168
state: winit::event::ElementState::Pressed,
164169
..
165170
} => {
166-
if self.player.on_ground {
167-
self.player.is_jumping = true;
168-
self.player.jump_action_start = Some(std::time::Instant::now());
171+
if player.on_ground {
172+
player.is_jumping = true;
173+
player.jump_action_start = Some(std::time::Instant::now());
169174
}
170175
}
171176
KeyEvent {
172177
physical_key: PhysicalKey::Code(KeyCode::KeyG),
173178
state: winit::event::ElementState::Pressed,
174179
..
175180
} => {
176-
self.player.is_ghost = !self.player.is_ghost;
181+
player.is_ghost = !player.is_ghost;
177182
}
178183
KeyEvent {
179184
physical_key: PhysicalKey::Code(KeyCode::KeyF),
@@ -190,9 +195,9 @@ impl State {
190195
}
191196
}
192197
pub fn on_click(&mut self, button: MouseButton) {
193-
if let Some(facing_block) = self.player.facing_block.as_ref() {
194-
let facing_face = self
195-
.player
198+
let player = self.player.read().unwrap();
199+
if let Some(facing_block) = player.facing_block.as_ref() {
200+
let facing_face = player
196201
.facing_face
197202
.expect("Cannot be not facing a face if it's facing a block");
198203
match button {
@@ -217,7 +222,7 @@ impl State {
217222
}
218223
}
219224
pub fn handle_mouse(&mut self, delta: &glam::Vec2) {
220-
self.player.camera.move_target(delta)
225+
self.player.write().unwrap().camera.move_target(delta)
221226
}
222227

223228
pub fn resize(&mut self, new_size: PhysicalSize<u32>) {
@@ -231,7 +236,8 @@ impl State {
231236
}
232237
pub fn update(&mut self, delta_time: f32, total_time: f32) {
233238
let mut collisions = vec![];
234-
if let Some(nearby_blocks) = self.world.get_blocks_nearby(&self.player) {
239+
240+
if let Some(nearby_blocks) = self.world.get_blocks_nearby(Arc::clone(&self.player)) {
235241
for block in nearby_blocks.iter() {
236242
let block = block.read().unwrap();
237243
let collision = CollisionBox::from_block_position(
@@ -242,22 +248,23 @@ impl State {
242248
collisions.push(collision);
243249
}
244250
};
245-
self.player.move_camera(
251+
let mut player = self.player.write().unwrap();
252+
player.move_camera(
246253
&self.camera_controller.movement_vector,
247254
delta_time,
248255
&collisions,
249256
);
250-
if let Some((block, face_dir)) = self.player.get_facing_block(&collisions) {
257+
if let Some((block, face_dir)) = player.get_facing_block(&collisions) {
251258
let block = self.world.get_blocks_absolute(&block.to_block_position());
252259

253-
self.player.facing_block = block;
254-
self.player.facing_face = Some(face_dir);
260+
player.facing_block = block;
261+
player.facing_face = Some(face_dir);
255262
} else {
256-
self.player.facing_block = None;
257-
self.player.facing_face = None;
263+
player.facing_block = None;
264+
player.facing_face = None;
258265
}
259266

260-
let uniforms = Uniforms::from(&self.player.camera);
267+
let uniforms = Uniforms::from(&player.camera);
261268

262269
for pipeline in self.pipelines.iter() {
263270
self.queue.write_buffer(
@@ -266,14 +273,16 @@ impl State {
266273
bytemuck::cast_slice(&[uniforms.view]),
267274
)
268275
}
276+
// Drop write lock
277+
std::mem::drop(player);
269278

270279
self.world.update(
271-
&mut self.player,
280+
Arc::clone(&self.player),
272281
Arc::clone(&self.queue),
273282
Arc::clone(&self.device),
274283
);
275284
self.ui.update(
276-
&mut self.player,
285+
Arc::clone(&self.player),
277286
Arc::clone(&self.queue),
278287
Arc::clone(&self.device),
279288
);
@@ -335,7 +344,7 @@ impl State {
335344
rpass.set_bind_group(1, pipeline.bind_group_1(), &[]);
336345

337346
for chunk in chunks.iter() {
338-
if chunk.is_visible(&self.player) {
347+
if chunk.visible {
339348
rpass.set_bind_group(2, &chunk.chunk_bind_group, &[]);
340349
rpass.set_vertex_buffer(
341350
0,
@@ -410,10 +419,9 @@ pub struct State {
410419
pub window: Arc<Mutex<Window>>,
411420
pub surface_config: wgpu::SurfaceConfiguration,
412421
pub pipelines: Vec<Box<dyn PipelineTrait>>,
413-
pub player: Player,
422+
pub player: Arc<RwLock<Player>>,
414423
pub world: World,
415424
pub ui: UI,
416425
pub config: Config,
417426
pub camera_controller: CameraController,
418-
// pub model: Rc<RefCell<Model>>,
419427
}

src/ui.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::pipeline::{PipelineTrait, PipelineType, Uniforms};
44
use crate::player::Player;
55
use crate::state::State;
66
use std::collections::HashMap;
7-
use std::sync::Arc;
7+
use std::sync::{Arc, Mutex, RwLock};
88
use wgpu::util::DeviceExt;
99
use wgpu::{BindGroup, Buffer, RenderPipeline};
1010

@@ -41,7 +41,13 @@ impl UI {
4141
index_buffer,
4242
}
4343
}
44-
pub fn update(&mut self, player: &Player, queue: Arc<wgpu::Queue>, device: Arc<wgpu::Device>) {
44+
pub fn update(
45+
&mut self,
46+
player: Arc<RwLock<Player>>,
47+
queue: Arc<wgpu::Queue>,
48+
device: Arc<wgpu::Device>,
49+
) {
50+
let player = player.read().unwrap();
4551
if let Some(block_ptr) = player.facing_block.as_ref() {
4652
let block = block_ptr.read().unwrap();
4753

@@ -115,7 +121,7 @@ impl UIPipeline {
115121
source: wgpu::ShaderSource::Wgsl(shader_source.into()),
116122
});
117123

118-
let uniforms = Uniforms::from(&state.player.camera);
124+
let uniforms = Uniforms::from(&state.player.read().unwrap().camera);
119125

120126
let projection_buffer =
121127
state

src/world.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use glam::Vec3;
22
use std::ops::{Deref, DerefMut};
3-
use std::sync::RwLock;
3+
use std::sync::{Mutex, RwLock};
44
use std::{
55
sync::{mpsc, Arc},
66
thread,
@@ -163,7 +163,11 @@ impl World {
163163

164164
return Some(block);
165165
}
166-
pub fn get_blocks_nearby(&self, player: &Player) -> Option<Vec<Arc<RwLock<Block>>>> {
166+
pub fn get_blocks_nearby(
167+
&self,
168+
player: Arc<RwLock<Player>>,
169+
) -> Option<Vec<Arc<RwLock<Block>>>> {
170+
let player = player.read().unwrap();
167171
let mut positions = vec![];
168172
let mut nearby_blocks = vec![];
169173

@@ -185,15 +189,18 @@ impl World {
185189
}
186190
pub fn update(
187191
&mut self,
188-
player: &mut Player,
192+
player: Arc<RwLock<Player>>,
189193
queue: Arc<wgpu::Queue>,
190194
device: Arc<wgpu::Device>,
191195
) {
192-
let current_chunk = player.calc_current_chunk();
193-
if current_chunk != player.current_chunk {
196+
let mut player_write = player.write().unwrap();
197+
let current_chunk = player_write.calc_current_chunk();
198+
199+
// Update loaded chunks based on player position
200+
if current_chunk != player_write.current_chunk {
194201
let delta = (
195-
current_chunk.0 - player.current_chunk.0,
196-
current_chunk.1 - player.current_chunk.1,
202+
current_chunk.0 - player_write.current_chunk.0,
203+
current_chunk.1 - player_write.current_chunk.1,
197204
);
198205

199206
let o = if CHUNKS_PER_ROW % 2 == 0 { 1 } else { 0 };
@@ -212,8 +219,8 @@ impl World {
212219

213220
let mut new_chunks_positions: Vec<(i32, i32)> = vec![];
214221

215-
let chunk_y_remove = player.current_chunk.1 + old_chunks_offset;
216-
let chunk_x_remove = player.current_chunk.0 + old_chunks_offset;
222+
let chunk_y_remove = player_write.current_chunk.1 + old_chunks_offset;
223+
let chunk_x_remove = player_write.current_chunk.0 + old_chunks_offset;
217224
if delta.0 != 0 {
218225
for i in LB + current_chunk.1..=UB + current_chunk.1 {
219226
new_chunks_positions.push((current_chunk.0 + new_chunks_offset, i));
@@ -295,11 +302,30 @@ impl World {
295302
// Re-render only the last inserted chunks
296303
}
297304

298-
player.current_chunk = current_chunk;
305+
player_write.current_chunk = current_chunk;
306+
std::mem::drop(player_write);
307+
// Update visible chunks based on player position and direction
308+
{
309+
let (sender, receiver) = mpsc::channel();
310+
for chunk in self.chunks.iter() {
311+
let chunk = Arc::clone(&chunk);
312+
let sender = sender.clone();
313+
let player = Arc::clone(&player);
314+
self.thread_pool.as_ref().unwrap().execute(move || {
315+
let mut chunk = chunk.write().unwrap();
316+
chunk.visible = chunk.is_visible(player);
317+
sender.send(()).unwrap();
318+
});
319+
}
320+
for _ in self.chunks.iter(){
321+
receiver.recv().unwrap();
322+
}
323+
}
299324
}
300325
pub fn dispose(&mut self) {
301326
self.thread_pool = None;
302327
}
328+
303329
pub fn save_state(&self) {
304330
for chunk in self.chunks.iter() {
305331
let chunkbrw = chunk.read().unwrap();

0 commit comments

Comments
 (0)