forestiles/src/map.rs

248 lines
8.6 KiB
Rust
Raw Normal View History

2024-12-22 19:33:27 +00:00
use bevy::{asset::RenderAssetUsages, prelude::*, render::mesh::{Indices, PrimitiveTopology}};
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
use rand::{Rng, SeedableRng};
use voronoice::{BoundingBox, Point, VoronoiBuilder};
pub struct Plugin;
impl bevy::prelude::Plugin for Plugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup)
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)));
}
}
pub const HEIGHT: f32 = 2.;
pub const WIDTH: f32 = 2.;
pub const REAL_HEIGHT: f32 = 500.;
pub const REAL_WIDTH: f32 = 500.;
pub const CELL_AREA: f32 = REAL_HEIGHT * REAL_WIDTH / SIZE as f32;
pub const SIZE: usize = 10000;
pub const seed: u32 = 0;
#[derive(Resource)]
struct Voronoi (voronoice::Voronoi);
fn setup(
mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>
) {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed as u64);
let mut sites = Vec::with_capacity(SIZE);
for _ in 0..SIZE {
sites.push(Point { x:rng.gen_range(-WIDTH/2.0..WIDTH/2.0) as f64, y:rng.gen_range(-HEIGHT/2.0..HEIGHT/2.0) as f64 })
}
let voronoi = VoronoiBuilder::default()
.set_sites(sites)
.set_bounding_box(BoundingBox::new_centered(WIDTH as f64, HEIGHT as f64))
.set_lloyd_relaxation_iterations(3)
.build()
.unwrap();
let mut cells_data = Vec::with_capacity(SIZE);
let z_noise = Fbm::<Perlin>::new(seed);
let moisture_noise = Fbm::<Perlin>::new(seed+1)
.set_frequency(2.);
for i in 0..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, 1.));
}
let mut poss = Vec::new();
let mut colors = Vec::new();
let mut indices = Vec::new();
for (c, cd) in voronoi.iter_cells().zip(cells_data.iter()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
let mut color = cd.color();
// if c.site() == 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::<Vec<_>>();
let i = poss.len() as u32;
for v in vs.iter() {
poss.push(Vec3::new(v.x as f32, v.y as f32, 0.));// [v.x as f32, v.y as f32, 0.]);
// poss.push(Vertex::new_col([v.x as f32, v.y as f32], color, 1));
colors.push(color);
}
for v in 1..(vs.len()-1) as u32 {
indices.push(i);
indices.push(i+v);
indices.push(i+v+1);
}
}
let mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
// Add 4 vertices, each with its own position attribute (coordinate in
// 3D space), for each of the corners of the parallelogram.
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
poss
)
.with_inserted_attribute(
Mesh::ATTRIBUTE_COLOR,
colors
)
.with_inserted_indices(Indices::U32(indices));
cmds.spawn((
Mesh2d(meshes.add(mesh)),
MeshMaterial2d(materials.add(ColorMaterial::default())),
Transform::default()
));
cmds.insert_resource(Voronoi(voronoi));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CellKind {
Void,
Sea,
Beach,
Forest,
Dirt,
Stone,
Grass
}
#[derive(Debug, Component)]
pub struct CellData {
pub kind: CellKind,
pub cid: usize,
z: f32,
pub moisture: f32,
pub resource: f32 // How much resource there is (between 0 and 1)
}
impl CellData {
pub fn new(kind: CellKind, cell: usize, z: f32, moisture: f32, resource: f32) -> Self {
Self {
kind,
cid: cell,
z,
moisture,
resource
}
}
// pub fn pos<'a>(&self, map: &'a Map) -> &'a Point {
// &map.voronoi.sites()[self.cid]
// }
pub 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::Beach => [0.82, 0.84, 0.51, 1.],
CellKind::Forest => [0., 0.5 - (self.resource*0.4), 0., 1.],
CellKind::Dirt => [0.53 - (self.resource*0.4), 0.38-(self.resource*0.4), 0.29-(self.resource*0.4), 1.],
CellKind::Stone => [0.5, 0.5, 0.5, 1.],
CellKind::Grass => [(136./255.) - (self.resource*0.4), (204./255.) - (self.resource*0.4), (59./255.) - (self.resource*0.4), 1.]
}
}
pub fn update(&mut self) {
// How much it get by day
let recuperation_rate = match self.kind {
CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => 0.,
CellKind::Forest => 1. / (100. * 365.25), // Let's say that a forest takes 100 years to mature
CellKind::Grass => 1. / (7. * 7.) // Let's say that grass takes 7 weaks to reach its max
};
self.resource = (self.resource + recuperation_rate).clamp(0., 1.);
}
pub fn set_resource(&mut self, val: f32, t: usize) {
self.resource = val.clamp(0., 1.);
if self.resource == 0. {
match self.kind {
CellKind::Forest => {
self.kind = CellKind::Grass;
self.resource = 1.;
},
CellKind::Grass => {
self.kind = CellKind::Dirt;
self.resource = 0.5;
},
CellKind::Beach => {
self.kind = CellKind::Sea;
},
_ => {}
}
}
}
}
// pub struct Map {
// pub voronoi: Voronoi,
// pub cells_data: Vec<CellData>,
// pub seed: u32
// }
// impl Map {
// pub const HEIGHT: f32 = 2.;
// pub const WIDTH: f32 = 2.;
// pub const REAL_HEIGHT: f32 = 500.;
// pub const REAL_WIDTH: f32 = 500.;
// pub const CELL_AREA: f32 = Self::REAL_HEIGHT * Self::REAL_WIDTH / Self::SIZE as f32;
// pub const SIZE: usize = 10000;
// pub fn new(seed: u32, t: usize) -> 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::<Perlin>::new(seed);
// let moisture_noise = Fbm::<Perlin>::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, 1., t));
// }
// Self {
// voronoi,
// cells_data,
// seed
// }
// }
// }