diff --git a/assets/horse.svg b/assets/horse.svg new file mode 100644 index 0000000..82f6550 --- /dev/null +++ b/assets/horse.svg @@ -0,0 +1,281 @@ + + + + diff --git a/src/graphics.rs b/src/graphics.rs index 7cc736b..a3ebdff 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -7,14 +7,14 @@ use crate::state::State; #[repr(C)] #[derive(Clone, Copy, Zeroable, Pod, Debug)] pub struct Vertex { - pub pos: [f32; 3], + pub pos: [f32; 2], pub color: [f32; 4] } impl Vertex { const DESC: VertexBufferLayout<'static> = VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x4], + attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x4], }; } @@ -33,13 +33,6 @@ impl Default for Uniforms { } } } -impl Uniforms { - const DESC: VertexBufferLayout<'static> = VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32], - }; -} pub struct Graphics<'a> { state: State, @@ -91,13 +84,13 @@ impl<'a> Graphics<'a> { let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), - contents: &[bytemuck::cast_slice::(&state.vertices), &[0; 1024]].concat(), + contents: &[bytemuck::cast_slice::(&state.vertices), &[0; 100000]].concat(), usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), - contents: &[bytemuck::cast_slice::(&state.indices), &[0; 1024]].concat(), + contents: &[bytemuck::cast_slice::(&state.indices), &[0; 100000]].concat(), usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, }); diff --git a/src/shader.wgsl b/src/shader.wgsl index 4230ead..f367341 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -11,13 +11,13 @@ struct VertexOutput { @vertex fn vs_main( - @location(0) pos: vec3f, + @location(0) pos: vec2f, @location(1) color: vec4f ) -> VertexOutput { var out: VertexOutput; out.color = color; // out.color[3] -= uniforms.darkness; - out.pos = vec4f(pos.xy-uniforms.camera.xy, pos.z, uniforms.camera.z); + out.pos = vec4f((pos.xy-uniforms.camera.xy)/uniforms.camera.z, 0, 1); return out; } diff --git a/src/state.rs b/src/state.rs index 13a8cfa..cddf73f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,23 +1,23 @@ use std::time::Instant; +use log::info; use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; use rand::prelude::*; use voronoice::{BoundingBox, Point, Voronoi, VoronoiBuilder}; -use winit::{event::{DeviceEvent, Event, MouseButton, MouseScrollDelta, WindowEvent}, window::Window}; +use winit::{event::{DeviceEvent, ElementState, Event, KeyEvent, MouseButton, MouseScrollDelta, RawKeyEvent, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window}; use crate::graphics::{Uniforms, Vertex}; const FRAMERATE: usize = 10; // Update per second -#[repr(u8)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum CellKind { Void, Sea, - Plain, Beach, Forest, Dirt, - Stone + Stone, + Grass } struct CellData { @@ -41,11 +41,11 @@ impl CellData { match self.kind { CellKind::Void => [0.; 4], CellKind::Sea => [0., 0., 1., 1.], - CellKind::Plain => [0., 1., 0., 1.], CellKind::Beach => [0.82, 0.84, 0.51, 1.], CellKind::Forest => [0., 0.5 - (self.moisture*0.4), 0., 1.], CellKind::Dirt => [0.53 - (self.moisture*0.4), 0.38-(self.moisture*0.4), 0.29-(self.moisture*0.4), 1.], - CellKind::Stone => [0.5, 0.5, 0.5, 1.] + CellKind::Stone => [0.5, 0.5, 0.5, 1.], + CellKind::Grass => [(136./255.) - (self.moisture*0.4), (204./255.) - (self.moisture*0.4), (59./255.) - (self.moisture*0.4), 1.] } } } @@ -105,11 +105,165 @@ impl Map { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum EntityKind { + Horse +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum EntityState { + Walking(Instant), // Start of walk + Resting +} + +#[derive(Debug)] +struct Entity { + pos: [f32; 3], + kind: EntityKind, + start: Instant, + state: EntityState +} +impl Entity { + fn new(pos: [f32; 3], kind: EntityKind) -> Self { + Self { + pos, + kind, + start: Instant::now(), + state: EntityState::Resting + } + } + fn render(&self, vertices: &mut Vec, indices: &mut Vec) { + match self.kind { + EntityKind::Horse => { + let color = [171./255., 122./255., 50./255., 1.]; + let dark = [color[0]-0.3, color[1]-0.3, color[2]-0.3, 1.]; + let (vs, is) = match self.state { + EntityState::Walking(now) => { + let now = now.elapsed().as_secs_f32()*5.; + ( + [ + // back left leg + Vertex { pos: [-0.5, 0.3], color: dark }, + Vertex { pos: [-0.4 + (now.sin()*0.1), -0.7 + (now.cos().max(-0.5)*0.1)], color: dark }, + Vertex { pos: [-0.25, 0.1], color: dark }, + + // back right leg + Vertex { pos: [-0.5, 0.3], color }, + Vertex { pos: [-0.4 + ((now + 1.).sin()*0.1), -0.7 + ((now + 1.).cos().max(-0.5)*0.1)], color }, + Vertex { pos: [-0.25, 0.1], color }, + + // front left leg + Vertex { pos: [0.3, 0.2], color: dark }, + Vertex { pos: [0.4 + ((now-1.).sin()*0.1), -0.7 + ((now-1.).cos().max(-0.5)*0.1)], color: dark }, + Vertex { pos: [0.5, 0.3], color: dark }, + + // front right leg + Vertex { pos: [0.3, 0.2], color }, + Vertex { pos: [0.4 + ((now-2.).sin()*0.1), -0.7 + ((now-2.).cos().max(-0.5)*0.1)], color }, + Vertex { pos: [0.5, 0.3], color }, + + // body + // 3 + Vertex { pos: [-0.3, 0.], color }, + Vertex { pos: [0.4, -0.1], color }, + // 11 + Vertex { pos: [0.3, 0.4], color }, + ], + [ + 0,1,2, + 3,4,5, + 6,7,8, + 9,10,11, + 3,12,13, + 3,13,11, + 3,11,14 + ] + ) + }, + EntityState::Resting => { + ( + [ + // back left leg + Vertex { pos: [-0.5, 0.3], color: dark }, + Vertex { pos: [-0.4, -0.75], color: dark }, + Vertex { pos: [-0.25, 0.1], color: dark }, + + // back right leg + Vertex { pos: [-0.5, 0.3], color }, + Vertex { pos: [-0.4, -0.75], color }, + Vertex { pos: [-0.25, 0.1], color }, + + // front left leg + Vertex { pos: [0.3, 0.2], color: dark }, + Vertex { pos: [0.4, -0.75], color: dark }, + Vertex { pos: [0.5, 0.3], color: dark }, + + // front right leg + Vertex { pos: [0.3, 0.2], color }, + Vertex { pos: [0.4, -0.75], color }, + Vertex { pos: [0.5, 0.3], color }, + + // body + // 3 + Vertex { pos: [-0.3, 0.], color }, + Vertex { pos: [0.4, -0.1], color }, + // 11 + Vertex { pos: [0.3, 0.4], color }, + ], + [ + 0,1,2, + 3,4,5, + 6,7,8, + 9,10,11, + 3,12,13, + 3,13,11, + 3,11,14 + ] + ) + } + }; + + + vertices.reserve(vs.len()); + let base = vertices.len() as u32; + for mut v in vs { + v.pos[0] = v.pos[0]/50. + self.pos[0]; + v.pos[1] = (v.pos[1] + 0.75)/50. + self.pos[1]; + vertices.push(v) + } + + indices.reserve(is.len()); + for i in is { + indices.push(base + i); + } + + // let vs = vec![ + // Vertex { pos: [-0.5, 0.3, 0.], color }, + // Vertex { pos: [-0.3, 0., 0.], color }, + // Vertex { pos: [0.4, -0.1, 0.], color }, + // Vertex { pos: [0.5, 0.3, 0.], color }, + // Vertex { pos: [0.3, 0.4, 0.], color }, + // ]; + // let i = self.vertices.len() as u32; + // for v in vs.iter() { + // self.vertices.push(*v); + // } + // for v in 1..(vs.len()-1) as u32 { + // self.indices.push(i); + // self.indices.push(i+v); + // self.indices.push(i+v+1); + // } + } + } + } +} + pub struct State { pub vertices: Vec, pub indices: Vec, pub uniforms: Uniforms, map: Map, + entities: Vec, start: Instant, frame: usize, selected_tile: usize, @@ -124,6 +278,7 @@ impl State { start: Instant::now(), frame: 0, map: Map::new(0), + entities: Vec::new(), selected_tile: 0, mouse_pressed: false }; @@ -153,17 +308,19 @@ impl State { Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, ..} => { let w_size = window.inner_size(); let pos = Point { - x: ((2.*position.x/(w_size.width as f64))-1.)*(self.uniforms.camera[2] as f64), - y: (1.-(2.*position.y/(w_size.height as f64)))*(self.uniforms.camera[2] as f64) + x: ((2.*position.x/(w_size.width as f64))-1.)*(self.uniforms.camera[2] as f64) + self.uniforms.camera[0] as f64, + y: (1.-(2.*position.y/(w_size.height as f64)))*(self.uniforms.camera[2] as f64) + self.uniforms.camera[1] as f64 }; let c = self.map.voronoi.cell(self.selected_tile); if self.mouse_pressed { - for i in c.iter_path(pos) { + for i in c.iter_path(pos.clone()) { self.selected_tile = i; let cd = &mut self.map.cells_data[self.selected_tile]; if let CellKind::Dirt = cd.kind { cd.kind = CellKind::Forest; } + + self.entities.push(Entity::new([pos.x as f32, pos.y as f32, 0.9], EntityKind::Horse)); } } else { self.selected_tile = c.iter_path(pos).last().unwrap(); @@ -174,15 +331,36 @@ impl State { MouseScrollDelta::PixelDelta(pos) => pos.y as f32, MouseScrollDelta::LineDelta(_, y) => y }; + self.uniforms.camera[2] = self.uniforms.camera[2].clamp(0.1, 1.); + }, + Event::WindowEvent { event: WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(kc), state, .. }, .. }, .. } => { + if state.is_pressed() { + match kc { + KeyCode::KeyW => { + self.uniforms.camera[1] += 0.1; + }, + KeyCode::KeyS => { + self.uniforms.camera[1] -= 0.1; + }, + KeyCode::KeyA => { + self.uniforms.camera[0] -= 0.1; + }, + KeyCode::KeyD => { + self.uniforms.camera[0] += 0.1; + }, + _ => {} + } + } }, _ => {} } } pub fn render(&mut self) { + info!("render"); self.vertices = Vec::new(); self.indices = Vec::new(); - for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()) { + for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind != CellKind::Forest) { let mut color = cd.color(); if c.site() == self.selected_tile { color[0] = (color[0]+0.4).clamp(0., 1.); @@ -192,7 +370,30 @@ impl State { let vs = c.iter_vertices().collect::>(); let i = self.vertices.len() as u32; for v in vs.iter() { - self.vertices.push(Vertex { pos: [v.x as f32, v.y as f32, cd.z], color }); + self.vertices.push(Vertex { pos: [v.x as f32, v.y as f32], color }); + } + for v in 1..(vs.len()-1) as u32 { + self.indices.push(i); + self.indices.push(i+v); + self.indices.push(i+v+1); + } + } + + for e in self.entities.iter() { + e.render(&mut self.vertices, &mut self.indices); + } + + for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind == CellKind::Forest) { + let mut color = cd.color(); + if c.site() == self.selected_tile { + color[0] = (color[0]+0.4).clamp(0., 1.); + color[1] = (color[1]+0.4).clamp(0., 1.); + color[2] = (color[2]+0.4).clamp(0., 1.); + } + let vs = c.iter_vertices().collect::>(); + let i = self.vertices.len() as u32; + for v in vs.iter() { + self.vertices.push(Vertex { pos: [v.x as f32, v.y as f32], color }); } for v in 1..(vs.len()-1) as u32 { self.indices.push(i); @@ -207,22 +408,30 @@ impl State { } } pub fn update(&mut self) { + info!("update"); let mut rng = thread_rng(); - let mut new_forest = Vec::new(); + let mut new_kind = Vec::new(); for cd in self.map.cells_data.iter() { - if let CellKind::Forest = cd.kind { - if rng.gen::() < (0.03*cd.moisture) { + if cd.kind == CellKind::Forest || cd.kind == CellKind::Grass { + let r = rng.gen::(); + if r < (0.035*cd.moisture) { let c = self.map.voronoi.cell(cd.cell); let n = c.iter_neighbors().choose(&mut rng).unwrap(); - new_forest.push(n); + let k = if r < (0.005*cd.moisture) && (self.map.cells_data[n].kind == CellKind::Dirt || self.map.cells_data[n].kind == CellKind::Grass) && cd.kind == CellKind::Forest { + Some(CellKind::Forest) + } else if self.map.cells_data[n].kind == CellKind::Dirt { + Some(CellKind::Grass) + } else { + None + }; + if let Some(k) = k { + new_kind.push((n, k)); + } } } } - for i in new_forest { - let cd = &mut self.map.cells_data[i]; - if let CellKind::Dirt = cd.kind { - cd.kind = CellKind::Forest; - } + for (n, k) in new_kind { + self.map.cells_data[n].kind = k; } self.frame += 1;