From 97f2497940d53b064f8c22b5570403b5be8f3020 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sat, 6 Jul 2024 20:41:50 -0400 Subject: [PATCH] New modular biomes system and updated world generator --- engine/world_generation/src/biome_asset.rs | 14 +- engine/world_generation/src/biome_painter.rs | 44 ++++-- .../src/generators/packed_mesh_generator.rs | 7 +- engine/world_generation/src/heightmap.rs | 131 ++++++++++++----- engine/world_generation/src/map/biome_map.rs | 136 ++++++++++++----- engine/world_generation/src/map/chunk.rs | 8 +- engine/world_generation/src/map/config.rs | 13 +- engine/world_generation/src/map/map.rs | 4 +- engine/world_generation/src/states.rs | 1 + game/main/assets | 2 +- game/main/src/camera_system/camera_plugin.rs | 2 +- game/main/src/camera_system/components.rs | 4 +- game/main/src/map_rendering/chunk_rebuild.rs | 3 +- game/main/src/map_rendering/map_init.rs | 139 +++++++++--------- game/main/src/phos.rs | 5 +- game/main/src/utlis/chunk_utils.rs | 18 +-- 16 files changed, 341 insertions(+), 190 deletions(-) diff --git a/engine/world_generation/src/biome_asset.rs b/engine/world_generation/src/biome_asset.rs index dbd94d9..f612a47 100644 --- a/engine/world_generation/src/biome_asset.rs +++ b/engine/world_generation/src/biome_asset.rs @@ -1,7 +1,7 @@ use asset_loader::create_asset_loader; use serde::{Deserialize, Serialize}; -use crate::{prelude::GeneratorLayer, tile_mapper::TileMapperAsset}; +use crate::{prelude::NoiseConfig, tile_mapper::TileMapperAsset}; #[derive(Serialize, Deserialize, Asset, TypePath, Debug, Clone)] pub struct BiomeAsset { @@ -12,17 +12,13 @@ pub struct BiomeAsset { #[serde(skip)] pub tile_mapper: Handle, pub tile_mapper_path: String, - pub generator_layers: Vec, + pub noise: NoiseConfig, } impl BiomeAsset { - pub fn distance(&self, moisture: f32, temperature: f32, continentality: f32) -> f32 { - let a = Vec3::new( - self.moisture - moisture, - self.temperature - temperature, - self.continentality - continentality, - ); - return a.length(); + pub fn distance(&self, data: Vec3) -> f32 { + let a = Vec3::new(self.moisture, self.temperature, self.continentality); + return (a - data).length(); } } diff --git a/engine/world_generation/src/biome_painter.rs b/engine/world_generation/src/biome_painter.rs index eab74e8..15f9e4f 100644 --- a/engine/world_generation/src/biome_painter.rs +++ b/engine/world_generation/src/biome_painter.rs @@ -6,7 +6,7 @@ use bevy::{ }; use serde::{Deserialize, Serialize}; -use crate::biome_asset::BiomeAsset; +use crate::{biome_asset::BiomeAsset, map::biome_map::BiomeData}; #[derive(Serialize, Deserialize, Debug, TypePath, Asset, Clone)] pub struct BiomePainterAsset { @@ -16,20 +16,14 @@ pub struct BiomePainterAsset { } impl BiomePainterAsset { - pub fn sample_biome( - &self, - assets: &Assets, - moisture: f32, - temperature: f32, - continentality: f32, - ) -> Handle { + pub fn sample_biome(&self, assets: &Assets, data: &BiomeData) -> Handle { assert!(self.biomes.length() != 0, "There are no biomes"); let mut biome = self.biomes.first().unwrap().clone(); let mut dist = f32::INFINITY; for b in &self.biomes { let asset = assets.get(b).unwrap(); - let d = asset.distance(moisture, temperature, continentality); + let d = asset.distance(data.into()); if d < dist { biome = b.clone(); dist = d; @@ -38,6 +32,38 @@ impl BiomePainterAsset { return biome; } + + pub fn build(&self, assets: &Assets) -> BiomePainter { + let mut biomes = Vec::with_capacity(self.biomes.len()); + for b in &self.biomes { + let asset = assets.get(b.clone()).unwrap(); + biomes.push(asset.clone()); + } + return BiomePainter { biomes }; + } +} + +#[derive(Resource)] +pub struct BiomePainter { + pub biomes: Vec, +} + +impl BiomePainter { + pub fn sample_biome(&self, data: &BiomeData) -> &BiomeAsset { + assert!(self.biomes.length() != 0, "There are no biomes"); + let mut biome = &self.biomes[0]; + let mut dist = f32::INFINITY; + + for b in &self.biomes { + let d = b.distance(data.into()); + if d < dist { + biome = b; + dist = d; + } + } + + return biome; + } } create_asset_loader!( diff --git a/engine/world_generation/src/generators/packed_mesh_generator.rs b/engine/world_generation/src/generators/packed_mesh_generator.rs index 916bd17..a80edf3 100644 --- a/engine/world_generation/src/generators/packed_mesh_generator.rs +++ b/engine/world_generation/src/generators/packed_mesh_generator.rs @@ -27,14 +27,11 @@ pub fn generate_packed_chunk_mesh( for z in 0..Chunk::SIZE { for x in 0..Chunk::SIZE { let height = chunk.heights[x + z * Chunk::SIZE]; - let moisture = chunk.moisture[x + z * Chunk::SIZE]; - let temperature = chunk.temperature[x + z * Chunk::SIZE]; + let data = chunk.biome_data[x + z * Chunk::SIZE]; let coord = HexCoord::from_offset(IVec2::new(x as i32, z as i32) + (chunk.chunk_offset * Chunk::SIZE as i32)); let n = map.get_neighbors(&coord); - let biome = biomes - .get(painter.sample_biome(biomes, moisture, temperature, 1.)) - .unwrap(); + let biome = biomes.get(painter.sample_biome(biomes, &data)).unwrap(); let mapper = mappers.get(biome.tile_mapper.clone()); let tile_handle = mapper.unwrap().sample_tile(height); diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index 94f7dea..c036a43 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -1,19 +1,24 @@ use bevy::math::IVec2; use bevy::prelude::{FloatExt, Vec2}; use bevy::utils::default; +use bevy::utils::petgraph::data; use noise::{NoiseFn, SuperSimplex}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use crate::biome_painter::{self, BiomePainter}; +use crate::map::biome_map::{self, BiomeChunk, BiomeData, BiomeMap}; use crate::prelude::*; -pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32) -> Map { +pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> Map { + let biomes = &generate_biomes(cfg, seed); // let mut chunks: Vec = Vec::with_capacity(cfg.size.length_squared() as usize); let chunks = (0..cfg.size.y) .into_par_iter() .flat_map(|z| { - (0..cfg.size.x) - .into_par_iter() - .map(move |x| generate_chunk(x as f64, z as f64, cfg, seed)) + (0..cfg.size.x).into_par_iter().map(move |x| { + let biome_chunk = &biomes.chunks[x as usize + z as usize * cfg.size.x as usize]; + return generate_chunk(x, z, cfg, seed, &biome_chunk, painter); + }) }) .collect(); return Map { @@ -24,49 +29,104 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32) -> Map { }; } -pub fn generate_chunk(chunk_x: f64, chunk_z: f64, cfg: &GenerationConfig, seed: u32) -> Chunk { +pub fn generate_biomes(cfg: &GenerationConfig, seed: u32) -> BiomeMap { + let mut map = BiomeMap::new(cfg.size, 8); + map.chunks = (0..cfg.size.y) + .into_par_iter() + .flat_map(|y| { + (0..cfg.size.x).into_par_iter().map(move |x| { + return generate_biome_chunk(x as usize, y as usize, cfg, seed); + }) + }) + .collect(); + map.blend(cfg.biome_blend); + return map; +} + +pub fn generate_biome_chunk(chunk_x: usize, chunk_y: usize, cfg: &GenerationConfig, seed: u32) -> BiomeChunk { + let mut chunk = BiomeChunk { + data: [BiomeData::default(); Chunk::AREA], + tiles: Vec::with_capacity(Chunk::AREA), + }; + let noise_m = SuperSimplex::new(seed + 1); + let noise_t = SuperSimplex::new(seed + 2); + let noise_c = SuperSimplex::new(seed + 3); + + for z in 0..Chunk::SIZE { + for x in 0..Chunk::SIZE { + let moisture = sample_point( + x as f64 + chunk_x as f64 * Chunk::SIZE as f64, + z as f64 + chunk_y as f64 * Chunk::SIZE as f64, + &cfg.moisture_layer, + &noise_m, + cfg.size.as_vec2(), + cfg.border_size, + ); + let temperature = sample_point( + x as f64 + chunk_x as f64 * Chunk::SIZE as f64, + z as f64 + chunk_y as f64 * Chunk::SIZE as f64, + &cfg.temperature_layer, + &noise_t, + cfg.size.as_vec2(), + cfg.border_size, + ); + let continentality = sample_point( + x as f64 + chunk_x as f64 * Chunk::SIZE as f64, + z as f64 + chunk_y as f64 * Chunk::SIZE as f64, + &cfg.continent_layer, + &noise_c, + cfg.size.as_vec2(), + cfg.border_size, + ); + chunk.data[x + z * Chunk::SIZE] = BiomeData { + moisture, + temperature, + continentality, + }; + } + } + + return chunk; +} + +pub fn generate_chunk( + chunk_x: u32, + chunk_z: u32, + cfg: &GenerationConfig, + seed: u32, + biome_chunk: &BiomeChunk, + biome_painter: &BiomePainter, +) -> Chunk { let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::SIZE * Chunk::SIZE]; - let mut moisture = [0.; Chunk::SIZE * Chunk::SIZE]; - let mut temp = [0.; Chunk::SIZE * Chunk::SIZE]; + let mut data = [BiomeData::default(); Chunk::SIZE * Chunk::SIZE]; let noise = SuperSimplex::new(seed); for z in 0..Chunk::SIZE { for x in 0..Chunk::SIZE { + let biome_data = biome_chunk.get_biome_data(x, z); + let biome = biome_painter.sample_biome(biome_data); let sample = sample_point( - x as f64 + chunk_x * Chunk::SIZE as f64, - z as f64 + chunk_z * Chunk::SIZE as f64, - &cfg, + x as f64 + chunk_x as f64 * Chunk::SIZE as f64, + z as f64 + chunk_z as f64 * Chunk::SIZE as f64, + &biome.noise, &noise, + cfg.size.as_vec2(), + cfg.border_size, ); result[x + z * Chunk::SIZE] = sample; - moisture[x + z * Chunk::SIZE] = noise.get([ - (x as f64 + chunk_x * Chunk::SIZE as f64) / &cfg.noise_scale, - (z as f64 + chunk_z * Chunk::SIZE as f64) / &cfg.noise_scale, - ]) as f32; - temp[x + z * Chunk::SIZE] = - sample_tempurature(z as f32 + chunk_z as f32 * Chunk::SIZE as f32, sample, &cfg, 100.); + data[x + z * Chunk::SIZE] = biome_data.clone(); } } return Chunk { heights: result, - moisture: moisture, - temperature: temp, + biome_data: data, chunk_offset: IVec2::new(chunk_x as i32, chunk_z as i32), ..default() }; } -fn sample_tempurature(z: f32, height: f32, cfg: &GenerationConfig, equator: f32) -> f32 { - let d = (equator - z).abs(); - let max_d = equator.max(cfg.get_total_height() as f32 - equator); - let t_mod = d.remap(0., max_d, 0., 1.).clamp(0., 1.); - - // let max_d = d.max() - return (height.remap(0., 50., 0., 1.).clamp(0., 1.) + t_mod) / 2.; -} - -fn sample_point(x: f64, z: f64, cfg: &GenerationConfig, noise: &impl NoiseFn) -> f32 { - let x_s = x / cfg.noise_scale; - let z_s = z / cfg.noise_scale; +fn sample_point(x: f64, z: f64, cfg: &NoiseConfig, noise: &impl NoiseFn, size: Vec2, border_size: f32) -> f32 { + let x_s = x / cfg.scale; + let z_s = z / cfg.scale; let mut elevation: f64 = 0.; let mut first_layer: f64 = 0.; @@ -82,26 +142,25 @@ fn sample_point(x: f64, z: f64, cfg: &GenerationConfig, noise: &impl NoiseFn f64 { - let m = (mask - sea_level).max(0.); - return value * m; +fn mask(mask: f64, value: f64) -> f64 { + return value * mask; } fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn) -> f64 { diff --git a/engine/world_generation/src/map/biome_map.rs b/engine/world_generation/src/map/biome_map.rs index ae6815a..3bfbf10 100644 --- a/engine/world_generation/src/map/biome_map.rs +++ b/engine/world_generation/src/map/biome_map.rs @@ -1,5 +1,5 @@ -use bevy::math::UVec2; -use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use bevy::math::{UVec2, Vec3}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use super::chunk::Chunk; @@ -7,16 +7,36 @@ pub struct BiomeMap { pub height: usize, pub width: usize, pub biome_count: usize, - pub tiles: Vec>, + pub chunks: Vec, +} + +#[derive(Default, Clone, Copy)] +pub struct BiomeData { + pub moisture: f32, + pub temperature: f32, + pub continentality: f32, +} + +impl Into for &BiomeData { + fn into(self) -> Vec3 { + return Vec3::new(self.moisture, self.temperature, self.continentality); + } +} + +impl Into for BiomeData { + fn into(self) -> Vec3 { + return Vec3::new(self.moisture, self.temperature, self.continentality); + } } impl BiomeMap { pub fn new(size: UVec2, biome_count: usize) -> Self { + let len = size.x as usize * size.y as usize * Chunk::AREA; return BiomeMap { height: size.y as usize * Chunk::SIZE, width: size.x as usize * Chunk::SIZE, biome_count, - tiles: Vec::with_capacity(size.x as usize * size.y as usize * Chunk::AREA), + chunks: Vec::with_capacity(len), }; } @@ -28,35 +48,44 @@ impl BiomeMap { } fn blend_once(&mut self) { - let t: Vec<_> = (0..self.height) + let c: Vec = (0..self.chunks.len()) .into_par_iter() - .map(|y| { - let mut new_tiles = Vec::with_capacity(self.width); - for x in 0..self.width { - let kernel = self.get_kernel(x as i32, y as i32); - let r = kernel - .iter() - .filter_map(|f| *f) - .fold(vec![0.; self.biome_count], |a, b| { - return a.iter().zip(b).map(|v| v.0 + v.1).collect(); - }); + .map(|i| &self.chunks[i]) + .map(|chunk| { + let tiles: Vec<_> = (0..Chunk::SIZE) + .into_par_iter() + .map(|y| { + let mut new_tiles = Vec::with_capacity(self.width); + for x in 0..Chunk::SIZE { + let kernel = self.get_kernel(x as i32, y as i32); + let r = kernel + .iter() + .filter_map(|f| *f) + .fold(vec![0.; self.biome_count], |a, b| { + return a.iter().zip(b).map(|v| v.0 + v.1).collect(); + }); - let sum: f32 = r.iter().sum(); - if sum == 0. { - new_tiles.push(vec![0.; self.biome_count]); - continue; - } - new_tiles.push(r.iter().map(|f| f / sum).collect()); - } - return new_tiles; + let sum: f32 = r.iter().sum(); + if sum == 0. { + new_tiles.push(vec![0.; self.biome_count]); + continue; + } + new_tiles.push(r.iter().map(|f| f / sum).collect()); + } + return new_tiles; + }) + .flatten() + .collect(); + return BiomeChunk { + tiles, + data: chunk.data, + }; }) - .flatten() .collect(); - - self.tiles = t; + self.chunks = c; } - pub fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec>; 9] { + fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec>; 9] { return [ self.get_biome(x - 1, y - 1), self.get_biome(x, y - 1), @@ -78,7 +107,37 @@ impl BiomeMap { return None; } - return Some(&self.tiles[x as usize + y as usize * self.width]); + let cx = x as usize / Chunk::SIZE; + let cy = y as usize / Chunk::SIZE; + + let chunk = &self.chunks[cx + cy * Chunk::SIZE]; + + return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE)); + } + + pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { + let cx = x / Chunk::SIZE; + let cy = y / Chunk::SIZE; + + let chunk = &self.chunks[cx + cy * Chunk::SIZE]; + + return chunk.get_biome_data(x - cx * Chunk::SIZE, y - cy * Chunk::SIZE); + } +} + +#[derive(Clone)] +pub struct BiomeChunk { + pub tiles: Vec>, + pub data: [BiomeData; Chunk::AREA], +} + +impl BiomeChunk { + pub fn get_biome(&self, x: usize, y: usize) -> &Vec { + return &self.tiles[x as usize + y as usize * Chunk::SIZE]; + } + + pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { + return &self.data[x + y * Chunk::SIZE]; } } @@ -90,18 +149,27 @@ mod tests { #[test] fn biome_blend() { let mut biome = BiomeMap::new(UVec2::ONE * 16, 8); + let w = biome.width / Chunk::SIZE; + let h = biome.height / Chunk::SIZE; - for y in 0..biome.height { - for x in 0..biome.width { + for y in 0..h { + for x in 0..w { let mut b = vec![0.; biome.biome_count]; - let i = x / Chunk::SIZE; - let z = y / Chunk::SIZE; - let idx = (i + z) % biome.biome_count; + let idx = (x + y) % biome.biome_count; b[idx] = 1.; - biome.tiles.push(b); + biome.chunks.push(generate_chunk(x, y, b)); } } biome.blend(8); } + + fn generate_chunk(x: usize, y: usize, biome: Vec) -> BiomeChunk { + let chunk = BiomeChunk { + data: [BiomeData::default(); Chunk::AREA], + tiles: (0..Chunk::AREA).into_iter().map(|_| biome.clone()).collect(), + }; + + return chunk; + } } diff --git a/engine/world_generation/src/map/chunk.rs b/engine/world_generation/src/map/chunk.rs index 316855a..edf414a 100644 --- a/engine/world_generation/src/map/chunk.rs +++ b/engine/world_generation/src/map/chunk.rs @@ -1,12 +1,13 @@ use crate::hex_utils::SHORT_DIAGONAL; use bevy::prelude::*; +use super::biome_map::BiomeData; + #[derive(Clone)] pub struct Chunk { pub heights: [f32; Chunk::AREA], pub textures: [[u32; 2]; Chunk::AREA], - pub moisture: [f32; Chunk::AREA], - pub temperature: [f32; Chunk::AREA], + pub biome_data: [BiomeData; Chunk::AREA], pub chunk_offset: IVec2, } @@ -15,8 +16,7 @@ impl Default for Chunk { Self { heights: [0.; Chunk::AREA], textures: [[0; 2]; Chunk::AREA], - moisture: [0.; Chunk::AREA], - temperature: [0.; Chunk::AREA], + biome_data: [BiomeData::default(); Chunk::AREA], chunk_offset: Default::default(), } } diff --git a/engine/world_generation/src/map/config.rs b/engine/world_generation/src/map/config.rs index 201135d..f7b07db 100644 --- a/engine/world_generation/src/map/config.rs +++ b/engine/world_generation/src/map/config.rs @@ -7,12 +7,13 @@ use super::chunk::Chunk; #[derive(Resource, Reflect, Default)] #[reflect(Resource)] pub struct GenerationConfig { - pub noise_scale: f64, pub sea_level: f64, pub border_size: f32, pub biome_blend: usize, + pub moisture_layer: NoiseConfig, + pub temperature_layer: NoiseConfig, + pub continent_layer: NoiseConfig, pub size: UVec2, - pub layers: Vec, } impl GenerationConfig { @@ -24,7 +25,13 @@ impl GenerationConfig { } } -#[derive(Reflect, InspectorOptions, Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Default, Reflect, Clone, Debug)] +pub struct NoiseConfig { + pub scale: f64, + pub layers: Vec, +} + +#[derive(Reflect, InspectorOptions, Serialize, Deserialize, Debug, Clone, Default)] pub struct GeneratorLayer { pub strength: f64, pub min_value: f64, diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index 914035d..35b2d17 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -58,12 +58,12 @@ impl Map { pub fn get_moisture(&self, pos: &HexCoord) -> f32 { let chunk = &self.chunks[pos.to_chunk_index(self.width)]; - return chunk.moisture[pos.to_chunk_local_index()]; + return chunk.biome_data[pos.to_chunk_local_index()].moisture; } pub fn get_tempurature(&self, pos: &HexCoord) -> f32 { let chunk = &self.chunks[pos.to_chunk_index(self.width)]; - return chunk.temperature[pos.to_chunk_local_index()]; + return chunk.biome_data[pos.to_chunk_local_index()].temperature; } pub fn get_center(&self) -> Vec3 { diff --git a/engine/world_generation/src/states.rs b/engine/world_generation/src/states.rs index 4376f9a..723848d 100644 --- a/engine/world_generation/src/states.rs +++ b/engine/world_generation/src/states.rs @@ -2,6 +2,7 @@ use bevy::ecs::schedule::States; #[derive(States, Debug, Clone, PartialEq, Eq, Hash)] pub enum GeneratorState { + Startup, GenerateHeightmap, SpawnMap, Idle, diff --git a/game/main/assets b/game/main/assets index ad2a485..f809b72 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit ad2a485fc589170c82839c43253e481fef005f74 +Subproject commit f809b7232a4395824d16c3ffef624f280987c66a diff --git a/game/main/src/camera_system/camera_plugin.rs b/game/main/src/camera_system/camera_plugin.rs index 8423ebf..5e40d29 100644 --- a/game/main/src/camera_system/camera_plugin.rs +++ b/game/main/src/camera_system/camera_plugin.rs @@ -19,7 +19,7 @@ impl Plugin for PhosCameraPlugin { app.add_systems(PreStartup, setup); app.add_systems(Update, rts_camera_system.run_if(in_state(MenuState::InGame))); - app.add_systems(PostUpdate, limit_camera_bounds); + app.add_systems(PostUpdate, limit_camera_bounds.run_if(in_state(MenuState::InGame))); //Free Cam //app.add_systems(Update, (grab_mouse, (update_camera, update_camera_mouse).chain())); diff --git a/game/main/src/camera_system/components.rs b/game/main/src/camera_system/components.rs index d713ac9..ec83e77 100644 --- a/game/main/src/camera_system/components.rs +++ b/game/main/src/camera_system/components.rs @@ -16,8 +16,8 @@ impl Default for PhosCamera { fn default() -> Self { Self { min_height: 10., - max_height: 120., - speed: 30., + max_height: 420., + speed: 100., zoom_speed: 0.3, min_angle: (20. as f32).to_radians(), max_angle: 1., diff --git a/game/main/src/map_rendering/chunk_rebuild.rs b/game/main/src/map_rendering/chunk_rebuild.rs index 2ae8ea1..67bb9ce 100644 --- a/game/main/src/map_rendering/chunk_rebuild.rs +++ b/game/main/src/map_rendering/chunk_rebuild.rs @@ -5,6 +5,7 @@ use bevy::utils::futures; use bevy_rapier3d::geometry::Collider; use bevy_rapier3d::geometry::TriMeshFlags; use world_generation::prelude::Map; +use world_generation::states::GeneratorState; use crate::prelude::RebuildChunk; use crate::{ @@ -18,7 +19,7 @@ impl Plugin for ChunkRebuildPlugin { fn build(&self, app: &mut App) { app.insert_resource(ChunkRebuildQueue::default()); app.init_resource::(); - app.add_systems(PreUpdate, chunk_rebuilder); + app.add_systems(PreUpdate, chunk_rebuilder.run_if(in_state(GeneratorState::SpawnMap))); app.add_systems(PostUpdate, collider_task_resolver); } } diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 359bc38..4ed9595 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -1,7 +1,7 @@ #[cfg(feature = "tracing")] use bevy::log::*; use bevy::{ - asset::{LoadState, StrongHandle}, + asset::LoadState, pbr::{ExtendedMaterial, NotShadowCaster}, prelude::*, }; @@ -9,7 +9,7 @@ use bevy_inspector_egui::quick::ResourceInspectorPlugin; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use shared::states::{GameplayState, MenuState}; use world_generation::{ - biome_asset::BiomeAsset, + biome_asset::{BiomeAsset, BiomeAssetLoadState}, biome_painter::*, heightmap::generate_heightmap, hex_utils::{offset_to_index, SHORT_DIAGONAL}, @@ -39,7 +39,7 @@ pub struct MapInitPlugin; impl Plugin for MapInitPlugin { fn build(&self, app: &mut App) { - app.insert_state(GeneratorState::GenerateHeightmap); + app.insert_state(GeneratorState::Startup); app.insert_state(AssetLoadState::StartLoading); app.add_plugins(ResourceInspectorPlugin::::default()); @@ -57,18 +57,17 @@ impl Plugin for MapInitPlugin { )); app.add_systems(Startup, (load_textures, load_tiles).in_set(AssetLoaderSet)); - app.add_systems(Startup, create_heightmap.in_set(GeneratorSet)); - app.configure_sets(Startup, AssetLoaderSet.run_if(in_state(AssetLoadState::StartLoading))); - app.configure_sets( - Startup, - GeneratorSet.run_if(in_state(GeneratorState::GenerateHeightmap)), + + app.add_systems( + Update, + create_heightmap.run_if(in_state(GeneratorState::GenerateHeightmap)), ); app.add_systems(Update, check_asset_load.run_if(in_state(AssetLoadState::Loading))); app.add_systems( Update, - finalize_texture.run_if(in_state(AssetLoadState::FinalizeAssets)), + (finalize_texture, finalize_biome_painter).run_if(in_state(AssetLoadState::FinalizeAssets)), ); app.add_systems(Update, despawn_map.run_if(in_state(GeneratorState::Regenerate))); app.add_systems( @@ -82,8 +81,6 @@ impl Plugin for MapInitPlugin { } } -#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] -struct GeneratorSet; #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] struct AssetLoaderSet; @@ -137,11 +134,16 @@ fn check_asset_load( atlas: Res, painter: Res, painter_load: Res, + biome_load: Res, tile_load: Res, mapper_load: Res, mut next_state: ResMut>, ) { - if !painter_load.is_all_loaded() || !tile_load.is_all_loaded() || !mapper_load.is_all_loaded() { + if !painter_load.is_all_loaded() + || !tile_load.is_all_loaded() + || !mapper_load.is_all_loaded() + || !biome_load.is_all_loaded() + { return; } @@ -155,11 +157,30 @@ fn check_asset_load( next_state.set(AssetLoadState::FinalizeAssets); } +fn finalize_biome_painter( + mut commands: Commands, + mut next_generator_state: ResMut>, + painter: Res, + painter_load: Res, + biome_load: Res, + biome_painters: Res>, + biome_assets: Res>, +) { + if !painter_load.is_all_loaded() || !biome_load.is_all_loaded() { + return; + } + + let painter_asset = biome_painters.get(painter.handle.clone()).unwrap(); + let biome_painter = painter_asset.build(&biome_assets); + commands.insert_resource(biome_painter); + next_generator_state.set(GeneratorState::GenerateHeightmap); +} + fn finalize_texture( mut atlas: ResMut, mut images: ResMut>, mut chunk_materials: ResMut>>, - mut next_state: ResMut>, + mut next_load_state: ResMut>, ) { let image = images.get_mut(&atlas.handle).unwrap(); @@ -175,21 +196,23 @@ fn finalize_texture( }); atlas.chunk_material_handle = chunk_material; - next_state.set(AssetLoadState::LoadComplete); + next_load_state.set(AssetLoadState::LoadComplete); } fn create_heightmap( mut commands: Commands, mut cam: Query<(&mut Transform, Entity), With>, mut next_state: ResMut>, + biome_painter: Res, ) { let config = GenerationConfig { biome_blend: 3, - layers: vec![ - GeneratorLayer { + continent_layer: NoiseConfig { + scale: 450., + layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, - strength: 8.3, + strength: 100., min_value: -0.2, persistence: 0.77, is_rigid: false, @@ -197,63 +220,44 @@ fn create_heightmap( weight_multi: 0., layers: 4, first_layer_mask: false, - }, - GeneratorLayer { - base_roughness: 2.85, - roughness: 2., - strength: -0.23, - min_value: -0., - persistence: 1., + }], + }, + moisture_layer: NoiseConfig { + scale: 450., + layers: vec![GeneratorLayer { + base_roughness: 2.14, + roughness: 0.87, + strength: 100., + min_value: -0.2, + persistence: 0.77, is_rigid: false, weight: 0., weight_multi: 0., layers: 4, first_layer_mask: false, - }, - GeneratorLayer { - base_roughness: 2.6, - roughness: 4., - strength: 3.1, - min_value: 0., - persistence: 1.57, - is_rigid: true, - weight: 1., - weight_multi: 0.35, + }], + }, + temperature_layer: NoiseConfig { + scale: 450., + layers: vec![GeneratorLayer { + base_roughness: 2.14, + roughness: 0.87, + strength: 100., + min_value: -0.2, + persistence: 0.77, + is_rigid: false, + weight: 0., + weight_multi: 0., layers: 4, - first_layer_mask: true, - }, - GeneratorLayer { - base_roughness: 3.87, - roughness: 5.8, - strength: -1., - min_value: 0., - persistence: 0., - is_rigid: true, - weight: 1., - weight_multi: 4.57, - layers: 3, - first_layer_mask: true, - }, - GeneratorLayer { - base_roughness: 3.87, - roughness: 5.8, - strength: -1.5, - min_value: 0., - persistence: 0.3, - is_rigid: true, - weight: 1., - weight_multi: 4.57, - layers: 3, first_layer_mask: false, - }, - ], - noise_scale: 450., + }], + }, sea_level: 8.5, border_size: 64., size: UVec2::splat(1024 / Chunk::SIZE as u32), // size: UVec2::splat(1), }; - let heightmap = generate_heightmap(&config, 42069); + let heightmap = generate_heightmap(&config, 42069, &biome_painter); let (mut cam_t, cam_entity) = cam.single_mut(); cam_t.translation = heightmap.get_center(); @@ -272,17 +276,13 @@ fn spawn_map( atlas: Res, tile_assets: Res>, tile_mappers: Res>, - biome_painters: Res>, - biome_assets: Res>, - painter: Res, mut generator_state: ResMut>, cur_game_state: Res>, mut game_state: ResMut>, mut gameplay_state: ResMut>, + biome_painter: Res, ) { - let b_painter = biome_painters.get(painter.handle.clone()); - let cur_painter = b_painter.unwrap(); - paint_map(&mut heightmap, cur_painter, &tile_assets, &biome_assets, &tile_mappers); + paint_map(&mut heightmap, &biome_painter, &tile_assets, &tile_mappers); let chunk_meshes: Vec<_> = heightmap .chunks @@ -347,11 +347,12 @@ fn despawn_map( cfg: Res, chunks: Query>, mut next_state: ResMut>, + biome_painter: Res, ) { for chunk in chunks.iter() { commands.entity(chunk).despawn(); } - *heightmap = generate_heightmap(&cfg, 4); + *heightmap = generate_heightmap(&cfg, 4, &biome_painter); next_state.set(GeneratorState::SpawnMap); } diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index ebe60de..42d3e95 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -12,10 +12,10 @@ use bevy_rapier3d::plugin::{NoUserData, RapierPhysicsPlugin}; use buildings::BuildingPugin; use iyes_perf_ui::prelude::*; use shared::despawn::DespawnPuglin; -use shared::states::{MenuState, GameplayState}; -use world_generation::biome_painter::BiomePainterPlugin; +use shared::states::{GameplayState, MenuState}; use world_generation::tile_manager::TileAssetPlugin; use world_generation::tile_mapper::TileMapperAssetPlugin; +use world_generation::{biome_asset::BiomeAssetPlugin, biome_painter::BiomePainterPlugin}; pub struct PhosGamePlugin; @@ -48,6 +48,7 @@ impl Plugin for PhosGamePlugin { app.add_plugins(TileAssetPlugin); app.add_plugins(TileMapperAssetPlugin); app.add_plugins(BiomePainterPlugin); + app.add_plugins(BiomeAssetPlugin); //Physics app.add_plugins(RapierPhysicsPlugin::::default()); // app.add_plugins(RapierDebugRenderPlugin::default()); diff --git a/game/main/src/utlis/chunk_utils.rs b/game/main/src/utlis/chunk_utils.rs index 0aedf8d..0433848 100644 --- a/game/main/src/utlis/chunk_utils.rs +++ b/game/main/src/utlis/chunk_utils.rs @@ -9,8 +9,7 @@ use bevy::{ use bevy_rapier3d::geometry::{Collider, TriMeshFlags}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use world_generation::{ - biome_asset::BiomeAsset, - biome_painter::BiomePainterAsset, + biome_painter::BiomePainter, generators::{chunk_colliders::generate_chunk_collider, mesh_generator::generate_chunk_mesh}, hex_utils::offset_to_world, prelude::{Chunk, Map, MeshChunkData}, @@ -20,32 +19,27 @@ use world_generation::{ pub fn paint_map( map: &mut Map, - painter: &BiomePainterAsset, + painter: &BiomePainter, tiles: &Res>, - biomes: &Res>, mappers: &Res>, ) { map.chunks.par_iter_mut().for_each(|chunk: &mut Chunk| { - paint_chunk(chunk, painter, tiles, biomes, mappers); + paint_chunk(chunk, painter, tiles, mappers); }); } pub fn paint_chunk( chunk: &mut Chunk, - painter: &BiomePainterAsset, + painter: &BiomePainter, tiles: &Res>, - biomes: &Res>, mappers: &Res>, ) { for z in 0..Chunk::SIZE { for x in 0..Chunk::SIZE { let idx = x + z * Chunk::SIZE; let height = chunk.heights[idx]; - let moisture = chunk.moisture[idx]; - let temperature = chunk.temperature[idx]; - let biome = biomes - .get(painter.sample_biome(&biomes, moisture, temperature, 0.)) - .unwrap(); + let biome_data = &chunk.biome_data[idx]; + let biome = painter.sample_biome(biome_data); let mapper = mappers.get(biome.tile_mapper.clone()); let tile_handle = mapper.unwrap().sample_tile(height); let tile = tiles.get(tile_handle).unwrap();