From 61ec4c4da5c3ec78b0cb014ffa72819823ca521c Mon Sep 17 00:00:00 2001 From: Vijfhoek Date: Sun, 30 May 2021 13:25:47 +0200 Subject: [PATCH] Add world generation, better fps stats, multiple chunks, grass/stone blocks --- Cargo.lock | 89 +++++++++++++++++++++++++- Cargo.toml | 4 ++ assets/block_temp/grass.png | 3 + assets/block_temp/grass.xcf | 3 + src/chunk.rs | 94 ++++++++++++++++++++++++--- src/cube.rs | 39 ++++++++++++ src/light.rs | 12 ++++ src/main.rs | 32 ++++++++-- src/shaders/world.wgsl | 2 +- src/state.rs | 89 +++++++++++++++----------- src/world.rs | 52 +++++++++++++++ src/world_state.rs | 123 ++++++++++++++++-------------------- 12 files changed, 415 insertions(+), 127 deletions(-) create mode 100644 assets/block_temp/grass.png create mode 100644 assets/block_temp/grass.xcf create mode 100644 src/world.rs diff --git a/Cargo.lock b/Cargo.lock index d96066d..4696f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" dependencies = [ - "getrandom", + "getrandom 0.2.3", "once_cell", "version_check", ] @@ -616,6 +616,17 @@ dependencies = [ "byteorder", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -624,7 +635,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -1150,6 +1161,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" +[[package]] +name = "noise" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82051dd6745d5184c6efb7bc8be14892a7f6d4f3ad6dbf754d1c7d7d5fe24b43" +dependencies = [ + "image", + "rand", + "rand_xorshift", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -1328,6 +1350,12 @@ dependencies = [ "miniz_oxide 0.3.7", ] +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1373,6 +1401,56 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +dependencies = [ + "rand_core", +] + [[package]] name = "range-alloc" version = "0.1.2" @@ -1451,6 +1529,7 @@ dependencies = [ "futures", "image", "log", + "noise", "wgpu", "winit", ] @@ -1611,6 +1690,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index cfd44ef..5310b1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,9 @@ env_logger = "0.8.3" futures = "0.3.15" image = "0.23.14" log = "0.4.14" +noise = "0.7.0" wgpu = "0.8.1" winit = { version = "0.25.0", default_features = false, features = ["x11"] } + +[profile.release] +debug = true diff --git a/assets/block_temp/grass.png b/assets/block_temp/grass.png new file mode 100644 index 0000000..d778e2c --- /dev/null +++ b/assets/block_temp/grass.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb8a1f17f47c26297e95ffc3accf132d28eb70c5080e42f7733cb761432037de +size 232544 diff --git a/assets/block_temp/grass.xcf b/assets/block_temp/grass.xcf new file mode 100644 index 0000000..7785cf9 --- /dev/null +++ b/assets/block_temp/grass.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8333039c3b0c648df0279787c97617244c221b788120bd6749eb6f9e0d6b90b9 +size 439028 diff --git a/src/chunk.rs b/src/chunk.rs index 43e8cc3..2a74f9c 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,11 +1,17 @@ use crate::instance::Instance; use ahash::AHashMap; use cgmath::{InnerSpace, Vector3}; +use noise::{ + utils::{NoiseMapBuilder, PlaneMapBuilder}, + Fbm, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BlockType { - Dirt, Cobblestone, + Dirt, + Grass, + Stone, } #[derive(Debug, Clone, Copy)] @@ -15,22 +21,87 @@ pub struct Block { const CHUNK_SIZE: usize = 16; +type ChunkBlocks = [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]; + pub struct Chunk { - pub blocks: [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE], + pub blocks: ChunkBlocks, pub highlighted: Option>, } impl Chunk { - pub fn to_instances(&self) -> Vec<(BlockType, Vec)> { + pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self { + let fbm = Fbm::new(); + + let builder = PlaneMapBuilder::new(&fbm) + .set_size(16, 16) + .set_x_bounds(chunk_x as f64 * 0.2, chunk_x as f64 * 0.2 + 0.2) + .set_y_bounds(chunk_z as f64 * 0.2, chunk_z as f64 * 0.2 + 0.2) + .build(); + + let mut blocks: ChunkBlocks = Default::default(); + for z in 0..CHUNK_SIZE { + for x in 0..CHUNK_SIZE { + let v = builder.get_value(x, z) * 10.0 + 64.0; + let v = v.round() as i32; + + let stone_max = (v - 4 - chunk_y * 16).min(CHUNK_SIZE as i32); + for y in 0..stone_max { + blocks[y as usize][z][x] = Some(Block { + block_type: BlockType::Stone, + }); + } + + let dirt_max = (v - chunk_y * 16).min(CHUNK_SIZE as i32); + for y in stone_max.max(0)..dirt_max { + blocks[y as usize][z][x] = Some(Block { + block_type: BlockType::Dirt, + }); + } + + if dirt_max >= 0 && dirt_max < CHUNK_SIZE as i32 { + blocks[dirt_max as usize][z][x] = Some(Block { + block_type: BlockType::Grass, + }); + } + } + } + + Self { + blocks, + highlighted: None, + } + } + + fn check_visible(&self, x: usize, y: usize, z: usize) -> bool { + (x > 0 && y > 0 && z > 0 && self.get_block(x - 1, y - 1, z - 1).is_some()) + && (y > 0 && z > 0 && self.get_block(x + 1, y - 1, z - 1).is_some()) + && (x > 0 && z > 0 && self.get_block(x - 1, y + 1, z - 1).is_some()) + && (z > 0 && self.get_block(x + 1, y + 1, z - 1).is_some()) + && (x > 0 && y > 0 && self.get_block(x - 1, y - 1, z + 1).is_some()) + && (y > 0 && self.get_block(x + 1, y - 1, z + 1).is_some()) + && (x > 0 && self.get_block(x - 1, y + 1, z + 1).is_some()) + && (x > 0 && y > 0 && z > 0 && self.get_block(x + 1, y + 1, z + 1).is_some()) + } + + pub fn to_instances(&self, offset: Vector3) -> Vec<(BlockType, Vec)> { let mut map: AHashMap> = AHashMap::new(); for (y, y_blocks) in self.blocks.iter().enumerate() { for (z, z_blocks) in y_blocks.iter().enumerate() { for (x, block) in z_blocks.iter().enumerate() { if let Some(block) = block { - let position = Vector3::new(x as f32, y as f32, z as f32); - let instances = map.entry(block.block_type).or_default(); + let position = Vector3::new( + (x as i32 + offset.x * 16) as f32, + (y as i32 + offset.y * 16) as f32, + (z as i32 + offset.z * 16) as f32, + ); + // Don't add the block if it's not visible + if self.check_visible(x, y, z) { + continue; + } + + let instances = map.entry(block.block_type).or_default(); instances.push(Instance { position: position.into(), highlighted: (self.highlighted == Some(Vector3::new(x, y, z))) as i32, @@ -120,11 +191,14 @@ impl Chunk { return None; } - if let Some(_) = self.get_block( - position.x as usize, - position.y as usize, - position.z as usize, - ) { + if self + .get_block( + position.x as usize, + position.y as usize, + position.z as usize, + ) + .is_some() + { // Intersection occurred return Some((position.map(|x| x as usize), face)); } diff --git a/src/cube.rs b/src/cube.rs index 60aacb4..3c7bce7 100644 --- a/src/cube.rs +++ b/src/cube.rs @@ -39,6 +39,45 @@ pub const VERTICES: &[Vertex] = &[ Vertex { position: [1.0, 1.0, 0.0], texture_coordinates: [1.0, 0.0], normal: [ 0.0, 1.0, 0.0] }, ]; +#[rustfmt::skip] +pub const VERTICES_GRASS: &[Vertex] = &[ + // Left + Vertex { position: [0.0, 0.0, 0.0], texture_coordinates: [0.5, 0.5], normal: [-1.0, 0.0, 0.0] }, + Vertex { position: [0.0, 0.0, 1.0], texture_coordinates: [0.0, 0.5], normal: [-1.0, 0.0, 0.0] }, + Vertex { position: [0.0, 1.0, 1.0], texture_coordinates: [0.0, 0.0], normal: [-1.0, 0.0, 0.0] }, + Vertex { position: [0.0, 1.0, 0.0], texture_coordinates: [0.5, 0.0], normal: [-1.0, 0.0, 0.0] }, + + // Right + Vertex { position: [1.0, 0.0, 0.0], texture_coordinates: [0.0, 0.5], normal: [ 1.0, 0.0, 0.0] }, + Vertex { position: [1.0, 0.0, 1.0], texture_coordinates: [0.5, 0.5], normal: [ 1.0, 0.0, 0.0] }, + Vertex { position: [1.0, 1.0, 1.0], texture_coordinates: [0.5, 0.0], normal: [ 1.0, 0.0, 0.0] }, + Vertex { position: [1.0, 1.0, 0.0], texture_coordinates: [0.0, 0.0], normal: [ 1.0, 0.0, 0.0] }, + + // Back + Vertex { position: [0.0, 0.0, 0.0], texture_coordinates: [0.5, 0.5], normal: [ 0.0, 0.0, -1.0] }, + Vertex { position: [0.0, 1.0, 0.0], texture_coordinates: [0.5, 0.0], normal: [ 0.0, 0.0, -1.0] }, + Vertex { position: [1.0, 1.0, 0.0], texture_coordinates: [0.0, 0.0], normal: [ 0.0, 0.0, -1.0] }, + Vertex { position: [1.0, 0.0, 0.0], texture_coordinates: [0.0, 0.5], normal: [ 0.0, 0.0, -1.0] }, + + // Front + Vertex { position: [0.0, 0.0, 1.0], texture_coordinates: [0.0, 0.5], normal: [ 0.0, 0.0, 1.0] }, + Vertex { position: [0.0, 1.0, 1.0], texture_coordinates: [0.0, 0.0], normal: [ 0.0, 0.0, 1.0] }, + Vertex { position: [1.0, 1.0, 1.0], texture_coordinates: [0.5, 0.0], normal: [ 0.0, 0.0, 1.0] }, + Vertex { position: [1.0, 0.0, 1.0], texture_coordinates: [0.5, 0.5], normal: [ 0.0, 0.0, 1.0] }, + + // Bottom + Vertex { position: [0.0, 0.0, 0.0], texture_coordinates: [0.5, 0.5], normal: [ 0.0, -1.0, 0.0] }, + Vertex { position: [0.0, 0.0, 1.0], texture_coordinates: [0.5, 1.0], normal: [ 0.0, -1.0, 0.0] }, + Vertex { position: [1.0, 0.0, 1.0], texture_coordinates: [0.0, 1.0], normal: [ 0.0, -1.0, 0.0] }, + Vertex { position: [1.0, 0.0, 0.0], texture_coordinates: [0.0, 0.5], normal: [ 0.0, -1.0, 0.0] }, + + // Top + Vertex { position: [0.0, 1.0, 0.0], texture_coordinates: [0.5, 0.0], normal: [ 0.0, 1.0, 0.0] }, + Vertex { position: [0.0, 1.0, 1.0], texture_coordinates: [0.5, 0.5], normal: [ 0.0, 1.0, 0.0] }, + Vertex { position: [1.0, 1.0, 1.0], texture_coordinates: [1.0, 0.5], normal: [ 0.0, 1.0, 0.0] }, + Vertex { position: [1.0, 1.0, 0.0], texture_coordinates: [1.0, 0.0], normal: [ 0.0, 1.0, 0.0] }, +]; + #[rustfmt::skip] pub const INDICES: &[u16] = &[ 2, 0, 1, diff --git a/src/light.rs b/src/light.rs index a01471e..6c332b1 100644 --- a/src/light.rs +++ b/src/light.rs @@ -1,3 +1,5 @@ +use cgmath::Vector3; + #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct Light { @@ -5,3 +7,13 @@ pub struct Light { pub _padding: u32, pub color: [f32; 3], } + +impl Light { + pub fn new(position: Vector3, color: Vector3) -> Self { + Self { + position: position.into(), + _padding: 0, + color: color.into(), + } + } +} diff --git a/src/main.rs b/src/main.rs index f9014b8..557c528 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,10 @@ mod state; mod texture; mod uniforms; mod vertex; +mod world; mod world_state; -use std::time::Instant; +use std::time::{Duration, Instant}; use winit::{ dpi::{PhysicalSize, Size}, event::{ElementState, Event, KeyboardInput, MouseButton, VirtualKeyCode, WindowEvent}, @@ -35,7 +36,11 @@ fn main() { let mut state = futures::executor::block_on(State::new(&window)); let mut frames = 0; - let mut instant = Instant::now(); + let mut frame_instant = Instant::now(); + let mut elapsed = Duration::from_secs(0); + + let mut frametime_min = Duration::from_secs(1000); + let mut frametime_max = Duration::from_secs(0); let mut last_render_time = Instant::now(); @@ -80,16 +85,29 @@ fn main() { _ => {} }, Event::RedrawRequested(_) => { + let frame_elapsed = frame_instant.elapsed(); + frame_instant = Instant::now(); + + frametime_min = frametime_min.min(frame_elapsed); + frametime_max = frametime_max.max(frame_elapsed); + elapsed += frame_elapsed; + frames += 1; - if instant.elapsed().as_secs() >= 1 { - let frametime = instant.elapsed() / frames; + if elapsed.as_secs() >= 1 { + let frametime = elapsed / frames; let fps = 1_000_000 / frametime.as_micros(); + let fps_max = 1_000_000 / frametime_min.as_micros(); + let fps_min = 1_000_000 / frametime_max.as_micros(); + println!( - "{} frames | frametime {:?} | fps {}", - frames, frametime, fps + "{} frames | frametime avg={:?} min={:?} max={:?} | fps avg={} min={} max={}", + frames, frametime, frametime_min, frametime_max, fps, fps_min, fps_max, ); - instant = Instant::now(); + + elapsed = Duration::from_secs(0); frames = 0; + frametime_min = Duration::from_secs(1000); + frametime_max = Duration::from_secs(0); } let now = Instant::now(); diff --git a/src/shaders/world.wgsl b/src/shaders/world.wgsl index 5f82c95..a154267 100644 --- a/src/shaders/world.wgsl +++ b/src/shaders/world.wgsl @@ -55,7 +55,7 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4 { let object_color: vec4 = textureSample(texture, sampler_diffuse, in.texture_coordinates); - let ambient_strength = 0.2; + let ambient_strength = 0.1; let ambient_color = light.color * ambient_strength; let light_direction = normalize(light.position - in.world_position); diff --git a/src/state.rs b/src/state.rs index 84386bb..3af83d3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -362,15 +362,15 @@ impl State { } fn update_aim(&mut self) { - let camera = &self.world_state.camera; - let chunk = &mut self.world_state.chunk; - let position = chunk - .raycast(camera.position.to_vec(), camera.direction()) - .map(|(position, _)| position); - if position != chunk.highlighted { - chunk.highlighted = position; - self.world_state.update_chunk(&self.render_queue); - } + // let camera = &self.world_state.camera; + // let chunk = &mut self.world_state.chunk; + // let position = chunk + // .raycast(camera.position.to_vec(), camera.direction()) + // .map(|(position, _)| position); + // if position != chunk.highlighted { + // chunk.highlighted = position; + // self.world_state.update_chunk(&self.render_queue); + // } } fn input_mouse(&mut self, dx: f64, dy: f64) { @@ -393,25 +393,25 @@ impl State { } if self.mouse_grabbed => { let camera = &self.world_state.camera; - if let Some((pos, axis)) = self - .world_state - .chunk - .raycast(camera.position.to_vec(), camera.direction()) - { - if *button == 1 { - self.world_state.chunk.blocks[pos.y][pos.z][pos.x].take(); - dbg!(&pos); - self.world_state.update_chunk(&self.render_queue); - } else if *button == 3 { - let new_pos = pos.map(|x| x as i32) - axis; - dbg!(&axis, &new_pos); - self.world_state.chunk.blocks[new_pos.y as usize][new_pos.z as usize] - [new_pos.x as usize] = Some(Block { - block_type: BlockType::Cobblestone, - }); - self.world_state.update_chunk(&self.render_queue); - } - } + // if let Some((pos, axis)) = self + // .world_state + // .chunk + // .raycast(camera.position.to_vec(), camera.direction()) + // { + // if *button == 1 { + // self.world_state.chunk.blocks[pos.y][pos.z][pos.x].take(); + // dbg!(&pos); + // self.world_state.update_chunk(&self.render_queue); + // } else if *button == 3 { + // let new_pos = pos.map(|x| x as i32) - axis; + // dbg!(&axis, &new_pos); + // self.world_state.chunk.blocks[new_pos.y as usize][new_pos.z as usize] + // [new_pos.x as usize] = Some(Block { + // block_type: BlockType::Cobblestone, + // }); + // self.world_state.update_chunk(&self.render_queue); + // } + // } } DeviceEvent::MouseMotion { delta: (dx, dy) } => self.input_mouse(*dx, *dy), @@ -425,13 +425,13 @@ impl State { let (yaw_sin, yaw_cos) = self.world_state.camera.yaw.0.sin_cos(); let forward = cgmath::Vector3::new(yaw_cos, 0.0, yaw_sin).normalize(); - self.world_state.camera.position += forward * self.forward_speed * 6.0 * dt_secs; + self.world_state.camera.position += forward * self.forward_speed * 15.0 * dt_secs; let right = cgmath::Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize(); - self.world_state.camera.position += right * self.right_speed * 6.0 * dt_secs; + self.world_state.camera.position += right * self.right_speed * 15.0 * dt_secs; let up = cgmath::Vector3::new(0.0, 1.0, 0.0).normalize(); - self.world_state.camera.position += up * self.up_speed * 6.0 * dt_secs; + self.world_state.camera.position += up * self.up_speed * 15.0 * dt_secs; self.update_aim(); @@ -454,6 +454,7 @@ impl State { label: Some("render_encoder"), }); + let mut triangle_count = 0; { let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("render_pass"), @@ -462,9 +463,9 @@ impl State { resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.05, - g: 0.05, - b: 0.05, + r: 0.502, + g: 0.663, + b: 0.965, a: 1.0, }), store: true, @@ -485,24 +486,33 @@ impl State { render_pass.set_bind_group(1, &self.world_state.uniform_bind_group, &[]); render_pass.set_bind_group(2, &self.world_state.light_bind_group, &[]); - render_pass.set_vertex_buffer(0, self.world_state.vertex_buffer.slice(..)); render_pass.set_index_buffer( self.world_state.index_buffer.slice(..), wgpu::IndexFormat::Uint16, ); - for (block_type, instance_list) in &self.world_state.instance_lists { - let instance_buffer = &self.world_state.instance_buffers[block_type]; - + for (block_type, offset, instance_list) in &self.world_state.instance_lists { + // Set the texture let texture_bind_group = &self.world_state.texture_bind_groups[block_type]; render_pass.set_bind_group(0, texture_bind_group, &[]); + // Set the vertex buffer + let vertex_buffer = match block_type { + BlockType::Grass => self.world_state.vertex_buffer_grass.slice(..), + _ => self.world_state.vertex_buffer.slice(..), + }; + render_pass.set_vertex_buffer(0, vertex_buffer); + + // Set the instance buffer + let instance_buffer = &self.world_state.instance_buffers[&(*block_type, *offset)]; render_pass.set_vertex_buffer(1, instance_buffer.slice(..)); + render_pass.draw_indexed( 0..cube::INDICES.len() as u32, 0, 0..instance_list.len() as u32, ); + triangle_count += cube::INDICES.len() / 3 * instance_list.len(); } } @@ -529,8 +539,11 @@ impl State { render_pass.set_bind_group(0, &self.ui_texture_bind_group, &[]); render_pass.draw_indexed(0..CROSSHAIR_INDICES.len() as u32, 0, 0..1); + triangle_count += CROSSHAIR_INDICES.len() / 3; } + // dbg!(triangle_count); + self.render_queue .submit(std::iter::once(render_encoder.finish())); diff --git a/src/world.rs b/src/world.rs new file mode 100644 index 0000000..631fdbd --- /dev/null +++ b/src/world.rs @@ -0,0 +1,52 @@ +use cgmath::Vector3; + +use crate::{ + chunk::{BlockType, Chunk}, + instance::Instance, +}; + +pub struct World { + chunks: Vec>>, +} + +impl World { + pub fn generate() -> Self { + let mut chunks = Vec::with_capacity(16); + for y in 0..16 { + let mut chunks_z = Vec::with_capacity(16); + for z in 0..16 { + let mut chunks_x = Vec::with_capacity(16); + for x in 0..16 { + let chunk = Chunk::generate(x, y, z); + chunks_x.push(chunk); + } + chunks_z.push(chunks_x); + } + chunks.push(chunks_z); + } + + Self { chunks } + } + + pub fn to_instances(&self) -> Vec<(BlockType, Vector3, Vec)> { + let instant = std::time::Instant::now(); + + let mut instance_lists = Vec::new(); + + for (y, chunks_y) in self.chunks.iter().enumerate() { + for (z, chunks_z) in chunks_y.iter().enumerate() { + for (x, chunk) in chunks_z.iter().enumerate() { + let offset = Vector3::new(x as i32, y as i32, z as i32); + for (block_type, instances) in chunk.to_instances(offset) { + instance_lists.push((block_type, offset, instances)); + } + } + } + } + + let elapsed = instant.elapsed(); + println!("generating world instances took {:?}", elapsed); + + instance_lists + } +} diff --git a/src/world_state.rs b/src/world_state.rs index 0369a66..b648958 100644 --- a/src/world_state.rs +++ b/src/world_state.rs @@ -1,6 +1,7 @@ use std::{mem::size_of, time::Instant}; use ahash::AHashMap; +use cgmath::{Vector3, Zero}; use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, BufferAddress, BufferDescriptor, @@ -16,6 +17,7 @@ use crate::{ texture::Texture, uniforms::Uniforms, vertex::Vertex, + world::World, }; pub struct WorldState { @@ -26,13 +28,14 @@ pub struct WorldState { pub texture_bind_groups: AHashMap, pub camera: Camera, pub projection: Projection, - pub instance_lists: Vec<(BlockType, Vec)>, + pub instance_lists: Vec<(BlockType, Vector3, Vec)>, pub vertex_buffer: wgpu::Buffer, + pub vertex_buffer_grass: wgpu::Buffer, pub index_buffer: wgpu::Buffer, - pub instance_buffers: AHashMap, + pub instance_buffers: AHashMap<(BlockType, Vector3), wgpu::Buffer>, pub depth_texture: Texture, pub light_bind_group: wgpu::BindGroup, - pub chunk: Chunk, + pub world: World, } impl WorldState { @@ -40,22 +43,6 @@ impl WorldState { render_device: &wgpu::Device, render_queue: &wgpu::Queue, ) -> (wgpu::BindGroupLayout, AHashMap) { - let dirt_texture = Texture::from_bytes( - render_device, - render_queue, - include_bytes!("../assets/block/dirt.png"), - "dirt", - ) - .unwrap(); - - let cobblestone_texture = Texture::from_bytes( - render_device, - render_queue, - include_bytes!("../assets/block/cobblestone.png"), - "cobblestone", - ) - .unwrap(); - let sampler = render_device.create_sampler(&wgpu::SamplerDescriptor::default()); let bind_group_layout = @@ -85,15 +72,21 @@ impl WorldState { }); let bind_groups: AHashMap = [ - (BlockType::Dirt, dirt_texture), - (BlockType::Cobblestone, cobblestone_texture), + (BlockType::Cobblestone, "assets/block/cobblestone.png"), + (BlockType::Dirt, "assets/block/dirt.png"), + (BlockType::Grass, "assets/block_temp/grass.png"), + (BlockType::Stone, "assets/block/stone.png"), ] .iter() - .map(|(block_type, texture)| { + .map(|(block_type, texture_path)| { + let bytes = std::fs::read(texture_path).unwrap(); + let texture = + Texture::from_bytes(render_device, render_queue, &bytes, "block texture").unwrap(); + ( *block_type, render_device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("texture_bind_group"), + label: Some("block texture bind group"), layout: &bind_group_layout, entries: &[ wgpu::BindGroupEntry { @@ -125,7 +118,7 @@ impl WorldState { swap_chain_descriptor.height, cgmath::Deg(45.0), 0.1, - 100.0, + 500.0, ); (camera, projection) @@ -185,11 +178,10 @@ impl WorldState { fn create_light( render_device: &wgpu::Device, ) -> (Light, wgpu::Buffer, wgpu::BindGroupLayout, wgpu::BindGroup) { - let light = Light { - position: [5.0, 5.0, 5.0], - _padding: 0, - color: [1.0, 1.0, 1.0], - }; + let light = Light::new( + Vector3::new(256.0, 500.0, 200.0), + Vector3::new(1.0, 1.0, 1.0), + ); let light_buffer = render_device.create_buffer_init(&BufferInitDescriptor { label: Some("light_buffer"), @@ -209,7 +201,7 @@ impl WorldState { }, count: None, }], - label: None, + label: Some("light_bind_group_layout"), }); let light_bind_group = render_device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -218,7 +210,7 @@ impl WorldState { binding: 0, resource: light_buffer.as_entire_binding(), }], - label: None, + label: Some("light_bind_group"), }); ( @@ -249,6 +241,8 @@ impl WorldState { push_constant_ranges: &[], }); + let wireframe = false; + render_device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), layout: Some(&render_pipeline_layout), @@ -273,8 +267,16 @@ impl WorldState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, + cull_mode: if wireframe { + None + } else { + Some(wgpu::Face::Back) + }, + polygon_mode: if wireframe { + wgpu::PolygonMode::Line + } else { + wgpu::PolygonMode::Fill + }, clamp_depth: false, conservative: false, }, @@ -295,16 +297,16 @@ impl WorldState { fn create_instances( render_device: &wgpu::Device, - chunk: &Chunk, + world: &World, ) -> ( - Vec<(BlockType, Vec)>, - AHashMap, + Vec<(BlockType, Vector3, Vec)>, + AHashMap<(BlockType, Vector3), wgpu::Buffer>, ) { - let instance_lists = chunk.to_instances(); + let instance_lists = world.to_instances(); let instance_buffers = instance_lists .iter() - .map(|(block_type, _)| { + .map(|(block_type, offset, _)| { let buffer = render_device.create_buffer(&BufferDescriptor { label: Some("instance_buffer"), size: (size_of::() * 16 * 16 * 16) as BufferAddress, @@ -312,7 +314,7 @@ impl WorldState { mapped_at_creation: false, }); - (*block_type, buffer) + ((*block_type, *offset), buffer) }) .collect(); @@ -322,10 +324,10 @@ impl WorldState { pub fn update_chunk(&mut self, render_queue: &wgpu::Queue) { let instant = Instant::now(); - self.instance_lists = self.chunk.to_instances(); + self.instance_lists = self.world.to_instances(); - for (block_type, instance_list) in &self.instance_lists { - if let Some(instance_buffer) = self.instance_buffers.get_mut(&block_type) { + for (block_type, offset, instance_list) in &self.instance_lists { + if let Some(instance_buffer) = self.instance_buffers.get_mut(&(*block_type, *offset)) { render_queue.write_buffer(instance_buffer, 0, bytemuck::cast_slice(&instance_list)); } else { todo!(); @@ -341,31 +343,8 @@ impl WorldState { render_queue: &wgpu::Queue, swap_chain_descriptor: &wgpu::SwapChainDescriptor, ) -> WorldState { - let chunk = Chunk { - blocks: [ - [[Some(Block { - block_type: BlockType::Cobblestone, - }); 16]; 16], - [[Some(Block { - block_type: BlockType::Dirt, - }); 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - [[None; 16]; 16], - ], - highlighted: None, - }; + // let chunk = Chunk::generate(0, 0, 0); + let world = World::generate(); let (world_texture_layout, texture_bind_groups) = Self::create_textures(&render_device, &render_queue); @@ -392,6 +371,11 @@ impl WorldState { contents: bytemuck::cast_slice(cube::VERTICES), usage: wgpu::BufferUsage::VERTEX, }); + let grass_vertex_buffer = render_device.create_buffer_init(&BufferInitDescriptor { + label: Some("grass vertex buffer"), + contents: bytemuck::cast_slice(cube::VERTICES_GRASS), + usage: wgpu::BufferUsage::VERTEX, + }); let index_buffer = render_device.create_buffer_init(&BufferInitDescriptor { label: Some("index_buffer"), @@ -399,7 +383,7 @@ impl WorldState { usage: wgpu::BufferUsage::INDEX, }); - let (instance_lists, instance_buffers) = Self::create_instances(&render_device, &chunk); + let (instance_lists, instance_buffers) = Self::create_instances(&render_device, &world); let depth_texture = Texture::create_depth_texture(&render_device, &swap_chain_descriptor, "depth_texture"); @@ -414,11 +398,12 @@ impl WorldState { projection, instance_lists, vertex_buffer, + vertex_buffer_grass: grass_vertex_buffer, index_buffer, instance_buffers, depth_texture, light_bind_group, - chunk, + world, }; world_state.update_chunk(&render_queue);