better expansion (optimization)
This commit is contained in:
parent
a181cbf7ad
commit
22f59b059e
74
src/map.rs
74
src/map.rs
@ -26,21 +26,27 @@ use picking::*;
|
||||
pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup)
|
||||
app.add_plugins(cells::Plugin)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
|
||||
.add_systems(Update, (update_map_mesh, cells_regeneration, expand))
|
||||
.add_systems(Update, update_map_mesh)
|
||||
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
|
||||
.insert_resource(Seed(thread_rng().gen()))
|
||||
.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
||||
.insert_resource(Seed(thread_rng().gen()));
|
||||
}
|
||||
}
|
||||
|
||||
pub const HEIGHT: f32 = 16.;
|
||||
pub const WIDTH: f32 = 16.;
|
||||
/// Determined empirically
|
||||
pub const AVERAGE_NEIGHBORS_NUMBER: f64 = 6.;
|
||||
|
||||
pub const HEIGHT: f64 = 16.;
|
||||
pub const WIDTH: f64 = 16.;
|
||||
pub const REAL_HEIGHT: f32 = 8000.;
|
||||
pub const REAL_WIDTH: f32 = 8000.;
|
||||
pub const CELL_AREA: f32 = REAL_HEIGHT * REAL_WIDTH / SIZE as f32;
|
||||
pub const SIZE: usize = 100_000;
|
||||
pub const CELL_AREA: f32 = REAL_HEIGHT * REAL_WIDTH / CELLS as f32;
|
||||
pub const CELLS_TARGET_NUMBER: usize = 100_000;
|
||||
pub const CHUNKS_RESOLUTION: usize = 2;
|
||||
pub const CELLS_PER_CHUNK: usize = CELLS_TARGET_NUMBER / CHUNKS_RESOLUTION.pow(2);
|
||||
pub const CELLS: usize = CELLS_PER_CHUNK * CHUNKS_RESOLUTION.pow(2);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Seed(u32);
|
||||
@ -68,24 +74,41 @@ fn setup(
|
||||
seed: Res<Seed>,
|
||||
) {
|
||||
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 as u64);
|
||||
let mut sites = Vec::with_capacity(SIZE);
|
||||
for _ in 0..SIZE {
|
||||
let mut sites = Vec::with_capacity(CELLS);
|
||||
for chunk_x in 0..CHUNKS_RESOLUTION {
|
||||
let min_x = WIDTH / 2. * ((2. * (chunk_x as f64) / (CHUNKS_RESOLUTION as f64)) - 1.);
|
||||
let max_x = min_x + (WIDTH as f64 / CHUNKS_RESOLUTION as f64);
|
||||
for chunk_y in 0..CHUNKS_RESOLUTION {
|
||||
let min_y = HEIGHT / 2. * ((2. * (chunk_y as f64) / (CHUNKS_RESOLUTION as f64)) - 1.);
|
||||
let max_y = min_y + (HEIGHT as f64 / CHUNKS_RESOLUTION as f64);
|
||||
println!(
|
||||
"[{},{}] x:[{}, {}], y:[{}, {}]",
|
||||
chunk_x, chunk_y, min_x, max_x, min_y, max_y
|
||||
);
|
||||
for _ in 0..CELLS_PER_CHUNK {
|
||||
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,
|
||||
})
|
||||
x: rng.gen_range(min_x..max_x),
|
||||
y: rng.gen_range(min_y..max_y),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// dbg!(sites.len());
|
||||
|
||||
let voronoi = VoronoiBuilder::default()
|
||||
.set_sites(sites)
|
||||
.set_bounding_box(BoundingBox::new_centered(WIDTH as f64, HEIGHT as f64))
|
||||
.set_bounding_box(BoundingBox::new_centered(WIDTH, HEIGHT))
|
||||
.set_lloyd_relaxation_iterations(3)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut cells = Vec::with_capacity(SIZE);
|
||||
|
||||
let mut cells = Vec::with_capacity(CELLS);
|
||||
let z_noise = Fbm::<Perlin>::new(seed.0);
|
||||
let moisture_noise = Fbm::<Perlin>::new(seed.0 + 1).set_frequency(2.);
|
||||
for i in 0..SIZE {
|
||||
let mut res = 0;
|
||||
for i in 0..CELLS {
|
||||
let c = voronoi.cell(i);
|
||||
res += c.iter_neighbors().count();
|
||||
let site = c.site_position();
|
||||
let z = get_altitude(&z_noise, &[site.x as f32, site.y as f32]);
|
||||
let _m = (
|
||||
@ -108,6 +131,7 @@ fn setup(
|
||||
vertices: vec![],
|
||||
});
|
||||
}
|
||||
dbg!(res as f64 / CELLS as f64);
|
||||
|
||||
let mut poss = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
@ -181,11 +205,11 @@ fn setup(
|
||||
MapMeshColors(colors),
|
||||
MeshNeedsUpdate(true),
|
||||
MapMarker,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
));
|
||||
|
||||
for cell in cells {
|
||||
let kind = cell.kind;
|
||||
let mut cmd = parent.spawn(cell);
|
||||
let mut cmd = cmds.spawn(cell);
|
||||
match kind {
|
||||
CellKind::Grass | CellKind::Forest => {
|
||||
cmd.insert((
|
||||
@ -201,7 +225,7 @@ fn setup(
|
||||
cmd.observe(self::cells::on_click);
|
||||
cells_entities.push(cmd.id());
|
||||
}
|
||||
});
|
||||
|
||||
cmds.insert_resource(Voronoi(voronoi));
|
||||
cmds.insert_resource(CellsEntities(cells_entities));
|
||||
}
|
||||
@ -219,10 +243,12 @@ fn update_map_mesh(
|
||||
for (cell, wealth) in cells.iter() {
|
||||
// let col: [f32; 4] = [rng.gen(), rng.gen(), rng.gen(), 1.];
|
||||
let col = cell.color(wealth.map(|w| w.0).unwrap_or_default());
|
||||
for id in cell.vertices.iter() {
|
||||
modified = modified || cols.0[*id] != col;
|
||||
cols.0[*id] = col.clone();
|
||||
}
|
||||
modified = modified || cols.0[cell.voronoi_id] != col;
|
||||
cols.0[cell.voronoi_id] = col;
|
||||
// for id in cell.vertices.iter() {
|
||||
// modified = modified || cols.0[*id] != col;
|
||||
// cols.0[*id] = col.clone();
|
||||
// }
|
||||
}
|
||||
if modified {
|
||||
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
|
||||
|
@ -1,13 +1,31 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::{prelude::*, time::common_conditions::on_timer};
|
||||
use rand::{seq::IteratorRandom, thread_rng, Rng};
|
||||
|
||||
use crate::{time::GameTime, ui::CurrentAction};
|
||||
|
||||
use super::{animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi};
|
||||
use super::{
|
||||
animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi,
|
||||
AVERAGE_NEIGHBORS_NUMBER, CELL_AREA,
|
||||
};
|
||||
|
||||
pub mod material;
|
||||
use material::CellMaterial;
|
||||
|
||||
pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
cells_regeneration.run_if(on_timer(Duration::from_secs(1))),
|
||||
expand.run_if(on_timer(Duration::from_secs(1))),
|
||||
),
|
||||
)
|
||||
.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CellKind {
|
||||
@ -79,6 +97,7 @@ pub struct Regeneration {
|
||||
pub full_growth_duration: Duration,
|
||||
}
|
||||
|
||||
const STEP: u8 = u8::MAX / 4;
|
||||
pub fn cells_regeneration(
|
||||
mut cells: Query<(&mut Regeneration, &mut Wealth)>,
|
||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||
@ -86,12 +105,17 @@ pub fn cells_regeneration(
|
||||
) {
|
||||
let mut map_needs_update = map_needs_update.single_mut();
|
||||
for (mut regen, mut wealth) in cells.iter_mut() {
|
||||
if gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
||||
regen.last_update = gt.current;
|
||||
while gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
||||
regen.last_update = gt
|
||||
.current
|
||||
.min(regen.last_update + (regen.full_growth_duration / u8::MAX as u32));
|
||||
wealth.0 = wealth.0.saturating_add(1);
|
||||
// Not update map each time for optimization
|
||||
if wealth.0 % STEP == 0 {
|
||||
map_needs_update.0 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
@ -113,28 +137,43 @@ pub fn expand(
|
||||
wealth.unwrap().0 as f64 / 255.
|
||||
};
|
||||
if random.gen_bool(
|
||||
(t.elapsed_secs_f64() * (gt.speed as f64) * wealth / (60. * 60. * 24. * 30.))
|
||||
dbg!(
|
||||
t.elapsed_secs_f64() * (gt.speed as f64) * wealth * 1e-7
|
||||
/ (CELL_AREA as f64).sqrt()
|
||||
)
|
||||
.min(1.),
|
||||
) {
|
||||
// Let say that grass takes 1 months to expand
|
||||
// "Grass can expand into adjacent dirt at a rate of 2.5 to 7.5 cm (1 to 3 inches) per month.", DeepSeek
|
||||
// With some (way too complicated and probably false) computation, we get for a good grass an extension rate of (1e-7 / sqrt(cell_area)) cells per seconds
|
||||
let target = voronoi
|
||||
.0
|
||||
.cell(cell.voronoi_id)
|
||||
.iter_neighbors()
|
||||
.choose(&mut random)
|
||||
.unwrap();
|
||||
changes.push((target, CellKind::Grass));
|
||||
changes.extend(
|
||||
voronoi
|
||||
.0
|
||||
.cell(cell.voronoi_id)
|
||||
.iter_neighbors()
|
||||
.map(|t| (t, CellKind::Grass)),
|
||||
);
|
||||
}
|
||||
}
|
||||
if cell.kind == CellKind::Forest {
|
||||
if random.gen_bool(
|
||||
(t.elapsed_secs_f64()
|
||||
dbg!(
|
||||
t.elapsed_secs_f64()
|
||||
* (gt.speed as f64)
|
||||
* (wealth.unwrap().0 as f64 / 255.).sqrt()
|
||||
/ (60. * 60. * 24. * 365. * 5.))
|
||||
* 5.6e-7
|
||||
/ (CELL_AREA as f64).sqrt()
|
||||
* AVERAGE_NEIGHBORS_NUMBER
|
||||
)
|
||||
.min(1.),
|
||||
) {
|
||||
// Let say that forest takes 5 years to expand
|
||||
// "Forests can expand at a rate of 1 to 10 meters per year", DeepSeek
|
||||
// Same computations as above : (5.6e-7 / sqrt(cell_area)) cells per seconds
|
||||
let target = voronoi
|
||||
.0
|
||||
.cell(cell.voronoi_id)
|
||||
|
Loading…
Reference in New Issue
Block a user