forestiles/src/map.rs

196 lines
6.6 KiB
Rust
Raw Normal View History

use std::time::Duration;
2025-01-09 19:56:50 +00:00
use bevy::{asset::RenderAssetUsages, picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}, utils::HashMap};
2024-12-22 19:33:27 +00:00
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
2025-01-11 14:09:32 +00:00
use rand::{thread_rng, Rng, SeedableRng};
2024-12-22 19:33:27 +00:00
use voronoice::{BoundingBox, Point, VoronoiBuilder};
2024-12-23 21:07:31 +00:00
mod cells;
2025-01-09 19:56:50 +00:00
mod picking;
use picking::*;
2024-12-23 21:07:31 +00:00
use cells::*;
2024-12-22 19:33:27 +00:00
pub struct Plugin;
impl bevy::prelude::Plugin for Plugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup)
2024-12-23 18:05:35 +00:00
.insert_resource(Time::<Fixed>::from_seconds(0.25)) // Time for a day
.add_systems(FixedUpdate, update_cells)
2025-01-09 19:56:50 +00:00
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
2025-01-11 14:09:32 +00:00
.add_systems(Update, update_map_mesh)
2024-12-24 17:59:27 +00:00
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
2025-01-11 14:09:32 +00:00
.insert_resource(Seed(thread_rng().gen()));
2024-12-22 19:33:27 +00:00
}
}
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;
2024-12-24 17:59:27 +00:00
#[derive(Resource)]
struct Seed(u32);
2024-12-22 19:33:27 +00:00
2024-12-22 21:02:45 +00:00
#[derive(Component)]
2025-01-09 19:56:50 +00:00
pub struct Voronoi (voronoice::Voronoi);
2024-12-22 19:33:27 +00:00
2024-12-22 21:02:45 +00:00
#[derive(Component)]
2024-12-24 17:59:27 +00:00
pub struct MapMarker;
2024-12-22 21:02:45 +00:00
#[derive(Component)]
struct MapColors (Vec<[f32; 4]>);
2025-01-09 19:56:50 +00:00
#[derive(Component)]
pub struct CellsEntities (Vec<Entity>);
2024-12-22 19:33:27 +00:00
fn setup(
mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>,
2024-12-24 17:59:27 +00:00
mut materials: ResMut<Assets<ColorMaterial>>,
seed: Res<Seed>
2024-12-22 19:33:27 +00:00
) {
2024-12-24 17:59:27 +00:00
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 as u64);
2024-12-22 19:33:27 +00:00
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);
2024-12-24 17:59:27 +00:00
let z_noise = Fbm::<Perlin>::new(seed.0);
let moisture_noise = Fbm::<Perlin>::new(seed.0+1)
2024-12-22 19:33:27 +00:00
.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 {
2025-01-11 14:09:32 +00:00
CellKind::Dirt
2024-12-22 19:33:27 +00:00
} else {
CellKind::Stone
};
2024-12-23 18:05:35 +00:00
cells_data.push(CellData::new(k, i, (z*255.) as u8, (m*255.) as u8, 0, vec![]));
2024-12-22 19:33:27 +00:00
}
let mut poss = Vec::new();
let mut colors = Vec::new();
let mut indices = Vec::new();
2024-12-26 11:46:03 +00:00
for (c, cd) in voronoi.iter_cells().zip(cells_data.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
let color = cd.color();
2024-12-22 19:33:27 +00:00
// 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<_>>();
2024-12-22 21:02:45 +00:00
let i = poss.len();
2024-12-22 19:33:27 +00:00
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);
}
2024-12-22 21:02:45 +00:00
for v in 1..(vs.len()-1) {
indices.extend_from_slice(&[i as u32, (i+v) as u32, (i+v+1) as u32]);
cd.vertices.extend_from_slice(&[i, i+v, i+v+1]);
2024-12-22 19:33:27 +00:00
}
}
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,
2024-12-22 21:02:45 +00:00
colors.clone()
2024-12-22 19:33:27 +00:00
)
.with_inserted_indices(Indices::U32(indices));
2025-01-09 19:56:50 +00:00
let mut cells_entities = Vec::with_capacity(cells_data.len());
2024-12-22 19:33:27 +00:00
cmds.spawn((
Mesh2d(meshes.add(mesh)),
MeshMaterial2d(materials.add(ColorMaterial::default())),
2024-12-22 21:02:45 +00:00
Transform::default(),
Voronoi(voronoi),
MapColors(colors),
MapMarker
2024-12-24 17:59:27 +00:00
)).with_children(|parent| {
for cd in cells_data {
2025-01-09 19:56:50 +00:00
let mut cmd = parent.spawn((cd, LastUpdate(0)));
2025-01-11 14:09:32 +00:00
cmd.observe(|trigger: Trigger<Pointer<Click>>, mut cells: Query<&mut CellData>| {
if trigger.duration > Duration::from_millis(100) {
return
}
2025-01-11 14:09:32 +00:00
let mut cd = cells.get_mut(trigger.target).unwrap();
match cd.kind {
CellKind::Dirt | CellKind::Grass => {cd.kind = CellKind::Forest;},
_ => {}
}
dbg!(trigger.duration);
2025-01-09 19:56:50 +00:00
});
cells_entities.push(cmd.id());
2024-12-24 17:59:27 +00:00
}
2025-01-09 19:56:50 +00:00
}).insert(CellsEntities(cells_entities));
2024-12-22 21:02:45 +00:00
}
2024-12-22 19:33:27 +00:00
2024-12-23 21:07:31 +00:00
#[derive(Component)]
pub struct LastUpdate(usize);
2024-12-23 18:05:35 +00:00
fn update_cells(
2025-01-11 14:09:32 +00:00
mut cells: Query<(&mut CellData, &mut LastUpdate)>
) {
for (mut cd, mut lu) in cells.iter_mut() {
lu.0 += 1;
if lu.0 > match cd.kind {
CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => usize::MAX,
CellKind::Forest => 100*365/4, // Let's say that a forest takes 100 years to mature
CellKind::Grass => 7*7/4 // Let's say that grass takes 7 weaks to reach its max
} {
lu.0 = 0;
cd.resource = (cd.resource + 1).clamp(0, 4);
}
}
}
fn update_map_mesh(
cells: Query<&CellData>,
2024-12-22 21:02:45 +00:00
mut map: Query<(&Mesh2d, &mut MapColors), With<MapMarker>>,
mut meshes: ResMut<Assets<Mesh>>
) {
let (mesh, mut cols) = map.single_mut();
if let Some(mesh) = meshes.get_mut(mesh) {
2024-12-23 18:05:35 +00:00
let mut modified = false;
2025-01-11 14:09:32 +00:00
for cd in cells.iter() {
2024-12-22 21:02:45 +00:00
let col = cd.color();
2024-12-22 21:19:07 +00:00
for id in cd.vertices.iter() {
2025-01-09 19:56:50 +00:00
modified = modified || cols.0[*id] != col;
2024-12-22 21:02:45 +00:00
cols.0[*id] = col.clone();
}
}
2024-12-23 18:05:35 +00:00
if modified {
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
}
2024-12-22 21:02:45 +00:00
}
2024-12-23 21:07:31 +00:00
}