use std::time::Instant; use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; use rand::prelude::*; use voronoice::{BoundingBox, Point, Voronoi, VoronoiBuilder, VoronoiCell}; use winit::{event::{DeviceEvent, ElementState, Event, MouseButton, MouseScrollDelta, WindowEvent}, window::Window}; use crate::graphics::{Uniforms, Vertex}; const SQRT_3: f32 = 1.732050807568877293527446341505872367; const FRAMERATE: usize = 10; // Update per second #[repr(u8)] #[derive(Debug, Clone, Copy)] enum CellKind { Void, Sea, Plain, Beach, Forest, Dirt, Stone } struct CellData { kind: CellKind, cell: usize, z: f32, moisture: f32 } impl CellData { fn new(kind: CellKind, cell: usize, z: f32, moisture: f32) -> Self { Self { kind, cell, z, moisture } } fn color(&self) -> [f32; 4] { // let mut rng = thread_rng(); // [rng.gen(), rng.gen(), rng.gen(), 1.] 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, 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.] } } } struct Map { voronoi: Voronoi, cells_data: Vec, seed: u32 } impl Map { const HEIGHT: f32 = 2.; const WIDTH: f32 = 2.; const SIZE: usize = 10_000; fn new(seed: u32) -> Self { let mut rng = rand::rngs::SmallRng::seed_from_u64(seed as u64); let mut sites = Vec::with_capacity(Self::SIZE); for _ in 0..Self::SIZE { sites.push(Point { x:rng.gen_range(-Self::WIDTH/2.0..Self::WIDTH/2.0) as f64, y:rng.gen_range(-Self::HEIGHT/2.0..Self::HEIGHT/2.0) as f64 }) } let voronoi = VoronoiBuilder::default() .set_sites(sites) .set_bounding_box(BoundingBox::new_centered(Self::WIDTH as f64, Self::HEIGHT as f64)) .set_lloyd_relaxation_iterations(3) .build() .unwrap(); let mut cells_data = Vec::with_capacity(Self::SIZE); let z_noise = Fbm::::new(seed); let moisture_noise = Fbm::::new(seed+1) .set_frequency(2.); for i in 0..Self::SIZE { let c = voronoi.cell(i); let site = c.site_position(); let z = ( 0.3 // Arbitrary value + ((z_noise.get([site.x, site.y])+1.)/2.) // Noise + [0; 1] - ((site.x.powi(2)+site.y.powi(2)).sqrt()*0.5) // Distance - [0; sqrt(2)] * 0.5 ).clamp(0., 1.); let m = ( (moisture_noise.get([site.x, site.y])+1.)/2. // Noise + [0; 1] ).clamp(0., 1.) as f32; let k = if z <= 0.5 { CellKind::Sea } else if z <= 0.52 { CellKind::Beach } else if z < 0.8 { CellKind::Dirt } else { CellKind::Stone }; cells_data.push(CellData::new(k, i, z as f32, m)); } Self { voronoi, cells_data, seed } } } pub struct State { pub vertices: Vec, pub indices: Vec, pub uniforms: Uniforms, map: Map, start: Instant, frame: usize, selected_tile: usize, mouse_pressed: bool } impl State { pub fn new() -> Self { let mut s = Self { vertices: vec![], indices: vec![], uniforms: Uniforms::default(), start: Instant::now(), frame: 0, map: Map::new(0), selected_tile: 0, mouse_pressed: false }; s.render(); s } pub fn input(&mut self, event: Event<()>, window: &Window) { match event { Event::WindowEvent { event: WindowEvent::MouseInput { state, button, ..}, ..} => { if state.is_pressed() { match button { MouseButton::Left => { self.mouse_pressed = true; }, MouseButton::Right => self.map = Map::new(self.map.seed + 1), _ => {} }; } else { match button { MouseButton::Left => { self.mouse_pressed = false; }, _ => {} } } }, Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, ..} => { dbg!(&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) }; let c = self.map.voronoi.cell(self.selected_tile); if self.mouse_pressed { for i in c.iter_path(pos) { self.selected_tile = i; let c = &mut self.map.cells_data[self.selected_tile]; if let CellKind::Dirt = c.kind { c.kind = CellKind::Forest; } } } else { self.selected_tile = c.iter_path(pos).last().unwrap(); } }, Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => { self.uniforms.camera[2] -= match delta { MouseScrollDelta::PixelDelta(pos) => pos.y as f32, MouseScrollDelta::LineDelta(_, y) => y }; }, _ => {} } } pub fn render(&mut self) { self.vertices = Vec::new(); self.indices = Vec::new(); for (c, data) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()) { let mut color = data.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, data.z], 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); } } } pub fn update_if_needed(&mut self) { while self.start.elapsed().as_secs_f32() > (self.frame as f32) / (FRAMERATE as f32) { self.update(); } } pub fn update(&mut self) { self.frame += 1; } }