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;