WebGPU Voxel Game
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Debug UI chunk mutation, flush inst. buffer

+224 -26
+70 -2
Cargo.lock
··· 43 43 "getrandom 0.2.15", 44 44 "once_cell", 45 45 "version_check", 46 - "zerocopy", 46 + "zerocopy 0.7.35", 47 47 ] 48 48 49 49 [[package]] ··· 251 251 "itertools", 252 252 "log", 253 253 "pollster", 254 + "rand", 254 255 "reqwest", 255 256 "thiserror 2.0.11", 256 257 "tobj", ··· 301 302 "quote", 302 303 "syn", 303 304 ] 305 + 306 + [[package]] 307 + name = "byteorder" 308 + version = "1.5.0" 309 + source = "registry+https://github.com/rust-lang/crates.io-index" 310 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 304 311 305 312 [[package]] 306 313 name = "byteorder-lite" ··· 2057 2064 checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" 2058 2065 2059 2066 [[package]] 2067 + name = "ppv-lite86" 2068 + version = "0.2.20" 2069 + source = "registry+https://github.com/rust-lang/crates.io-index" 2070 + checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 2071 + dependencies = [ 2072 + "zerocopy 0.7.35", 2073 + ] 2074 + 2075 + [[package]] 2060 2076 name = "presser" 2061 2077 version = "0.3.1" 2062 2078 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2105 2121 ] 2106 2122 2107 2123 [[package]] 2124 + name = "rand" 2125 + version = "0.9.0" 2126 + source = "registry+https://github.com/rust-lang/crates.io-index" 2127 + checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" 2128 + dependencies = [ 2129 + "rand_chacha", 2130 + "rand_core", 2131 + "zerocopy 0.8.16", 2132 + ] 2133 + 2134 + [[package]] 2135 + name = "rand_chacha" 2136 + version = "0.9.0" 2137 + source = "registry+https://github.com/rust-lang/crates.io-index" 2138 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 2139 + dependencies = [ 2140 + "ppv-lite86", 2141 + "rand_core", 2142 + ] 2143 + 2144 + [[package]] 2145 + name = "rand_core" 2146 + version = "0.9.0" 2147 + source = "registry+https://github.com/rust-lang/crates.io-index" 2148 + checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" 2149 + dependencies = [ 2150 + "getrandom 0.3.1", 2151 + "zerocopy 0.8.16", 2152 + ] 2153 + 2154 + [[package]] 2108 2155 name = "range-alloc" 2109 2156 version = "0.1.4" 2110 2157 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3721 3768 source = "registry+https://github.com/rust-lang/crates.io-index" 3722 3769 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 3723 3770 dependencies = [ 3724 - "zerocopy-derive", 3771 + "byteorder", 3772 + "zerocopy-derive 0.7.35", 3773 + ] 3774 + 3775 + [[package]] 3776 + name = "zerocopy" 3777 + version = "0.8.16" 3778 + source = "registry+https://github.com/rust-lang/crates.io-index" 3779 + checksum = "7b8c07a70861ce02bad1607b5753ecb2501f67847b9f9ada7c160fff0ec6300c" 3780 + dependencies = [ 3781 + "zerocopy-derive 0.8.16", 3725 3782 ] 3726 3783 3727 3784 [[package]] ··· 3729 3786 version = "0.7.35" 3730 3787 source = "registry+https://github.com/rust-lang/crates.io-index" 3731 3788 checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 3789 + dependencies = [ 3790 + "proc-macro2", 3791 + "quote", 3792 + "syn", 3793 + ] 3794 + 3795 + [[package]] 3796 + name = "zerocopy-derive" 3797 + version = "0.8.16" 3798 + source = "registry+https://github.com/rust-lang/crates.io-index" 3799 + checksum = "5226bc9a9a9836e7428936cde76bb6b22feea1a8bfdbc0d241136e4d13417e25" 3732 3800 dependencies = [ 3733 3801 "proc-macro2", 3734 3802 "quote",
+1
Cargo.toml
··· 22 22 egui = "0.31" 23 23 egui-wgpu = "0.31" 24 24 egui-winit = { version = "0.31", default-features = false } 25 + rand = "0.9.0" 25 26 26 27 [build-dependencies] 27 28 fs_extra = "1.3"
+53 -3
src/gfx.rs
··· 20 20 app::WASM_WIN_SIZE, 21 21 gfx::model::Vertex, 22 22 gui::EguiRenderer, 23 - map::{sl3get, Block, CHUNK_SIZE}, 23 + map::{sl3get, Block, WorldMap, CHUNK_SIZE}, 24 24 Instance, InstanceRaw, 25 25 }; 26 26 ··· 58 58 pub model: model::Model, 59 59 pub instances: Vec<Instance>, 60 60 pub instance_buffer: wgpu::Buffer, 61 + pub remake: bool, 61 62 } 62 63 63 64 pub struct LightState { ··· 182 183 pub render_pipelines: RenderPipelines, 183 184 pub depth_texture: texture::Texture, 184 185 186 + pub map: WorldMap, 185 187 pub object: ObjectState, 186 188 pub camera: CameraState, 187 189 pub interact: InteractState, ··· 349 351 let mut instances = vec![]; 350 352 351 353 const SPACE_BETWEEN: f32 = 2.0; 352 - for (coords, chunk) in map.chunks { 354 + for (coords, chunk) in &map.chunks { 353 355 let _3diter = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2); 354 356 355 357 let mut i = _3diter ··· 508 510 queue, 509 511 surface_config, 510 512 depth_texture, 513 + map, 511 514 camera: camera_state, 512 515 interact: InteractState { 513 516 wireframe: false, ··· 527 530 model: obj_model, 528 531 instances, 529 532 instance_buffer, 533 + remake: false, 530 534 }, 531 535 light: LightState { 532 536 uniform: light_uniform, ··· 554 558 ); 555 559 } 556 560 561 + pub fn update_instance_buf(&mut self) { 562 + let mut instances = vec![]; 563 + 564 + const SPACE_BETWEEN: f32 = 2.0; 565 + for (coords, chunk) in &self.map.chunks { 566 + let _3diter = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2); 567 + 568 + let mut i = _3diter 569 + .filter_map(|(x, y, z)| { 570 + if let Block::Air = sl3get(&chunk.blocks, x, y, z) { 571 + return None; 572 + } 573 + 574 + let chunk_offset = coords.as_vec2() * (SPACE_BETWEEN * CHUNK_SIZE.0 as f32); 575 + 576 + let mapping = |n| SPACE_BETWEEN * (n as f32 - CHUNK_SIZE.0 as f32 / 2.0); 577 + let position = vec3( 578 + mapping(x) + chunk_offset.x, 579 + -mapping(y), 580 + mapping(z) + chunk_offset.y, 581 + ); 582 + 583 + let rotation = Quat::from_axis_angle(Vec3::Y, 0.0); 584 + 585 + Some(Instance { position, rotation }) 586 + }) 587 + .collect::<Vec<_>>(); 588 + 589 + instances.append(&mut i); 590 + } 591 + 592 + let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); 593 + let instance_buffer = self 594 + .device 595 + .create_buffer_init(&wgpu::util::BufferInitDescriptor { 596 + label: Some("Instance Buffer"), 597 + contents: bytemuck::cast_slice(&instance_data), 598 + usage: wgpu::BufferUsages::VERTEX, 599 + }); 600 + self.object.instances = instances; 601 + self.object.instance_buffer = instance_buffer; 602 + self.object.remake = false; 603 + } 604 + 557 605 pub(crate) fn render( 558 606 &mut self, 559 607 egui: &mut Option<EguiRenderer>, ··· 675 723 0, 676 724 bytemuck::cast_slice(&[self.light.uniform]), 677 725 ); 726 + if self.object.remake { 727 + self.update_instance_buf(); 728 + } 678 729 } 679 730 680 731 pub fn input(&mut self, event: &WindowEvent, window_size: PhysicalSize<u32>) -> bool { 681 732 self.camera.controller.process_events(event); 682 - 683 733 684 734 // Deprecated! Replaced with EGUI debug ui. 685 735 // match event {
+37 -2
src/gui.rs
··· 1 1 use egui_winit::EventResponse; 2 + use glam::{ivec2, IVec2}; 2 3 use winit::window::Window; 3 4 4 - use crate::gfx::Gfx; 5 + use crate::{gfx::Gfx, map::{chunk_scramble_dispatch, ChunkScramble}}; 5 6 6 7 pub struct EguiRenderer { 7 8 state: egui_winit::State, 8 9 renderer: egui_wgpu::Renderer, 9 10 frame_started: bool, 10 11 pub scale_factor: f32, 12 + pub chunk_influence: (i32, i32), 11 13 } 12 14 13 15 impl EguiRenderer { ··· 51 53 state, 52 54 renderer, 53 55 frame_started: false, 54 - scale_factor: 1.0 56 + scale_factor: 1.0, 57 + chunk_influence: (0,0) 55 58 } 56 59 } 57 60 ··· 136 139 let ctx = self.ctx(); 137 140 138 141 let mut scale_factor = self.scale_factor; 142 + let (mut chunk_x, mut chunk_z) = self.chunk_influence; 139 143 140 144 egui::Window::new("Debug Menu") 141 145 .resizable(true) ··· 183 187 scale_factor = (scale_factor + 0.1).min(3.0); 184 188 } 185 189 }); 190 + 191 + ui.separator(); 192 + 193 + ui.horizontal(|ui| { 194 + ui.label("Scramble chunk at..."); 195 + ui.add(egui::DragValue::new(&mut chunk_x).speed(0.1)); 196 + ui.label("x, "); 197 + 198 + ui.add(egui::DragValue::new(&mut chunk_z).speed(0.1)); 199 + ui.label("z. "); 200 + }); 201 + 202 + ui.horizontal(|ui| { 203 + if ui.button("Random").clicked() { 204 + let c = chunk_scramble_dispatch(ChunkScramble::Random)(chunk_x,chunk_z); 205 + gfx.map.chunks.insert(ivec2(chunk_x,chunk_z), c); 206 + gfx.object.remake = true; 207 + } 208 + if ui.button("Normal").clicked() { 209 + let c = chunk_scramble_dispatch(ChunkScramble::Normal)(chunk_x,chunk_z); 210 + gfx.map.chunks.insert(ivec2(chunk_x,chunk_z), c); 211 + gfx.object.remake = true; 212 + } 213 + if ui.button("Inverse").clicked() { 214 + let c = chunk_scramble_dispatch(ChunkScramble::Inverse)(chunk_x,chunk_z); 215 + gfx.map.chunks.insert(ivec2(chunk_x,chunk_z), c); 216 + gfx.object.remake = true; 217 + } 218 + }); 219 + 186 220 }); 187 221 188 222 self.scale_factor = scale_factor; 223 + self.chunk_influence = (chunk_x, chunk_z); 189 224 } 190 225 }
+63 -19
src/map.rs
··· 1 1 use glam::{ivec2, IVec2}; 2 + use itertools::Itertools; 3 + use rand::{distr::{Distribution, StandardUniform}, Rng}; 2 4 use std::collections::HashMap; 3 5 4 6 // I have arbitrarily decided that this is (x,z,y) where +y is up. ··· 6 8 pub(crate) const CHUNK_SIZE: (usize, usize, usize) = (16, 16, 16); 7 9 8 10 // 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]; 11 + pub type Slice3 = [Block; CHUNK_SIZE.0 * CHUNK_SIZE.1 * CHUNK_SIZE.2]; 10 12 11 13 pub fn sl3get(sl3: &Slice3, x: usize, y: usize, z: usize) -> Block { 12 - sl3[y][z][x] 14 + sl3[y + CHUNK_SIZE.2 * (z + CHUNK_SIZE.1 * x)] 13 15 } 14 16 pub fn sl3set(sl3: &mut Slice3, x: usize, y: usize, z: usize, new: Block) { 15 - sl3[y][z][x] = new; 17 + sl3[y + CHUNK_SIZE.2 * (z + CHUNK_SIZE.1 * x)] = new; 16 18 } 17 19 18 20 pub struct WorldMap { ··· 31 33 Brick, 32 34 } 33 35 36 + impl Distribution<Block> for StandardUniform { 37 + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Block { 38 + match rng.random_range(0..=1) { 39 + 0 => Block::Air, 40 + _ => Block::Brick, 41 + } 42 + } 43 + } 44 + 45 + 34 46 fn new_chunk(world_x: i32, world_z: i32) -> Chunk { 35 - let mut blocks = Slice3::default(); 47 + let blocks = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2) 48 + .map(|(x, z, y)| { 49 + let (xf, zf) = ( 50 + (x as i32 + (world_x * CHUNK_SIZE.0 as i32)) as f32, 51 + (z as i32 + (world_z * CHUNK_SIZE.0 as i32)) as f32, 52 + ); 36 53 37 - for (x, z, y) in itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2) { 38 - let (xf, zf) = ( 39 - (x as i32 + (world_x * CHUNK_SIZE.0 as i32)) as f32, 40 - (z as i32 + (world_z * CHUNK_SIZE.0 as i32)) as f32, 41 - ); 54 + let sines = f32::sin(xf * 0.1) + f32::sin(zf * 0.1); 42 55 43 - let sines = f32::sin(xf * 0.1) + f32::sin(zf * 0.1); 56 + // Pretty arbitrary numbers! Just trying to get something interesting 57 + let n = (((sines / 4. + 0.5) * CHUNK_SIZE.2 as f32).round() as i32) <= y as _; 44 58 45 - // Pretty arbitrary numbers! Just trying to get something interesting 46 - let n = (((sines / 4. + 0.5) * 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 - } 59 + if n { 60 + Block::Brick 61 + } else { 62 + Block::Air 63 + } 64 + }) 65 + .collect_array() 66 + .unwrap(); 51 67 52 68 Chunk { blocks } 53 69 } 54 70 55 71 pub fn new_map() -> WorldMap { 56 - 57 72 const INITIAL_GENERATION_SIZE: usize = 11; 58 - let iter = (-(INITIAL_GENERATION_SIZE as i32/2)..).take(INITIAL_GENERATION_SIZE); 73 + let iter = (-(INITIAL_GENERATION_SIZE as i32 / 2)..).take(INITIAL_GENERATION_SIZE); 59 74 60 75 let mut chunks = HashMap::new(); 61 76 62 77 for (x, z) in itertools::iproduct!(iter.clone(), iter) { 63 - chunks.insert(ivec2(x,z) ,new_chunk(x, z)); 78 + chunks.insert(ivec2(x, z), new_chunk(x, z)); 64 79 } 65 80 66 81 WorldMap { chunks } 82 + } 83 + 84 + pub fn chunk_scramble_dispatch(method: ChunkScramble) -> fn(i32, i32) -> Chunk { 85 + use ChunkScramble as C; 86 + match method { 87 + C::Normal => new_chunk, 88 + C::Inverse => |x, z| Chunk { 89 + blocks: new_chunk(x, z) 90 + .blocks 91 + .iter() 92 + .map(|b| match b { 93 + Block::Air => Block::Brick, 94 + Block::Brick => Block::Air, 95 + }) 96 + .collect_array() 97 + .unwrap(), 98 + }, 99 + C::Random => |_,_| { 100 + Chunk { 101 + blocks: rand::rng().random() 102 + } 103 + } 104 + } 105 + } 106 + 107 + pub enum ChunkScramble { 108 + Normal, 109 + Inverse, 110 + Random, 67 111 }