WebGPU Voxel Game

Basic map chunking

+85 -10
+25 -10
src/gfx.rs
··· 15 15 window::Window, 16 16 }; 17 17 18 - use crate::{app::WASM_WIN_SIZE, gfx::model::Vertex, Instance, InstanceRaw}; 18 + use crate::{app::WASM_WIN_SIZE, gfx::model::Vertex, map::{sl3get, Block, CHUNK_SIZE}, Instance, InstanceRaw}; 19 19 20 20 #[repr(C)] 21 21 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] ··· 290 290 291 291 292 292 let camera = camera::Camera { 293 - eye: vec3(0., 1., 2.), 293 + eye: vec3(50., 20., 50.), 294 294 target: Vec3::ZERO, 295 295 up: Vec3::Y, 296 296 aspect: surface_config.width as f32 / surface_config.height as f32, ··· 336 336 bind_group: camera_bind_group, 337 337 }; 338 338 339 - const NUM_INSTANCES_PER_ROW: u32 = 10; 339 + // MAP LOAD 340 + 341 + let map = crate::map::new_map(); 342 + 343 + let mut instances = vec![]; 344 + 340 345 const SPACE_BETWEEN: f32 = 3.0; 341 - let instances = itertools::iproduct!(0..NUM_INSTANCES_PER_ROW, 0..NUM_INSTANCES_PER_ROW) 342 - .map(|(x, z)| { 343 - let mapping = |n| SPACE_BETWEEN * (n as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0); 344 - let position = vec3(mapping(x), 0.0, mapping(z)); 346 + for (_coords, chunk) in map.chunks { 347 + let _3diter = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2); 348 + 349 + let mut i = _3diter.filter_map(|(x,y,z)| { 350 + 351 + if let Block::AIR = sl3get(&chunk.blocks, x, y, z) { 352 + return None; 353 + } 354 + 355 + let mapping = |n| SPACE_BETWEEN * (n as f32 - CHUNK_SIZE.0 as f32 / 2.0); 356 + let position = vec3(mapping(x), -mapping(y), mapping(z)); 345 357 346 358 // this is needed so an object at (0, 0, 0) won't get scaled to zero 347 359 // as Quaternions can affect scale if they're not created correctly ··· 350 362 _ => Quat::from_axis_angle(Vec3::Z, 0.0), 351 363 }; 352 364 353 - Instance { position, rotation } 354 - }) 355 - .collect::<Vec<_>>(); 365 + Some(Instance { position, rotation }) 366 + 367 + }).collect::<Vec<_>>(); 368 + 369 + instances.append(&mut i); 370 + } 356 371 357 372 let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); 358 373 let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+1
src/lib.rs
··· 2 2 3 3 mod app; 4 4 mod gfx; 5 + mod map; 5 6 6 7 use glam::{Mat3, Mat4, Quat, Vec3}; 7 8 #[cfg(target_arch = "wasm32")]
+59
src/map.rs
··· 1 + use glam::IVec2; 2 + use std::collections::HashMap; 3 + 4 + // I have arbitrarily decided that this is (x,z,y) where +y is up. 5 + 6 + pub(crate) const CHUNK_SIZE: (usize, usize, usize) = (16, 16, 16); 7 + 8 + // A [Block; X*Y*Z] would be a much more efficient datatype, but, well... 9 + pub type Slice3 = [[[Block; CHUNK_SIZE.0]; CHUNK_SIZE.1]; CHUNK_SIZE.2]; 10 + 11 + pub fn sl3get(sl3: &Slice3, x: usize, y: usize, z: usize) -> Block { 12 + sl3[y][z][x] 13 + } 14 + pub fn sl3set(sl3: &mut Slice3, x: usize, y: usize, z: usize, new: Block) { 15 + sl3[y][z][x] = new; 16 + } 17 + 18 + pub struct WorldMap { 19 + pub chunks: HashMap<IVec2, Chunk>, 20 + } 21 + 22 + pub struct Chunk { 23 + pub blocks: Slice3, 24 + } 25 + 26 + #[derive(Copy, Clone, Default)] 27 + #[repr(u32)] 28 + pub enum Block { 29 + #[default] 30 + AIR = 0, 31 + BRICK, 32 + } 33 + 34 + 35 + fn new_chunk(world_x: i32, world_z: i32) -> Chunk { 36 + let mut blocks = Slice3::default(); 37 + 38 + for (x, z, y) in itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2) { 39 + let (xf, zf) = ( 40 + (x as i32 + (world_x * CHUNK_SIZE.0 as i32)) as f32, 41 + (z as i32 + (world_z * CHUNK_SIZE.0 as i32)) as f32, 42 + ); 43 + 44 + let sines = f32::sin(xf * 0.1) + f32::sin(zf * 0.1); 45 + 46 + let n = ((sines / 2. * CHUNK_SIZE.2 as f32).round() as i32) <= y as _; 47 + sl3set(&mut blocks, x, y, z, { 48 + if n { Block::BRICK } else { Block::AIR } 49 + }); 50 + } 51 + 52 + Chunk { blocks } 53 + } 54 + 55 + pub fn new_map() -> WorldMap { 56 + WorldMap { 57 + chunks: HashMap::from([((0, 0).into(), new_chunk(0, 0))]), 58 + } 59 + }