From 8d2a3b9c810d6ae0fb88a8294c3b78e48cea6956 Mon Sep 17 00:00:00 2001 From: Khamraj Rohit Date: Thu, 1 Aug 2024 15:25:49 -0400 Subject: [PATCH 01/24] Create rust.yml --- .github/workflows/rust.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..000bb2c --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From b73ef8c0c87506bb0d58164e1029cd34061d9a3d Mon Sep 17 00:00:00 2001 From: Khamraj Rohit Date: Thu, 1 Aug 2024 15:32:58 -0400 Subject: [PATCH 02/24] Update rust.yml --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 000bb2c..99f056a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,6 +16,8 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install deps + run: sudo apt install libasound2-dev - name: Build run: cargo build --verbose - name: Run tests From 29f480b8a75fc1a08ebc0a3a29bf3e940b05b133 Mon Sep 17 00:00:00 2001 From: Khamraj Rohit Date: Thu, 1 Aug 2024 15:52:02 -0400 Subject: [PATCH 03/24] Update rust.yml --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 99f056a..cdec66f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install deps - run: sudo apt install libasound2-dev + run: sudo apt install libasound2-dev libudev-dev - name: Build run: cargo build --verbose - name: Run tests From f337b78beef0e26cfe7552248a194c4fab9d6cae Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sat, 3 Aug 2024 12:40:45 -0400 Subject: [PATCH 04/24] Spawn Building --- engine/world_generation/src/hex_utils.rs | 8 ++++++ game/buildings/src/build_queue.rs | 2 ++ game/buildings/src/building_plugin.rs | 35 ++++++++++++++++++++---- game/buildings/src/buildings_map.rs | 3 +- game/main/src/phos.rs | 2 +- game/shared/src/building.rs | 20 +++++++++++++- 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index c2441c4..907e2b5 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::prelude::Chunk; use bevy::prelude::*; use serde::{Deserialize, Serialize}; @@ -62,6 +64,12 @@ pub struct HexCoord { pub hex: IVec3, } +impl Display for HexCoord { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("HexCoord{}", self.hex)) + } +} + impl HexCoord { pub const DIRECTIONS: [IVec3; 6] = [ IVec3::new(0, 1, -1), diff --git a/game/buildings/src/build_queue.rs b/game/buildings/src/build_queue.rs index 93b8e3b..934c2ee 100644 --- a/game/buildings/src/build_queue.rs +++ b/game/buildings/src/build_queue.rs @@ -1,3 +1,5 @@ +use std::path::Display; + use bevy::prelude::Resource; use shared::building::BuildingIdentifier; use world_generation::hex_utils::HexCoord; diff --git a/game/buildings/src/building_plugin.rs b/game/buildings/src/building_plugin.rs index f9aeaff..785ce4e 100644 --- a/game/buildings/src/building_plugin.rs +++ b/game/buildings/src/building_plugin.rs @@ -3,17 +3,21 @@ use bevy_asset_loader::loading_state::{ config::{ConfigureLoadingState, LoadingStateConfig}, LoadingStateAppExt, }; -use bevy_rapier3d::{pipeline::QueryFilter, plugin::RapierContext}; +use bevy_rapier3d::{parry::transformation::utils::transform, pipeline::QueryFilter, plugin::RapierContext}; use shared::{ despawn::Despawn, states::{AssetLoadState, GameplayState}, tags::MainCamera, }; -use world_generation::{hex_utils::HexCoord, map::map::Map}; +use world_generation::{heightmap, hex_utils::HexCoord, map::map::Map}; use crate::{ - assets::{building_asset::BuildingAssetPlugin, building_database::BuildingDatabase}, + assets::{ + building_asset::{BuildingAsset, BuildingAssetPlugin}, + building_database::BuildingDatabase, + }, build_queue::{BuildQueue, QueueEntry}, + buildings_map::BuildingMap, }; pub struct BuildingPugin; @@ -21,13 +25,14 @@ pub struct BuildingPugin; impl Plugin for BuildingPugin { fn build(&self, app: &mut App) { app.insert_resource(BuildQueue::default()); + app.init_resource::(); app.add_plugins(BuildingAssetPlugin); app.configure_loading_state( LoadingStateConfig::new(AssetLoadState::Loading).load_collection::(), ); - app.add_systems(Startup, init.run_if(in_state(AssetLoadState::Loading))); + app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading))); app.add_systems(Update, hq_placement.run_if(in_state(GameplayState::PlaceHQ))); app.add_systems(PreUpdate, process_build_queue.run_if(in_state(GameplayState::Playing))); @@ -104,4 +109,24 @@ fn show_indicators(positions: Vec, commands: &mut Commands, indicator: &In } } -fn process_build_queue(mut queue: ResMut) {} +fn process_build_queue( + mut queue: ResMut, + mut commands: Commands, + db: Res, + building_assets: Res>, + heightmap: Res, +) { + for item in &queue.queue { + let handle = &db.buildings[item.building.0]; + if let Some(building) = building_assets.get(handle.id()) { + let h = heightmap.sample_height(&item.pos); + println!("Spawning {} at {}", building.name, item.pos); + commands.spawn(SceneBundle { + scene: building.prefab.clone(), + transform: Transform::from_translation(item.pos.to_world(h)), + ..Default::default() + }); + } + } + queue.queue.clear(); +} diff --git a/game/buildings/src/buildings_map.rs b/game/buildings/src/buildings_map.rs index db9f6c2..1f4a46c 100644 --- a/game/buildings/src/buildings_map.rs +++ b/game/buildings/src/buildings_map.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use world_generation::hex_utils::HexCoord; -#[derive(Resource)] +#[derive(Resource, Default)] pub struct BuildingMap { pub chunks: Vec, } @@ -33,6 +33,7 @@ impl BuildingMap { } } + pub struct BuildingChunk { pub entries: Vec, pub index: usize, diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index e230447..432f798 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -31,7 +31,7 @@ impl Plugin for PhosGamePlugin { PhosCameraPlugin, MapInitPlugin, RenderDistancePlugin, - // BuildingPugin, + BuildingPugin, DespawnPuglin, )); diff --git a/game/shared/src/building.rs b/game/shared/src/building.rs index 904f8ef..e3bdafc 100644 --- a/game/shared/src/building.rs +++ b/game/shared/src/building.rs @@ -1,10 +1,28 @@ use serde::{Deserialize, Serialize}; #[derive(Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct BuildingIdentifier(u32); +pub struct BuildingIdentifier(pub usize); + +impl From for BuildingIdentifier { + fn from(value: i32) -> Self { + return BuildingIdentifier(value as usize); + } +} impl From for BuildingIdentifier { fn from(value: u32) -> Self { + return BuildingIdentifier(value as usize); + } +} + +impl From for BuildingIdentifier { + fn from(value: usize) -> Self { return BuildingIdentifier(value); } } + +impl Into for BuildingIdentifier { + fn into(self) -> usize { + return self.0; + } +} From 59090fd3fbbed771e02c731977a592de548e8839 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sat, 3 Aug 2024 12:41:32 -0400 Subject: [PATCH 05/24] Update assets --- game/main/assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/main/assets b/game/main/assets index 3bb0aaa..a223382 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit 3bb0aaab5b5917c8c1f3ec4839c2269e62d17098 +Subproject commit a223382d13d1e3cac29ecc1f9152ac81e2b19c74 From bde25435ec10434011da4e854518d6df50664dd7 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sat, 3 Aug 2024 18:48:47 -0400 Subject: [PATCH 06/24] Buildings Processing Tile/Chunk updates events --- Cargo.lock | 1 + engine/world_generation/src/hex_utils.rs | 38 ++++++++ engine/world_generation/src/map/config.rs | 1 + engine/world_generation/src/map/map.rs | 41 ++++---- engine/world_generation/src/states.rs | 2 +- game/buildings/src/building_plugin.rs | 76 +++++++++++++-- game/buildings/src/buildings_map.rs | 42 ++++++-- game/buildings/src/lib.rs | 1 + game/buildings/src/prelude.rs | 3 + game/main/assets | 2 +- game/main/src/map_rendering/chunk_rebuild.rs | 14 ++- .../src/map_rendering/terraforming_test.rs | 32 ++++-- game/main/src/phos.rs | 4 +- game/main/src/utlis/debug_plugin.rs | 97 +++++++++++++++++++ game/main/src/utlis/mod.rs | 1 + game/shared/Cargo.toml | 1 + game/shared/src/events.rs | 13 +++ game/shared/src/lib.rs | 1 + 18 files changed, 318 insertions(+), 52 deletions(-) create mode 100644 game/buildings/src/prelude.rs create mode 100644 game/main/src/utlis/debug_plugin.rs create mode 100644 game/shared/src/events.rs diff --git a/Cargo.lock b/Cargo.lock index 8a5241d..15925f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3991,6 +3991,7 @@ version = "0.1.0" dependencies = [ "bevy", "serde", + "world_generation", ] [[package]] diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index 907e2b5..ba20e2c 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -144,6 +144,7 @@ impl HexCoord { }; } + /// Converts this coordinate to it's chunk local equivalent pub fn to_chunk(&self) -> HexCoord { let c_pos = self.to_chunk_pos(); let off = self.to_offset(); @@ -164,14 +165,20 @@ impl HexCoord { return IVec2::new(self.hex.x + (self.hex.y / 2), self.hex.y); } + /// Convert the current coordiante to an index pub fn to_index(&self, width: usize) -> usize { return ((self.hex.x + self.hex.y * width as i32) + (self.hex.y / 2)) as usize; } + + /// Gets the index of this coord in the chunk array. + /// + /// [`width`] is in number of chunks pub fn to_chunk_index(&self, width: usize) -> usize { let pos = self.to_chunk_pos(); return (pos.x + pos.y * width as i32) as usize; } + /// Gets the index of this tile in the chunk pub fn to_chunk_local_index(&self) -> usize { return self.to_chunk().to_index(Chunk::SIZE); } @@ -251,6 +258,37 @@ impl HexCoord { return result; } + pub fn hex_select_bounded( + &self, + radius: usize, + include_center: bool, + height: usize, + width: usize, + ) -> Vec { + assert!(radius != 0, "Radius cannot be zero"); + let mut result = Vec::with_capacity(get_tile_count(radius)); + + if include_center { + if self.is_in_bounds(height, width) { + result.push(*self); + } + } + + for k in 0..(radius + 1) { + let mut p = self.scale(4, k); + for i in 0..6 { + for _j in 0..k { + p = p.get_neighbor(i); + if p.is_in_bounds(height, width) { + result.push(p); + } + } + } + } + + return result; + } + pub fn select_ring(&self, radius: usize) -> Vec { assert!(radius != 0, "Radius cannot be zero"); let mut result = Vec::with_capacity(radius * 6); diff --git a/engine/world_generation/src/map/config.rs b/engine/world_generation/src/map/config.rs index 27582c9..86fe848 100644 --- a/engine/world_generation/src/map/config.rs +++ b/engine/world_generation/src/map/config.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use bevy_asset_loader::asset_collection::AssetCollection; use bevy_inspector_egui::InspectorOptions; use serde::{Deserialize, Serialize}; diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index 35b2d17..1a9ce59 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -87,22 +87,19 @@ impl Map { self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height; } - pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec { + pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<(HexCoord, f32)> { assert!(radius != 0, "Radius cannot be zero"); - let width = self.width; - let mut chunks = self.hex_select_mut(pos, radius, true, |p, h, r| { + let tiles = self.hex_select_mut(pos, radius, true, |p, h, r| { let d = (r as f32) / (radius as f32); let cur = *h; let h2 = cur - depth; *h = h2.lerp(cur, d * d).max(0.); - return p.to_chunk_index(width); + return (*p, *h); }); - chunks.dedup(); - - return chunks; + return tiles; } pub fn hex_select(&self, center: &HexCoord, radius: usize, include_center: bool, op: OP) -> Vec @@ -111,20 +108,25 @@ impl Map { { assert!(radius != 0, "Radius cannot be zero"); + let mut result = if include_center { + Vec::with_capacity(get_tile_count(radius) + 1) + } else { + Vec::with_capacity(get_tile_count(radius)) + }; if include_center { let h = self.sample_height(¢er); - (op)(¢er, h, 0); + result.push((op)(center, h, 0)); } - let mut result = Vec::with_capacity(get_tile_count(radius)); - for k in 0..(radius + 1) { let mut p = center.scale(4, k); for i in 0..6 { for _j in 0..k { p = p.get_neighbor(i); - let h = self.sample_height(&p); - result.push((op)(&p, h, k)); + if self.is_in_bounds(&p) { + let h = self.sample_height(&p); + result.push((op)(&p, h, k)); + } } } } @@ -144,20 +146,25 @@ impl Map { { assert!(radius != 0, "Radius cannot be zero"); + let mut result = if include_center { + Vec::with_capacity(get_tile_count(radius) + 1) + } else { + Vec::with_capacity(get_tile_count(radius)) + }; if include_center { let h = self.sample_height_mut(¢er); - (op)(¢er, h, 0); + result.push((op)(center, h, 0)); } - let mut result = Vec::with_capacity(get_tile_count(radius)); - for k in 0..(radius + 1) { let mut p = center.scale(4, k); for i in 0..6 { for _j in 0..k { p = p.get_neighbor(i); - let h = self.sample_height_mut(&p); - result.push((op)(&p, h, k)); + if self.is_in_bounds(&p) { + let h = self.sample_height_mut(&p); + result.push((op)(&p, h, k)); + } } } } diff --git a/engine/world_generation/src/states.rs b/engine/world_generation/src/states.rs index dfb66ff..a1659a3 100644 --- a/engine/world_generation/src/states.rs +++ b/engine/world_generation/src/states.rs @@ -7,5 +7,5 @@ pub enum GeneratorState { SpawnMap, Idle, Regenerate, + Cleanup, } - diff --git a/game/buildings/src/building_plugin.rs b/game/buildings/src/building_plugin.rs index 785ce4e..9ca45d9 100644 --- a/game/buildings/src/building_plugin.rs +++ b/game/buildings/src/building_plugin.rs @@ -1,4 +1,6 @@ -use bevy::{prelude::*, window::PrimaryWindow}; +use std::f32::consts::E; + +use bevy::{ecs::world::CommandQueue, prelude::*, window::PrimaryWindow}; use bevy_asset_loader::loading_state::{ config::{ConfigureLoadingState, LoadingStateConfig}, LoadingStateAppExt, @@ -6,10 +8,13 @@ use bevy_asset_loader::loading_state::{ use bevy_rapier3d::{parry::transformation::utils::transform, pipeline::QueryFilter, plugin::RapierContext}; use shared::{ despawn::Despawn, + events::TileModifiedEvent, states::{AssetLoadState, GameplayState}, tags::MainCamera, }; -use world_generation::{heightmap, hex_utils::HexCoord, map::map::Map}; +use world_generation::{ + heightmap, hex_utils::HexCoord, map::map::Map, prelude::GenerationConfig, states::GeneratorState, +}; use crate::{ assets::{ @@ -17,7 +22,8 @@ use crate::{ building_database::BuildingDatabase, }, build_queue::{BuildQueue, QueueEntry}, - buildings_map::BuildingMap, + buildings_map::{BuildingEntry, BuildingMap}, + prelude::Building, }; pub struct BuildingPugin; @@ -25,7 +31,6 @@ pub struct BuildingPugin; impl Plugin for BuildingPugin { fn build(&self, app: &mut App) { app.insert_resource(BuildQueue::default()); - app.init_resource::(); app.add_plugins(BuildingAssetPlugin); app.configure_loading_state( @@ -34,11 +39,31 @@ impl Plugin for BuildingPugin { app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading))); app.add_systems(Update, hq_placement.run_if(in_state(GameplayState::PlaceHQ))); + app.add_systems( + PreUpdate, + prepare_building_map.run_if(in_state(GeneratorState::SpawnMap)), + ); + app.add_systems(Update, regernerate.run_if(in_state(GeneratorState::Regenerate))); + app.add_systems( + PostUpdate, + update_building_heights.run_if(in_state(GeneratorState::Idle)), + ); app.add_systems(PreUpdate, process_build_queue.run_if(in_state(GameplayState::Playing))); } } +fn prepare_building_map(mut commands: Commands, cfg: Res) { + commands.insert_resource(BuildingMap::new(cfg.size)); +} + +fn regernerate(mut commands: Commands, buildings: Query>, cfg: Res) { + for e in buildings.iter() { + commands.entity(e).despawn(); + } + commands.insert_resource(BuildingMap::new(cfg.size)); +} + #[derive(Resource)] struct IndicatorCube(Handle, Handle); @@ -114,6 +139,7 @@ fn process_build_queue( mut commands: Commands, db: Res, building_assets: Res>, + mut building_map: ResMut, heightmap: Res, ) { for item in &queue.queue { @@ -121,12 +147,44 @@ fn process_build_queue( if let Some(building) = building_assets.get(handle.id()) { let h = heightmap.sample_height(&item.pos); println!("Spawning {} at {}", building.name, item.pos); - commands.spawn(SceneBundle { - scene: building.prefab.clone(), - transform: Transform::from_translation(item.pos.to_world(h)), - ..Default::default() - }); + let e = commands.spawn(( + SceneBundle { + scene: building.prefab.clone(), + transform: Transform::from_translation(item.pos.to_world(h)), + ..Default::default() + }, + Building, + )); + + building_map.add_building(BuildingEntry::new(item.pos, e.id())); } } queue.queue.clear(); } + +fn update_building_heights( + mut tile_updates: EventReader, + building_map: Res, + mut commands: Commands, +) { + for event in tile_updates.read() { + match event { + TileModifiedEvent::HeightChanged(coord, new_height) => { + if let Some(building) = building_map.get_building(coord) { + let mut queue = CommandQueue::default(); + let e = building.entity.clone(); + let h = *new_height; + queue.push(move |world: &mut World| { + let mut emut = world.entity_mut(e); + if let Some(mut transform) = emut.get_mut::() { + transform.translation.y = h; + } + }); + + commands.append(&mut queue); + } + } + _ => (), + } + } +} diff --git a/game/buildings/src/buildings_map.rs b/game/buildings/src/buildings_map.rs index 1f4a46c..620463b 100644 --- a/game/buildings/src/buildings_map.rs +++ b/game/buildings/src/buildings_map.rs @@ -1,14 +1,16 @@ use bevy::prelude::*; -use world_generation::hex_utils::HexCoord; +use world_generation::{hex_utils::HexCoord, prelude::Chunk}; -#[derive(Resource, Default)] +#[derive(Resource)] pub struct BuildingMap { pub chunks: Vec, + pub size: UVec2, } impl BuildingMap { pub fn new(size: UVec2) -> Self { let mut db = BuildingMap { + size, chunks: Vec::with_capacity(size.length_squared() as usize), }; @@ -23,19 +25,39 @@ impl BuildingMap { return db; } - pub fn get_buildings_in_range(&self, coord: &HexCoord, radius: usize) -> Option> { + pub fn get_buildings_in_range(&self, coord: &HexCoord, radius: usize) -> Vec<&BuildingEntry> { assert!(radius != 0, "Radius cannot be zero"); - todo!(); + + let w = self.size.x as usize * Chunk::SIZE; + let h = self.size.y as usize * Chunk::SIZE; + let coords = coord.hex_select_bounded(radius, true, h, w); + return self.get_buildings_in_coords(coords); + } + + pub fn get_buildings_in_coords(&self, coords: Vec) -> Vec<&BuildingEntry> { + let mut result = Vec::new(); + for coord in &coords { + if let Some(buidling) = self.get_building(coord) { + result.push(buidling); + } + } + + return result; } pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> { - todo!(); + let chunk = &self.chunks[coord.to_chunk_index(self.size.x as usize)]; + return chunk.get_building(coord); + } + + pub fn add_building(&mut self, entry: BuildingEntry) { + let chunk = &mut self.chunks[entry.coord.to_chunk_index(self.size.x as usize)]; + chunk.add_building(entry); } } - pub struct BuildingChunk { - pub entries: Vec, + pub entries: Vec, pub index: usize, pub offset: IVec2, } @@ -50,7 +72,11 @@ impl BuildingChunk { } pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> { - todo!(); + return self.entries.iter().find(|b| &b.coord == coord); + } + + pub fn add_building(&mut self, entry: BuildingEntry) { + self.entries.push(entry); } } diff --git a/game/buildings/src/lib.rs b/game/buildings/src/lib.rs index 6909842..9e24bf4 100644 --- a/game/buildings/src/lib.rs +++ b/game/buildings/src/lib.rs @@ -3,4 +3,5 @@ pub mod build_queue; pub mod building_plugin; pub mod buildings_map; pub mod footprint; +pub mod prelude; pub use building_plugin::*; diff --git a/game/buildings/src/prelude.rs b/game/buildings/src/prelude.rs new file mode 100644 index 0000000..3d6fc40 --- /dev/null +++ b/game/buildings/src/prelude.rs @@ -0,0 +1,3 @@ +use bevy::prelude::*; +#[derive(Component)] +pub struct Building; diff --git a/game/main/assets b/game/main/assets index a223382..6a48d91 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit a223382d13d1e3cac29ecc1f9152ac81e2b19c74 +Subproject commit 6a48d910aa3826fac04af0b481e741d3b8e2e911 diff --git a/game/main/src/map_rendering/chunk_rebuild.rs b/game/main/src/map_rendering/chunk_rebuild.rs index 44000d9..418aa60 100644 --- a/game/main/src/map_rendering/chunk_rebuild.rs +++ b/game/main/src/map_rendering/chunk_rebuild.rs @@ -4,6 +4,8 @@ use bevy::tasks::*; use bevy::utils::futures; use bevy_rapier3d::geometry::Collider; use bevy_rapier3d::geometry::TriMeshFlags; +use shared::events::ChunkModifiedEvent; +use shared::events::TileModifiedEvent; use world_generation::prelude::Map; use world_generation::states::GeneratorState; @@ -17,18 +19,14 @@ pub struct ChunkRebuildPlugin; impl Plugin for ChunkRebuildPlugin { fn build(&self, app: &mut App) { - app.insert_resource(ChunkRebuildQueue::default()); app.init_resource::(); - app.add_systems(PreUpdate, chunk_rebuilder.run_if(in_state(GeneratorState::SpawnMap))); + app.add_event::(); + app.add_event::(); + app.add_systems(PreUpdate, chunk_rebuilder.run_if(in_state(GeneratorState::Idle))); app.add_systems(PostUpdate, collider_task_resolver); } } -#[derive(Resource, Default)] -pub struct ChunkRebuildQueue { - pub queue: Vec, -} - fn chunk_rebuilder( mut commands: Commands, chunk_query: Query<(Entity, &PhosChunk), (With, Without)>, @@ -39,7 +37,7 @@ fn chunk_rebuilder( for (chunk_entity, idx) in &chunk_query { #[cfg(feature = "tracing")] let _spawn_span = info_span!("Rebuild Chunk").entered(); - + println!("Rebuilding Chunk"); let chunk_index = idx.index; let chunk_data = heightmap.get_chunk_mesh_data(chunk_index); let chunk_offset = heightmap.chunks[chunk_index].chunk_offset; diff --git a/game/main/src/map_rendering/terraforming_test.rs b/game/main/src/map_rendering/terraforming_test.rs index 1525c21..382a804 100644 --- a/game/main/src/map_rendering/terraforming_test.rs +++ b/game/main/src/map_rendering/terraforming_test.rs @@ -1,5 +1,9 @@ -use bevy::{prelude::*, window::PrimaryWindow}; +use bevy::{prelude::*, utils::hashbrown::HashSet, window::PrimaryWindow}; use bevy_rapier3d::{pipeline::QueryFilter, plugin::RapierContext}; +use shared::{ + events::{ChunkModifiedEvent, TileModifiedEvent}, + states::GameplayState, +}; use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState}; use crate::{ @@ -11,7 +15,12 @@ pub struct TerraFormingTestPlugin; impl Plugin for TerraFormingTestPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, deform.run_if(in_state(GeneratorState::Idle))); + app.add_systems( + Update, + deform + .run_if(in_state(GeneratorState::Idle)) + .run_if(in_state(GameplayState::Playing)), + ); } } @@ -23,6 +32,8 @@ fn deform( rapier_context: Res, mut heightmap: ResMut, chunks: Res, + mut chunk_modified: EventWriter, + mut tile_modified: EventWriter, ) { let mut multi = 0.; if mouse.just_pressed(MouseButton::Left) { @@ -53,15 +64,22 @@ fn deform( QueryFilter::only_fixed(), ); - if let Some((e, dist)) = collision { + if let Some((_, dist)) = collision { #[cfg(feature = "tracing")] let span = info_span!("Deform Mesh").entered(); let contact_point = cam_ray.get_point(dist); let contact_coord = HexCoord::from_world_pos(contact_point); - let modified_chunks = heightmap.create_crater(&contact_coord, 5, 5. * multi); - for c in modified_chunks { - commands.entity(chunks.chunks[c]).insert(RebuildChunk); + let modified_tiles = heightmap.create_crater(&contact_coord, 5, 5. * multi); + let mut chunk_set: HashSet = HashSet::new(); + for (tile, height) in modified_tiles { + let chunk = tile.to_chunk_index(heightmap.width); + if !chunk_set.contains(&chunk) { + chunk_modified.send(ChunkModifiedEvent { index: chunk }); + chunk_set.insert(chunk); + commands.entity(chunks.chunks[chunk]).insert(RebuildChunk); + } + tile_modified.send(TileModifiedEvent::HeightChanged(tile, height)); } - commands.entity(e).insert(RebuildChunk); + // commands.entity(e).insert(RebuildChunk); } } diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index 432f798..aba0cf5 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -1,7 +1,7 @@ -use crate::camera_system::camera_plugin::PhosCameraPlugin; use crate::camera_system::components::PhosCamera; use crate::map_rendering::map_init::MapInitPlugin; use crate::utlis::render_distance_system::RenderDistancePlugin; +use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin}; use bevy::{ pbr::{wireframe::WireframeConfig, CascadeShadowConfig}, prelude::*, @@ -33,6 +33,8 @@ impl Plugin for PhosGamePlugin { RenderDistancePlugin, BuildingPugin, DespawnPuglin, + #[cfg(debug_assertions)] + DebugPlugin, )); //Systems - Startup diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs new file mode 100644 index 0000000..ca1ecd0 --- /dev/null +++ b/game/main/src/utlis/debug_plugin.rs @@ -0,0 +1,97 @@ +use bevy::{prelude::*, window::PrimaryWindow}; +use bevy_inspector_egui::bevy_egui::EguiContexts; +use bevy_inspector_egui::egui; +use bevy_rapier3d::prelude::*; +use shared::tags::MainCamera; +use world_generation::{ + consts::HEX_CORNERS, + hex_utils::{HexCoord, INNER_RADIUS}, + prelude::Map, + states::GeneratorState, +}; + +pub struct DebugPlugin; + +impl Plugin for DebugPlugin { + fn build(&self, app: &mut App) { + app.insert_state(DebugState::Base); + + app.add_systems( + Update, + show_tile_heights + .run_if(in_state(GeneratorState::Idle)) + .run_if(not(in_state(DebugState::None))), + ); + + app.add_systems( + Update, + verbose_data + .run_if(in_state(GeneratorState::Idle)) + .run_if(in_state(DebugState::Verbose)), + ); + + app.insert_resource(Shape(Polyline3d::new([ + HEX_CORNERS[0], + HEX_CORNERS[1], + HEX_CORNERS[2], + HEX_CORNERS[3], + HEX_CORNERS[4], + HEX_CORNERS[5], + HEX_CORNERS[0], + ]))); + } +} + +#[derive(Resource)] +struct Shape(pub Polyline3d<7>); + +#[derive(States, Debug, Clone, PartialEq, Eq, Hash)] +pub enum DebugState { + Base, + None, + Verbose, +} + +fn show_tile_heights( + cam_query: Query<(&GlobalTransform, &Camera), With>, + window: Query<&Window, With>, + rapier_context: Res, + map: Res, + mut gizmos: Gizmos, + shape: Res, +) { + let win = window.single(); + let (cam_transform, camera) = cam_query.single(); + let Some(cursor_pos) = win.cursor_position() else { + return; + }; + + let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else { + return; + }; + + let collision = rapier_context.cast_ray( + cam_ray.origin, + cam_ray.direction.into(), + 500., + true, + QueryFilter::only_fixed(), + ); + + if let Some((_e, dist)) = collision { + let contact_point = cam_ray.get_point(dist); + let contact_coord = HexCoord::from_world_pos(contact_point); + if map.is_in_bounds(&contact_coord) { + let height = map.sample_height(&contact_coord); + gizmos.primitive_3d( + &shape.0, + contact_coord.to_world(height + 0.01), + Quat::IDENTITY, + Color::WHITE, + ); + } + gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, Srgba::rgb(1., 0., 0.5)); + } +} + +fn verbose_data() {} diff --git a/game/main/src/utlis/mod.rs b/game/main/src/utlis/mod.rs index fc1e23a..b2154a8 100644 --- a/game/main/src/utlis/mod.rs +++ b/game/main/src/utlis/mod.rs @@ -1,2 +1,3 @@ pub mod chunk_utils; pub mod render_distance_system; +pub mod debug_plugin; diff --git a/game/shared/Cargo.toml b/game/shared/Cargo.toml index b19ce07..c394b40 100644 --- a/game/shared/Cargo.toml +++ b/game/shared/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bevy = "0.14.0" serde = { version = "1.0.204", features = ["derive"] } +world_generation = { path = "../../engine/world_generation" } [features] diff --git a/game/shared/src/events.rs b/game/shared/src/events.rs new file mode 100644 index 0000000..d4ddef7 --- /dev/null +++ b/game/shared/src/events.rs @@ -0,0 +1,13 @@ +use bevy::prelude::*; +use world_generation::hex_utils::*; + +#[derive(Event)] +pub enum TileModifiedEvent { + HeightChanged(HexCoord, f32), + TypeChanged(HexCoord, usize), +} + +#[derive(Event)] +pub struct ChunkModifiedEvent { + pub index: usize, +} diff --git a/game/shared/src/lib.rs b/game/shared/src/lib.rs index d317d7e..244a2c5 100644 --- a/game/shared/src/lib.rs +++ b/game/shared/src/lib.rs @@ -3,3 +3,4 @@ pub mod despawn; pub mod resource; pub mod states; pub mod tags; +pub mod events; From f7a3a56a0a58ada8d1a4c3ace49920c9ec0b70c4 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 4 Aug 2024 21:45:23 -0400 Subject: [PATCH 07/24] Added generation of images based on the map Tweaks to consistency with DIRECTIONS and HEX_CORNERS Misc debug visualizations --- Cargo.lock | 426 +++++++++++++++++- engine/world_generation/Cargo.toml | 1 + .../src/generators/chunk_colliders.rs | 6 +- engine/world_generation/src/heightmap.rs | 61 ++- engine/world_generation/src/hex_utils.rs | 8 +- engine/world_generation/src/map/biome_map.rs | 1 + engine/world_generation/src/map/chunk.rs | 4 + engine/world_generation/src/map/map.rs | 50 +- engine/world_generation/src/map/map_utils.rs | 115 +++++ engine/world_generation/src/map/mesh_chunk.rs | 2 +- engine/world_generation/src/map/mod.rs | 3 +- game/main/src/map_rendering/map_init.rs | 6 + game/main/src/utlis/debug_plugin.rs | 9 + 13 files changed, 671 insertions(+), 21 deletions(-) create mode 100644 engine/world_generation/src/map/map_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 15925f1..caf296d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -186,6 +192,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arboard" version = "3.4.0" @@ -194,7 +206,7 @@ checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" dependencies = [ "clipboard-win", "core-graphics", - "image 0.25.1", + "image 0.25.2", "log", "objc2", "objc2-app-kit", @@ -204,6 +216,17 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.70", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -316,6 +339,29 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "base64" version = "0.21.7" @@ -1001,7 +1047,7 @@ dependencies = [ "encase", "futures-lite", "hexasphere", - "image 0.25.1", + "image 0.25.2", "js-sys", "ktx2", "naga", @@ -1301,6 +1347,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -1316,6 +1368,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitstream-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" + [[package]] name = "blake3" version = "1.5.1" @@ -1372,6 +1430,12 @@ dependencies = [ "world_generation", ] +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" + [[package]] name = "bumpalo" version = "3.16.0" @@ -1404,6 +1468,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -1462,6 +1532,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -1767,6 +1847,12 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "cursor-icon" version = "1.1.0" @@ -1984,6 +2070,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -2021,6 +2123,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2145,6 +2256,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gilrs" version = "0.10.8" @@ -2343,6 +2464,16 @@ dependencies = [ "svg_fmt", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -2369,6 +2500,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.4.0" @@ -2424,17 +2561,43 @@ dependencies = [ [[package]] name = "image" -version = "0.25.1" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", "num-traits", "png", + "qoi", + "ravif", + "rayon", + "rgb", "tiff", + "zune-core", + "zune-jpeg", ] +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "immutable-chunkmap" version = "2.0.5" @@ -2480,6 +2643,17 @@ dependencies = [ "libc", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.70", +] + [[package]] name = "io-kit-sys" version = "0.4.1" @@ -2599,6 +2773,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "lewton" version = "0.10.2" @@ -2616,6 +2796,17 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2704,6 +2895,15 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mach2" version = "0.4.2" @@ -2741,6 +2941,15 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.7.4" @@ -2910,6 +3119,12 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.29.0" @@ -2966,6 +3181,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "ntapi" version = "0.4.1" @@ -3585,6 +3806,21 @@ dependencies = [ "syn 2.0.70", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.34.0" @@ -3617,7 +3853,7 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.16", "libc", - "rand_chacha", + "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc", ] @@ -3628,6 +3864,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -3641,6 +3879,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -3655,6 +3903,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] [[package]] name = "rand_hc" @@ -3714,6 +3965,55 @@ dependencies = [ "vec_map", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5797d09f9bd33604689e87e8380df4951d4912f01b63f71205e2abd4ae25e6b6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3820,6 +4120,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rgb" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12bc8d2f72df26a5d3178022df33720fbede0d31d82c7291662eff89836994d" +dependencies = [ + "bytemuck", +] + [[package]] name = "robust" version = "1.1.0" @@ -3976,6 +4285,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4019,6 +4337,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "slab" version = "0.4.9" @@ -4089,6 +4416,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -4152,6 +4488,19 @@ dependencies = [ "windows 0.52.0", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "taffy" version = "0.5.2" @@ -4165,6 +4514,12 @@ dependencies = [ "slotmap", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "termcolor" version = "1.4.1" @@ -4255,11 +4610,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.15", +] + [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -4279,6 +4649,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow 0.6.13", ] @@ -4484,6 +4856,17 @@ dependencies = [ "serde", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -4496,6 +4879,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" @@ -5318,6 +5707,7 @@ dependencies = [ "bevy", "bevy-inspector-egui", "bevy_asset_loader", + "image 0.25.2", "noise 0.9.0", "rayon", "ron", @@ -5413,3 +5803,27 @@ dependencies = [ "quote", "syn 2.0.70", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] diff --git a/engine/world_generation/Cargo.toml b/engine/world_generation/Cargo.toml index 5232c4c..4bc9dbc 100644 --- a/engine/world_generation/Cargo.toml +++ b/engine/world_generation/Cargo.toml @@ -18,6 +18,7 @@ bevy_asset_loader = { version = "0.21.0", features = [ "3d", ] } ron = "0.8.1" +image = "0.25.2" [features] tracing = ["bevy/trace_tracy"] diff --git a/engine/world_generation/src/generators/chunk_colliders.rs b/engine/world_generation/src/generators/chunk_colliders.rs index 6d72be7..78a4baf 100644 --- a/engine/world_generation/src/generators/chunk_colliders.rs +++ b/engine/world_generation/src/generators/chunk_colliders.rs @@ -31,7 +31,7 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec, indices: &mut Vec<[u32 verts.push(p); } - //Top Surfave + //Top Surface indices.push([idx, idx + 1, idx + 5]); indices.push([idx + 1, idx + 2, idx + 5]); indices.push([idx + 2, idx + 4, idx + 5]); @@ -43,7 +43,7 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec, indices: &mut Vec<[u32 create_tile_wall_collider( idx, Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z), - i, + (i + 1) % 6, verts, indices, ); @@ -54,7 +54,7 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec, indices: &mut Vec<[u32 fn create_tile_wall_collider(idx: u32, pos: Vec3, dir: usize, verts: &mut Vec, indices: &mut Vec<[u32; 3]>) { let idx2 = verts.len() as u32; - verts.push(pos + HEX_CORNERS[dir]); + verts.push(pos + HEX_CORNERS[(dir) % 6]); verts.push(pos + HEX_CORNERS[(dir + 1) % 6]); let off = dir as u32; diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index de0e2b9..49e9458 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -1,5 +1,8 @@ +use core::f32; + use bevy::math::{IVec2, UVec2}; use bevy::prelude::{FloatExt, Vec2}; +use bevy::render::render_resource::encase::internal::BufferMut; use bevy::utils::default; use noise::{NoiseFn, SuperSimplex}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -11,7 +14,7 @@ use crate::prelude::*; pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> Map { let biomes = &generate_biomes(cfg, seed, painter); // let mut chunks: Vec = Vec::with_capacity(cfg.size.length_squared() as usize); - let chunks = (0..cfg.size.y) + let chunks: Vec = (0..cfg.size.y) .into_par_iter() .flat_map(|z| { (0..cfg.size.x).into_par_iter().map(move |x| { @@ -20,11 +23,24 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain }) }) .collect(); + let mut min = f32::MAX; + let mut max = f32::MIN; + for chunk in &chunks { + if chunk.min_level < min { + min = chunk.min_level; + } + if chunk.max_level > max { + max = chunk.max_level; + } + } + return Map { chunks, height: cfg.size.y as usize, width: cfg.size.x as usize, sea_level: cfg.sea_level as f32, + min_level: min, + max_level: max, }; } @@ -66,7 +82,7 @@ pub fn generate_biome_chunk( &cfg.moisture_noise, &noise_m, cfg.size.as_vec2(), - cfg.border_size, + 0.0, ); let temperature = sample_point( x as f64 + chunk_x as f64 * Chunk::SIZE as f64, @@ -74,7 +90,7 @@ pub fn generate_biome_chunk( &cfg.temperature_noise, &noise_t, cfg.size.as_vec2(), - cfg.border_size, + 0.0, ); let continentality = sample_point( x as f64 + chunk_x as f64 * Chunk::SIZE as f64, @@ -82,7 +98,7 @@ pub fn generate_biome_chunk( &cfg.continent_noise, &noise_c, cfg.size.as_vec2(), - cfg.border_size, + 0.0, ); let data = BiomeData { moisture: moisture.clamp(0., 100.), @@ -100,6 +116,29 @@ pub fn generate_biome_chunk( return chunk; } +pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size: f32) -> Vec { + let noise = SuperSimplex::new(seed); + + let data: Vec<_> = (0..(size.y as usize * Chunk::SIZE)) + .into_par_iter() + .flat_map(|y| { + let mut row = Vec::with_capacity(size.x as usize * Chunk::SIZE); + for x in 0..row.capacity() { + row.push(sample_point( + x as f64, + y as f64, + cfg, + &noise, + size.as_vec2(), + border_size, + )); + } + return row; + }) + .collect(); + return data; +} + pub fn generate_chunk( chunk_x: u32, chunk_z: u32, @@ -112,6 +151,8 @@ pub fn generate_chunk( let mut data = [BiomeData::default(); Chunk::AREA]; let mut biome_ids = [0; Chunk::AREA]; let noise = SuperSimplex::new(seed); + let mut min = f32::MAX; + let mut max = f32::MIN; for z in 0..Chunk::SIZE { for x in 0..Chunk::SIZE { let biome_data = biome_chunk.get_biome_data(x, z); @@ -135,6 +176,12 @@ pub fn generate_chunk( let idx = x + z * Chunk::SIZE; biome_ids[idx] = biome_chunk.get_biome_id_dithered(x, z, &noise, cfg.biome_dither); result[idx] = sample; + if sample > max { + max = sample; + } + if sample < min { + min = sample; + } data[idx] = biome_data.clone(); } } @@ -143,6 +190,8 @@ pub fn generate_chunk( biome_data: data, biome_id: biome_ids, chunk_offset: IVec2::new(chunk_x as i32, chunk_z as i32), + max_level: max, + min_level: min, ..default() }; } @@ -171,6 +220,10 @@ fn sample_point(x: f64, z: f64, cfg: &NoiseConfig, noise: &impl NoiseFn, } } + if border_size == 0.0 { + return elevation as f32; + } + let outer = size * Chunk::SIZE as f32; let p = Vec2::new(x as f32, z as f32); diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index ba20e2c..5cacc23 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -72,12 +72,12 @@ impl Display for HexCoord { impl HexCoord { pub const DIRECTIONS: [IVec3; 6] = [ - IVec3::new(0, 1, -1), - IVec3::new(1, 0, -1), IVec3::new(1, -1, 0), - IVec3::new(0, -1, 1), - IVec3::new(-1, 0, 1), + IVec3::new(1, 0, -1), + IVec3::new(0, 1, -1), IVec3::new(-1, 1, 0), + IVec3::new(-1, 0, 1), + IVec3::new(0, -1, 1), ]; pub const ZERO: HexCoord = HexCoord { hex: IVec3::ZERO }; diff --git a/engine/world_generation/src/map/biome_map.rs b/engine/world_generation/src/map/biome_map.rs index 91fc3c7..213ba55 100644 --- a/engine/world_generation/src/map/biome_map.rs +++ b/engine/world_generation/src/map/biome_map.rs @@ -4,6 +4,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use super::chunk::Chunk; +#[derive(Clone)] pub struct BiomeMap { pub height: usize, pub width: usize, diff --git a/engine/world_generation/src/map/chunk.rs b/engine/world_generation/src/map/chunk.rs index d59282e..ef37799 100644 --- a/engine/world_generation/src/map/chunk.rs +++ b/engine/world_generation/src/map/chunk.rs @@ -10,6 +10,8 @@ pub struct Chunk { pub biome_data: [BiomeData; Chunk::AREA], pub biome_id: [usize; Chunk::AREA], pub chunk_offset: IVec2, + pub min_level: f32, + pub max_level: f32, } impl Default for Chunk { @@ -20,6 +22,8 @@ impl Default for Chunk { biome_data: [BiomeData::default(); Chunk::AREA], biome_id: [0; Chunk::AREA], chunk_offset: Default::default(), + min_level: 0.0, + max_level: 0.0, } } } diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index 1a9ce59..b344ee5 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -2,7 +2,11 @@ use bevy::prelude::*; use crate::hex_utils::*; -use super::{chunk::Chunk, mesh_chunk::MeshChunkData}; +use super::{ + biome_map::{BiomeData, BiomeMap}, + chunk::Chunk, + mesh_chunk::MeshChunkData, +}; #[derive(Resource, Clone)] pub struct Map { @@ -10,6 +14,8 @@ pub struct Map { pub height: usize, pub width: usize, pub sea_level: f32, + pub min_level: f32, + pub max_level: f32, } impl Map { @@ -43,11 +49,21 @@ impl Map { } pub fn sample_height(&self, pos: &HexCoord) -> f32 { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.heights[pos.to_chunk_local_index()]; } pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32 { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + let chunk = &mut self.chunks[pos.to_chunk_index(self.width)]; return &mut chunk.heights[pos.to_chunk_local_index()]; } @@ -56,16 +72,46 @@ impl Map { return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE); } + pub fn get_biome(&self, pos: &HexCoord) -> &BiomeData { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; + return &chunk.biome_data[pos.to_chunk_local_index()]; + } + pub fn get_moisture(&self, pos: &HexCoord) -> f32 { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.biome_data[pos.to_chunk_local_index()].moisture; } pub fn get_tempurature(&self, pos: &HexCoord) -> f32 { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.biome_data[pos.to_chunk_local_index()].temperature; } + pub fn get_continentality(&self, pos: &HexCoord) -> f32 { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; + return chunk.biome_data[pos.to_chunk_local_index()].continentality; + } + pub fn get_center(&self) -> Vec3 { let w = self.get_world_width(); let h = self.get_world_height(); @@ -96,7 +142,7 @@ impl Map { let h2 = cur - depth; *h = h2.lerp(cur, d * d).max(0.); - return (*p, *h); + return (*p, *h); }); return tiles; diff --git a/engine/world_generation/src/map/map_utils.rs b/engine/world_generation/src/map/map_utils.rs new file mode 100644 index 0000000..6e6ebb6 --- /dev/null +++ b/engine/world_generation/src/map/map_utils.rs @@ -0,0 +1,115 @@ +use bevy::{math::VectorSpace, prelude::*}; +use image::ImageBuffer; +use rayon::prelude::*; + +use crate::hex_utils::HexCoord; + +use super::{biome_map::BiomeMap, chunk::Chunk, map::Map}; + +pub fn render_image( + size: UVec2, + data: &Vec, + color1: LinearRgba, + color2: LinearRgba, +) -> ImageBuffer, Vec> { + let mut image = ImageBuffer::new(size.x * Chunk::SIZE as u32, size.y * Chunk::SIZE as u32); + update_image(size, data, color1, color2, &mut image); + + return image; +} + +pub fn update_image( + size: UVec2, + data: &Vec, + color1: LinearRgba, + color2: LinearRgba, + image: &mut ImageBuffer, Vec>, +) { + let min = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&0.0); + let max = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&1.0); + + let w = size.x * Chunk::SIZE as u32; + + image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { + let idx = (y * w + x) as usize; + let v = data[idx]; + let t = v.remap(min, max, 0.0, 1.0); + let col = LinearRgba::lerp(&color1, color2, t); + *pixel = to_pixel(&col); + }); +} + +fn to_pixel(col: &LinearRgba) -> image::Rgb { + return image::Rgb([ + (col.red * 255.0) as u8, + (col.green * 255.0) as u8, + (col.blue * 255.0) as u8, + ]); +} +pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer, Vec> { + let mut image = ImageBuffer::new( + map.width as u32 * Chunk::SIZE as u32, + map.height as u32 * Chunk::SIZE as u32, + ); + update_map(map, smooth, &mut image); + return image; +} +pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer, Vec>) { + image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { + let coord = HexCoord::from_grid_pos(x as usize, y as usize); + let right = coord.get_neighbor(1); + let height = map.sample_height(&coord); + let mut color = Hsla::hsl(138.0, 1.0, 0.4); + if height < map.sea_level { + color.hue = 217.0; + } + if map.is_in_bounds(&right) { + let h2 = map.sample_height(&right); + let mut d = h2 - height; + + if smooth == 0.0 || d.abs() > smooth { + if d > 0.0 { + color.lightness += 0.1; + } else if d < 0.0 { + color.lightness -= 0.1; + } + } else { + if d.abs() <= smooth { + d /= smooth; + if d > 0.0 { + let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into(); + color = LinearRgba::lerp(&color.into(), c2, d).into(); + } else { + let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into(); + color = LinearRgba::lerp(&color.into(), c2, d.abs()).into(); + } + } + } + } + + *pixel = to_pixel(&color.into()); + }); +} + +pub fn render_biome_map(map: &Map) -> ImageBuffer, Vec> { + let mut image = ImageBuffer::new( + map.width as u32 * Chunk::SIZE as u32, + map.height as u32 * Chunk::SIZE as u32, + ); + update_biome_map(map, &mut image); + return image; +} + +pub fn update_biome_map(map: &Map, image: &mut ImageBuffer, Vec>) { + image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { + let coord = HexCoord::from_grid_pos(x as usize, y as usize); + let tile = map.get_biome(&coord); + + let color = LinearRgba::rgb( + tile.temperature / 100.0, + tile.continentality / 100.0, + tile.moisture / 100.0, + ); + *pixel = to_pixel(&color); + }); +} diff --git a/engine/world_generation/src/map/mesh_chunk.rs b/engine/world_generation/src/map/mesh_chunk.rs index 3a09423..be87f78 100644 --- a/engine/world_generation/src/map/mesh_chunk.rs +++ b/engine/world_generation/src/map/mesh_chunk.rs @@ -11,7 +11,7 @@ impl MeshChunkData { pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] { let mut data = [0.; 6]; let n_tiles = coord.get_neighbors(); - for i in 0..6 { + for i in 6..0 { let n = n_tiles[i]; if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) { continue; diff --git a/engine/world_generation/src/map/mod.rs b/engine/world_generation/src/map/mod.rs index dc19c18..fad9755 100644 --- a/engine/world_generation/src/map/mod.rs +++ b/engine/world_generation/src/map/mod.rs @@ -2,4 +2,5 @@ pub mod chunk; pub mod mesh_chunk; pub mod config; pub mod map; -pub mod biome_map; \ No newline at end of file +pub mod biome_map; +pub mod map_utils; \ No newline at end of file diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 1a67cb5..b6b7429 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -14,6 +14,7 @@ use world_generation::{ biome_painter::*, heightmap::generate_heightmap, hex_utils::{offset_to_index, SHORT_DIAGONAL}, + map::map_utils::{render_biome_map, render_map}, prelude::*, tile_manager::*, tile_mapper::*, @@ -212,6 +213,11 @@ fn create_heightmap( }; let heightmap = generate_heightmap(&config, 42069, &biome_painter); + let game_map = render_map(&heightmap, 1.5); + let biome_map = render_biome_map(&heightmap); + _ = biome_map.save("Biomes.png"); + _ = game_map.save("Test.png"); + let (mut cam_t, cam_entity) = cam.single_mut(); cam_t.translation = heightmap.get_center(); diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index ca1ecd0..1558b1e 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -90,6 +90,15 @@ fn show_tile_heights( Color::WHITE, ); } + let nbors = map.get_neighbors(&contact_coord); + for i in 0..6 { + if let Some(s) = nbors[i] { + let coord = contact_coord.get_neighbor(i); + let p = coord.to_world(s); + gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), Color::WHITE); + } + } + gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, Srgba::rgb(1., 0., 0.5)); } } From 911cf8d5c3c12308862118b539f649b003be98bb Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 5 Aug 2024 20:52:25 -0400 Subject: [PATCH 08/24] render map image --- Cargo.lock | 1 + engine/world_generation/src/biome_painter.rs | 2 +- engine/world_generation/src/map/config.rs | 2 +- engine/world_generation/src/map/map_utils.rs | 17 +++---- game/main/Cargo.toml | 1 + game/main/src/map_rendering/map_init.rs | 7 +-- game/main/src/phos.rs | 3 ++ game/main/src/utlis/editor_plugin.rs | 49 ++++++++++++++++++++ game/main/src/utlis/mod.rs | 1 + 9 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 game/main/src/utlis/editor_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index caf296d..0c8b2d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3662,6 +3662,7 @@ dependencies = [ "bevy_asset_loader", "bevy_rapier3d", "buildings", + "image 0.25.2", "iyes_perf_ui", "noise 0.8.2", "rayon", diff --git a/engine/world_generation/src/biome_painter.rs b/engine/world_generation/src/biome_painter.rs index 802d3fc..0452d9d 100644 --- a/engine/world_generation/src/biome_painter.rs +++ b/engine/world_generation/src/biome_painter.rs @@ -38,7 +38,7 @@ impl BiomePainterAsset { } } -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct BiomePainter { pub biomes: Vec, } diff --git a/engine/world_generation/src/map/config.rs b/engine/world_generation/src/map/config.rs index 86fe848..84c22b4 100644 --- a/engine/world_generation/src/map/config.rs +++ b/engine/world_generation/src/map/config.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use super::chunk::Chunk; -#[derive(Resource, Reflect, Default)] +#[derive(Resource, Reflect, Default, Clone)] #[reflect(Resource)] pub struct GenerationConfig { pub sea_level: f64, diff --git a/engine/world_generation/src/map/map_utils.rs b/engine/world_generation/src/map/map_utils.rs index 6e6ebb6..3dc58e6 100644 --- a/engine/world_generation/src/map/map_utils.rs +++ b/engine/world_generation/src/map/map_utils.rs @@ -11,7 +11,7 @@ pub fn render_image( data: &Vec, color1: LinearRgba, color2: LinearRgba, -) -> ImageBuffer, Vec> { +) -> ImageBuffer, Vec> { let mut image = ImageBuffer::new(size.x * Chunk::SIZE as u32, size.y * Chunk::SIZE as u32); update_image(size, data, color1, color2, &mut image); @@ -23,7 +23,7 @@ pub fn update_image( data: &Vec, color1: LinearRgba, color2: LinearRgba, - image: &mut ImageBuffer, Vec>, + image: &mut ImageBuffer, Vec>, ) { let min = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&0.0); let max = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&1.0); @@ -39,14 +39,15 @@ pub fn update_image( }); } -fn to_pixel(col: &LinearRgba) -> image::Rgb { - return image::Rgb([ +fn to_pixel(col: &LinearRgba) -> image::Rgba { + return image::Rgba([ (col.red * 255.0) as u8, (col.green * 255.0) as u8, (col.blue * 255.0) as u8, + 255, ]); } -pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer, Vec> { +pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer, Vec> { let mut image = ImageBuffer::new( map.width as u32 * Chunk::SIZE as u32, map.height as u32 * Chunk::SIZE as u32, @@ -54,7 +55,7 @@ pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer, Vec update_map(map, smooth, &mut image); return image; } -pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer, Vec>) { +pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer, Vec>) { image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { let coord = HexCoord::from_grid_pos(x as usize, y as usize); let right = coord.get_neighbor(1); @@ -91,7 +92,7 @@ pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer }); } -pub fn render_biome_map(map: &Map) -> ImageBuffer, Vec> { +pub fn render_biome_map(map: &Map) -> ImageBuffer, Vec> { let mut image = ImageBuffer::new( map.width as u32 * Chunk::SIZE as u32, map.height as u32 * Chunk::SIZE as u32, @@ -100,7 +101,7 @@ pub fn render_biome_map(map: &Map) -> ImageBuffer, Vec> { return image; } -pub fn update_biome_map(map: &Map, image: &mut ImageBuffer, Vec>) { +pub fn update_biome_map(map: &Map, image: &mut ImageBuffer, Vec>) { image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { let coord = HexCoord::from_grid_pos(x as usize, y as usize); let tile = map.get_biome(&coord); diff --git a/game/main/Cargo.toml b/game/main/Cargo.toml index 657cb9c..c7c66ae 100644 --- a/game/main/Cargo.toml +++ b/game/main/Cargo.toml @@ -21,6 +21,7 @@ bevy_asset_loader = { version = "0.21.0", features = [ "3d", ] } ron = "0.8.1" +image = "0.25.2" [features] tracing = ["bevy/trace_tracy", "world_generation/tracing", "buildings/tracing"] diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index b6b7429..0a7fcd8 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -3,10 +3,12 @@ use bevy::log::*; use bevy::{ pbr::{ExtendedMaterial, NotShadowCaster}, prelude::*, + render::texture::ImageFormat, }; use bevy_asset_loader::prelude::*; use bevy_inspector_egui::quick::ResourceInspectorPlugin; +use image::DynamicImage; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use shared::states::{AssetLoadState, GameplayState, MenuState}; use world_generation::{ @@ -213,11 +215,6 @@ fn create_heightmap( }; let heightmap = generate_heightmap(&config, 42069, &biome_painter); - let game_map = render_map(&heightmap, 1.5); - let biome_map = render_biome_map(&heightmap); - _ = biome_map.save("Biomes.png"); - _ = game_map.save("Test.png"); - let (mut cam_t, cam_entity) = cam.single_mut(); cam_t.translation = heightmap.get_center(); diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index aba0cf5..7ed8296 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -1,5 +1,6 @@ use crate::camera_system::components::PhosCamera; use crate::map_rendering::map_init::MapInitPlugin; +use crate::utlis::editor_plugin::EditorPlugin; use crate::utlis::render_distance_system::RenderDistancePlugin; use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin}; use bevy::{ @@ -34,6 +35,8 @@ impl Plugin for PhosGamePlugin { BuildingPugin, DespawnPuglin, #[cfg(debug_assertions)] + EditorPlugin, + #[cfg(debug_assertions)] DebugPlugin, )); diff --git a/game/main/src/utlis/editor_plugin.rs b/game/main/src/utlis/editor_plugin.rs new file mode 100644 index 0000000..2460c55 --- /dev/null +++ b/game/main/src/utlis/editor_plugin.rs @@ -0,0 +1,49 @@ +use bevy::{prelude::*, render::render_asset::RenderAssetUsages}; +use bevy_inspector_egui::bevy_egui::EguiContexts; +use bevy_inspector_egui::egui::{self}; +use world_generation::{map::map_utils::render_map, prelude::Map, states::GeneratorState}; + +pub struct EditorPlugin; + +impl Plugin for EditorPlugin { + fn build(&self, app: &mut App) { + app.init_resource::(); + + app.add_systems(PostUpdate, prepare_image.run_if(in_state(GeneratorState::SpawnMap))); + app.add_systems(Update, (render_map_ui).run_if(in_state(GeneratorState::Idle))); + } +} + +#[derive(Resource)] +struct MapImage(pub Handle); + +pub fn prepare_image(mut images: ResMut>, heightmap: Res, mut commands: Commands) { + let image = render_map(&heightmap, 0.1); + let handle = images.add(Image::from_dynamic(image.into(), true, RenderAssetUsages::RENDER_WORLD)); + + commands.insert_resource(MapImage(handle)); +} + +#[derive(Resource)] +struct UIState { + pub is_open: bool, +} + +impl Default for UIState { + fn default() -> Self { + Self { is_open: true } + } +} + +fn render_map_ui(image: Res, mut contexts: EguiContexts, mut state: ResMut) { + let id = contexts.add_image(image.0.clone_weak()); + + let ctx = contexts.ctx_mut(); + egui::Window::new("Map").open(&mut state.is_open).show(ctx, |ui| { + ui.label("Map Test"); + ui.add(egui::widgets::Image::new(egui::load::SizedTexture::new( + id, + [512.0, 512.0], + ))); + }); +} diff --git a/game/main/src/utlis/mod.rs b/game/main/src/utlis/mod.rs index b2154a8..c307916 100644 --- a/game/main/src/utlis/mod.rs +++ b/game/main/src/utlis/mod.rs @@ -1,3 +1,4 @@ pub mod chunk_utils; pub mod render_distance_system; pub mod debug_plugin; +pub mod editor_plugin; From a8409e5720c7149b1432ab9d5e7346515e90aeff Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Tue, 6 Aug 2024 20:49:47 -0400 Subject: [PATCH 09/24] finally realized that bevy uses right handed coords --- engine/world_generation/src/heightmap.rs | 1 - engine/world_generation/src/hex_utils.rs | 8 +++--- engine/world_generation/src/map/mesh_chunk.rs | 2 +- game/buildings/src/building_plugin.rs | 3 +++ game/main/src/camera_system/camera_plugin.rs | 19 +++++++------ game/main/src/camera_system/components.rs | 2 +- game/main/src/map_rendering/map_init.rs | 2 +- .../src/map_rendering/terraforming_test.rs | 3 +++ game/main/src/utlis/debug_plugin.rs | 27 ++++++++++++------- 9 files changed, 42 insertions(+), 25 deletions(-) diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index 49e9458..4312ba6 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -2,7 +2,6 @@ use core::f32; use bevy::math::{IVec2, UVec2}; use bevy::prelude::{FloatExt, Vec2}; -use bevy::render::render_resource::encase::internal::BufferMut; use bevy::utils::default; use noise::{NoiseFn, SuperSimplex}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index 5cacc23..ba20e2c 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -72,12 +72,12 @@ impl Display for HexCoord { impl HexCoord { pub const DIRECTIONS: [IVec3; 6] = [ - IVec3::new(1, -1, 0), - IVec3::new(1, 0, -1), IVec3::new(0, 1, -1), - IVec3::new(-1, 1, 0), - IVec3::new(-1, 0, 1), + IVec3::new(1, 0, -1), + IVec3::new(1, -1, 0), IVec3::new(0, -1, 1), + IVec3::new(-1, 0, 1), + IVec3::new(-1, 1, 0), ]; pub const ZERO: HexCoord = HexCoord { hex: IVec3::ZERO }; diff --git a/engine/world_generation/src/map/mesh_chunk.rs b/engine/world_generation/src/map/mesh_chunk.rs index be87f78..3a09423 100644 --- a/engine/world_generation/src/map/mesh_chunk.rs +++ b/engine/world_generation/src/map/mesh_chunk.rs @@ -11,7 +11,7 @@ impl MeshChunkData { pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] { let mut data = [0.; 6]; let n_tiles = coord.get_neighbors(); - for i in 6..0 { + for i in 0..6 { let n = n_tiles[i]; if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) { continue; diff --git a/game/buildings/src/building_plugin.rs b/game/buildings/src/building_plugin.rs index 9ca45d9..cd18a07 100644 --- a/game/buildings/src/building_plugin.rs +++ b/game/buildings/src/building_plugin.rs @@ -106,6 +106,9 @@ fn hq_placement( if let Some((_e, dist)) = collision { let contact_point = cam_ray.get_point(dist); let contact_coord = HexCoord::from_world_pos(contact_point); + if !map.is_in_bounds(&contact_coord) { + return; + } let positions = map.hex_select(&contact_coord, 3, true, |pos, h, _| pos.to_world(h)); show_indicators(positions, &mut commands, &indicator); diff --git a/game/main/src/camera_system/camera_plugin.rs b/game/main/src/camera_system/camera_plugin.rs index 9a947ab..43b08bf 100644 --- a/game/main/src/camera_system/camera_plugin.rs +++ b/game/main/src/camera_system/camera_plugin.rs @@ -31,7 +31,7 @@ fn setup(mut commands: Commands, mut msaa: ResMut) { commands .spawn(( Camera3dBundle { - transform: Transform::from_xyz(0., 30., 0.).looking_to(Vec3::Z, Vec3::Y), + transform: Transform::from_xyz(0., 30., 0.).looking_to(Vec3::NEG_Z, Vec3::Y), ..default() }, PhosCamera::default(), @@ -138,15 +138,15 @@ fn rts_camera_system( let mut cam_pos = cam.translation; if key.pressed(KeyCode::KeyA) { - cam_move.x = 1.; - } else if key.pressed(KeyCode::KeyD) { cam_move.x = -1.; + } else if key.pressed(KeyCode::KeyD) { + cam_move.x = 1.; } if key.pressed(KeyCode::KeyW) { - cam_move.z = 1.; - } else if key.pressed(KeyCode::KeyS) { cam_move.z = -1.; + } else if key.pressed(KeyCode::KeyS) { + cam_move.z = 1.; } let move_speed = if key.pressed(KeyCode::ShiftLeft) { @@ -156,7 +156,7 @@ fn rts_camera_system( }; cam_move = cam_move.normalize_or_zero() * move_speed * time.delta_seconds(); - cam_pos -= cam_move; + cam_pos += cam_move; let mut scroll = 0.0; for e in wheel.read() { @@ -209,8 +209,11 @@ fn rts_camera_system( cam_targets.rotate_time = cam_targets.rotate_time.min(1.); } let angle = cam_cfg.min_angle.lerp(cam_cfg.max_angle, t); - let rot = Quat::from_axis_angle(Vec3::X, -angle); - cam.rotation = rot; + let mut rot = cam.rotation.to_euler(EulerRot::XYZ); + rot.0 = -angle; + cam.rotation = Quat::from_euler(EulerRot::XYZ, rot.0, rot.1, rot.2); + // let rot = Quat::from_axis_angle(Vec3::X, -angle); + // cam.rotation = rot; cam.translation = cam_pos; } diff --git a/game/main/src/camera_system/components.rs b/game/main/src/camera_system/components.rs index ec83e77..28510d2 100644 --- a/game/main/src/camera_system/components.rs +++ b/game/main/src/camera_system/components.rs @@ -38,7 +38,7 @@ impl Default for PhosCameraTargets { fn default() -> Self { Self { height: Default::default(), - forward: Vec3::Z, + forward: Vec3::NEG_Z, last_height: Default::default(), anim_time: Default::default(), rotate_time: Default::default(), diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 0a7fcd8..07b2606 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -55,7 +55,7 @@ impl Plugin for MapInitPlugin { app.register_asset_reflect::>(); app.add_plugins(( ChunkRebuildPlugin, - TerraFormingTestPlugin, + // TerraFormingTestPlugin, MaterialPlugin::>::default(), MaterialPlugin::> { prepass_enabled: false, diff --git a/game/main/src/map_rendering/terraforming_test.rs b/game/main/src/map_rendering/terraforming_test.rs index 382a804..53edbcd 100644 --- a/game/main/src/map_rendering/terraforming_test.rs +++ b/game/main/src/map_rendering/terraforming_test.rs @@ -69,6 +69,9 @@ fn deform( let span = info_span!("Deform Mesh").entered(); let contact_point = cam_ray.get_point(dist); let contact_coord = HexCoord::from_world_pos(contact_point); + if !heightmap.is_in_bounds(&contact_coord) { + return; + } let modified_tiles = heightmap.create_crater(&contact_coord, 5, 5. * multi); let mut chunk_set: HashSet = HashSet::new(); for (tile, height) in modified_tiles { diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index 1558b1e..9437b75 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -81,25 +81,34 @@ fn show_tile_heights( if let Some((_e, dist)) = collision { let contact_point = cam_ray.get_point(dist); let contact_coord = HexCoord::from_world_pos(contact_point); - if map.is_in_bounds(&contact_coord) { - let height = map.sample_height(&contact_coord); - gizmos.primitive_3d( - &shape.0, - contact_coord.to_world(height + 0.01), - Quat::IDENTITY, - Color::WHITE, - ); + if !map.is_in_bounds(&contact_coord) { + return; } + let height = map.sample_height(&contact_coord); + gizmos.primitive_3d( + &shape.0, + contact_coord.to_world(height + 0.01), + Quat::IDENTITY, + Color::WHITE, + ); let nbors = map.get_neighbors(&contact_coord); + let contact_tile_pos = contact_coord.to_world(map.sample_height(&contact_coord)); + for i in 0..6 { if let Some(s) = nbors[i] { let coord = contact_coord.get_neighbor(i); let p = coord.to_world(s); gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), Color::WHITE); } + + let p = HEX_CORNERS[i] + contact_tile_pos; + gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), LinearRgba::rgb(1.0, 0.0, 0.5)); } - gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, Srgba::rgb(1., 0., 0.5)); + gizmos.line(contact_point, contact_point + Vec3::X, LinearRgba::RED); + gizmos.line(contact_point, contact_point + Vec3::Y, LinearRgba::GREEN); + gizmos.line(contact_point, contact_point + Vec3::Z, LinearRgba::BLUE); + //gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, LinearRgba::rgb(1., 0., 0.5)); } } From b96b0c5c66d26075a3536b11054ed8edbeb74608 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Tue, 6 Aug 2024 21:29:10 -0400 Subject: [PATCH 10/24] added unit tests to mesh generation --- .../src/generators/mesh_generator.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/engine/world_generation/src/generators/mesh_generator.rs b/engine/world_generation/src/generators/mesh_generator.rs index 17747d1..b7b9a74 100644 --- a/engine/world_generation/src/generators/mesh_generator.rs +++ b/engine/world_generation/src/generators/mesh_generator.rs @@ -135,3 +135,60 @@ fn create_tile_wall( uvs.push((Vec2::new(0., pos.y - height) / TEX_MULTI) + tex_off); uvs.push((Vec2::new(1., pos.y - height) / TEX_MULTI) + tex_off); } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn generate_tile_wall() { + let mut verts = Vec::new(); + let mut uvs = Vec::new(); + let mut normals = Vec::new(); + let mut indices = Vec::new(); + + create_tile_wall( + Vec3::ZERO, + 3, + 5.0, + &mut verts, + &mut uvs, + &mut indices, + &mut normals, + Vec2::new(3.0, 0.0), + ); + + assert!(verts.len() == 4, "Number of verts don't match"); + assert!(uvs.len() == 4, "Number of uvs don't match"); + assert!(normals.len() == 4, "Number of normals don't match"); + assert!(indices.len() == 6, "Number of normals don't match"); + + let index = uvs[0].x.floor(); + assert!(index == 3.0, "Texture Index could not be decoded"); + } + + #[test] + fn generate_tile() { + let mut verts = Vec::new(); + let mut uvs = Vec::new(); + let mut normals = Vec::new(); + let mut indices = Vec::new(); + + //4 side faces + let nbors = [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]; + + create_tile(Vec3::Y, &nbors, &mut verts, &mut uvs, &mut indices, &mut normals, 3, 7); + + assert!(verts.len() == (6 + 4 * 4), "Number of verts don't match"); + assert!(uvs.len() == (6 + 4 * 4), "Number of uvs don't match"); + assert!(normals.len() == (6 + 4 * 4), "Number of normals don't match"); + //12 tris for surface, 6 tris per side + assert!(indices.len() == (12 + 4 * 6), "Number of indicies don't match"); + + let top_index = uvs[0].x.floor(); + assert!(top_index == 3.0, "Top Texture Index could not be decoded"); + let side_index = uvs[6].x.floor(); + assert!(side_index == 7.0, "Top Texture Index could not be decoded"); + } +} From 25b6ad10999ccb7b0dbfffec05f8b0850e383ea2 Mon Sep 17 00:00:00 2001 From: Khamraj Rohit Date: Thu, 8 Aug 2024 15:58:47 -0400 Subject: [PATCH 11/24] Update rust.yml --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cdec66f..09a2b74 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,6 +19,6 @@ jobs: - name: Install deps run: sudo apt install libasound2-dev libudev-dev - name: Build - run: cargo build --verbose + run: cargo build - name: Run tests - run: cargo test --verbose + run: cargo test From 77fa421bb27687d031a9e66f29264678aea98e55 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Tue, 27 Aug 2024 23:04:03 -0400 Subject: [PATCH 12/24] units wip --- Cargo.lock | 16 ++++++ Cargo.toml | 2 +- game/buildings/src/building_plugin.rs | 5 +- game/main/Cargo.toml | 1 + game/main/src/map_rendering/chunk_rebuild.rs | 2 +- game/main/src/map_rendering/map_init.rs | 4 +- game/main/src/utlis/debug_plugin.rs | 18 ++++++- game/shared/src/lib.rs | 1 + game/shared/src/sets.rs | 4 ++ game/shared/src/tags.rs | 6 +++ game/units/Cargo.toml | 21 ++++++++ game/units/src/assets/mod.rs | 3 ++ game/units/src/assets/unit_asset.rs | 52 ++++++++++++++++++++ game/units/src/assets/unit_database.rs | 10 ++++ game/units/src/components.rs | 12 +++++ game/units/src/lib.rs | 5 ++ game/units/src/units_debug_plugin.rs | 18 +++++++ game/units/src/units_plugin.rs | 37 ++++++++++++++ 18 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 game/shared/src/sets.rs create mode 100644 game/units/Cargo.toml create mode 100644 game/units/src/assets/mod.rs create mode 100644 game/units/src/assets/unit_asset.rs create mode 100644 game/units/src/assets/unit_database.rs create mode 100644 game/units/src/components.rs create mode 100644 game/units/src/lib.rs create mode 100644 game/units/src/units_debug_plugin.rs create mode 100644 game/units/src/units_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index 0c8b2d8..e4f008c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3668,6 +3668,7 @@ dependencies = [ "rayon", "ron", "shared", + "units", "world_generation", ] @@ -4836,6 +4837,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "units" +version = "0.1.0" +dependencies = [ + "asset_loader", + "bevy", + "bevy_asset_loader", + "bevy_rapier3d", + "ron", + "serde", + "serde_json", + "shared", + "world_generation", +] + [[package]] name = "url" version = "2.5.2" diff --git a/Cargo.toml b/Cargo.toml index a5b56d5..5a441d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "game/buildings", "game/shared", "engine/world_generation", - "engine/asset_loader", "game/buildings", "game/shared"] + "engine/asset_loader", "game/buildings", "game/shared", "game/units"] # Enable a small amount of optimization in debug mode [profile.dev] diff --git a/game/buildings/src/building_plugin.rs b/game/buildings/src/building_plugin.rs index cd18a07..2115002 100644 --- a/game/buildings/src/building_plugin.rs +++ b/game/buildings/src/building_plugin.rs @@ -38,7 +38,10 @@ impl Plugin for BuildingPugin { ); app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading))); - app.add_systems(Update, hq_placement.run_if(in_state(GameplayState::PlaceHQ))); + app.add_systems( + Update, + hq_placement.run_if(in_state(GameplayState::PlaceHQ).and_then(in_state(GeneratorState::Idle))), + ); app.add_systems( PreUpdate, prepare_building_map.run_if(in_state(GeneratorState::SpawnMap)), diff --git a/game/main/Cargo.toml b/game/main/Cargo.toml index c7c66ae..943a7ab 100644 --- a/game/main/Cargo.toml +++ b/game/main/Cargo.toml @@ -15,6 +15,7 @@ world_generation = { path = "../../engine/world_generation" } bevy_rapier3d = { version = "0.27.0", features = ["simd-stable", "parallel"] } rayon = "1.10.0" buildings = { path = "../buildings" } +units = { path = "../units" } shared = { path = "../shared" } bevy_asset_loader = { version = "0.21.0", features = [ "standard_dynamic_assets", diff --git a/game/main/src/map_rendering/chunk_rebuild.rs b/game/main/src/map_rendering/chunk_rebuild.rs index 418aa60..6783b65 100644 --- a/game/main/src/map_rendering/chunk_rebuild.rs +++ b/game/main/src/map_rendering/chunk_rebuild.rs @@ -37,7 +37,7 @@ fn chunk_rebuilder( for (chunk_entity, idx) in &chunk_query { #[cfg(feature = "tracing")] let _spawn_span = info_span!("Rebuild Chunk").entered(); - println!("Rebuilding Chunk"); + info!("Rebuilding Chunk"); let chunk_index = idx.index; let chunk_data = heightmap.get_chunk_mesh_data(chunk_index); let chunk_offset = heightmap.chunks[chunk_index].chunk_offset; diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 07b2606..10a8ea8 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -84,9 +84,7 @@ impl Plugin for MapInitPlugin { app.add_systems(Update, despawn_map.run_if(in_state(GeneratorState::Regenerate))); app.add_systems( Update, - spawn_map - .run_if(in_state(AssetLoadState::LoadComplete)) - .run_if(in_state(GeneratorState::SpawnMap)), + spawn_map.run_if(in_state(AssetLoadState::LoadComplete).and_then(in_state(GeneratorState::SpawnMap))), ); app.insert_resource(TileManager::default()); diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index 9437b75..35be8ef 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -1,8 +1,10 @@ use bevy::{prelude::*, window::PrimaryWindow}; -use bevy_inspector_egui::bevy_egui::EguiContexts; +use bevy_inspector_egui::bevy_egui::{systems::InputEvents, EguiContexts}; use bevy_inspector_egui::egui; use bevy_rapier3d::prelude::*; +use shared::states::GameplayState; use shared::tags::MainCamera; +use units::units_debug_plugin::UnitsDebugPlugin; use world_generation::{ consts::HEX_CORNERS, hex_utils::{HexCoord, INNER_RADIUS}, @@ -14,6 +16,7 @@ pub struct DebugPlugin; impl Plugin for DebugPlugin { fn build(&self, app: &mut App) { + app.add_plugins(UnitsDebugPlugin); app.insert_state(DebugState::Base); app.add_systems( @@ -30,6 +33,8 @@ impl Plugin for DebugPlugin { .run_if(in_state(DebugState::Verbose)), ); + app.add_systems(Update, regenerate_map.run_if(in_state(GeneratorState::Idle))); + app.insert_resource(Shape(Polyline3d::new([ HEX_CORNERS[0], HEX_CORNERS[1], @@ -52,6 +57,17 @@ pub enum DebugState { Verbose, } +fn regenerate_map( + mut generator_state: ResMut>, + mut gameplay_state: ResMut>, + input: Res>, +) { + if input.just_pressed(KeyCode::KeyR) { + generator_state.set(GeneratorState::Regenerate); + gameplay_state.set(GameplayState::PlaceHQ); + } +} + fn show_tile_heights( cam_query: Query<(&GlobalTransform, &Camera), With>, window: Query<&Window, With>, diff --git a/game/shared/src/lib.rs b/game/shared/src/lib.rs index 244a2c5..0d6610b 100644 --- a/game/shared/src/lib.rs +++ b/game/shared/src/lib.rs @@ -4,3 +4,4 @@ pub mod resource; pub mod states; pub mod tags; pub mod events; +pub mod sets; diff --git a/game/shared/src/sets.rs b/game/shared/src/sets.rs new file mode 100644 index 0000000..d5102f3 --- /dev/null +++ b/game/shared/src/sets.rs @@ -0,0 +1,4 @@ +use bevy::prelude::*; + +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub struct GameplaySet; diff --git a/game/shared/src/tags.rs b/game/shared/src/tags.rs index 96a24d9..093b86e 100644 --- a/game/shared/src/tags.rs +++ b/game/shared/src/tags.rs @@ -1,3 +1,9 @@ use bevy::prelude::*; #[derive(Component)] pub struct MainCamera; + +#[derive(Component)] +pub enum Faction { + Player, + Phos, +} diff --git a/game/units/Cargo.toml b/game/units/Cargo.toml new file mode 100644 index 0000000..2f716ad --- /dev/null +++ b/game/units/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "units" +version = "0.1.0" +edition = "2021" + +[dependencies] +bevy = "0.14.0" +world_generation = { path = "../../engine/world_generation" } +shared = { path = "../shared" } +bevy_rapier3d = "0.27.0" +serde = { version = "1.0.204", features = ["derive"] } +asset_loader = { path = "../../engine/asset_loader" } +serde_json = "1.0.120" +ron = "0.8.1" +bevy_asset_loader = { version = "0.21.0", features = [ + "standard_dynamic_assets", + "3d", +] } + +[features] +tracing = [] diff --git a/game/units/src/assets/mod.rs b/game/units/src/assets/mod.rs new file mode 100644 index 0000000..77724e5 --- /dev/null +++ b/game/units/src/assets/mod.rs @@ -0,0 +1,3 @@ + +pub mod unit_asset; +pub mod unit_database; diff --git a/game/units/src/assets/unit_asset.rs b/game/units/src/assets/unit_asset.rs new file mode 100644 index 0000000..94c077d --- /dev/null +++ b/game/units/src/assets/unit_asset.rs @@ -0,0 +1,52 @@ +use asset_loader::create_asset_loader; +use bevy::{ecs::world::CommandQueue, prelude::*}; +use serde::{Deserialize, Serialize}; + +use crate::components::{Unit, UnitDomain}; + +#[derive(Asset, TypePath, Debug, Serialize, Deserialize)] +pub struct UnitAsset { + pub name: String, + pub description: String, + pub size: u32, + pub prefab_path: String, + #[serde(skip)] + pub prefab: Handle, + pub unit_type: UnitType, + pub domain: UnitDomain, +} + +impl UnitAsset { + pub fn spawn(&self, transform: Transform) -> CommandQueue { + let mut commands = CommandQueue::default(); + + let bundle = ( + PbrBundle { + transform: transform, + ..default() + }, + Unit, + self.domain.clone(), + ); + commands.push(move |world: &mut World| { + world.spawn(bundle); + }); + + todo!(); + } +} + +create_asset_loader!( + UnitAssetPlugin, + UnitAssetLoader, + UnitAsset, + &["unit", "unit.ron"], + prefab_path -> prefab + ;? +); + +#[derive(Debug, Serialize, Deserialize)] +pub enum UnitType { + Basic, + Turret, +} diff --git a/game/units/src/assets/unit_database.rs b/game/units/src/assets/unit_database.rs new file mode 100644 index 0000000..363d09e --- /dev/null +++ b/game/units/src/assets/unit_database.rs @@ -0,0 +1,10 @@ +use bevy::prelude::*; +use bevy_asset_loader::asset_collection::AssetCollection; + +use super::unit_asset::UnitAsset; + +#[derive(Resource, AssetCollection)] +pub struct UnitDatabase { + #[asset(key = "units", collection(typed))] + pub units: Vec>, +} diff --git a/game/units/src/components.rs b/game/units/src/components.rs new file mode 100644 index 0000000..ff9be19 --- /dev/null +++ b/game/units/src/components.rs @@ -0,0 +1,12 @@ +use bevy::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Component, Debug)] +pub struct Unit; + +#[derive(Component, Serialize, Deserialize, Debug, Clone, Copy)] +pub enum UnitDomain { + Land, + Air, + Navy, +} diff --git a/game/units/src/lib.rs b/game/units/src/lib.rs new file mode 100644 index 0000000..3c8f634 --- /dev/null +++ b/game/units/src/lib.rs @@ -0,0 +1,5 @@ +#[cfg(debug_assertions)] +pub mod units_debug_plugin; +pub mod units_plugin; +pub mod components; +pub mod assets; diff --git a/game/units/src/units_debug_plugin.rs b/game/units/src/units_debug_plugin.rs new file mode 100644 index 0000000..bca2d0c --- /dev/null +++ b/game/units/src/units_debug_plugin.rs @@ -0,0 +1,18 @@ +use bevy::prelude::*; +use shared::states::GameplayState; +use world_generation::states::GeneratorState; + +pub struct UnitsDebugPlugin; + +impl Plugin for UnitsDebugPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + Update, + spawn_test_unit.run_if(in_state(GeneratorState::Idle).and_then(in_state(GameplayState::Playing))), + ); + } +} + +fn spawn_test_unit(mut commands: Commands, input: Res>) { + +} diff --git a/game/units/src/units_plugin.rs b/game/units/src/units_plugin.rs new file mode 100644 index 0000000..a89e0df --- /dev/null +++ b/game/units/src/units_plugin.rs @@ -0,0 +1,37 @@ +use bevy::{prelude::*, window::PrimaryWindow}; +use bevy_asset_loader::loading_state::{ + config::{ConfigureLoadingState, LoadingStateConfig}, + LoadingStateAppExt, +}; +use shared::states::{AssetLoadState, GameplayState}; +use world_generation::states::GeneratorState; + +use crate::assets::{unit_asset::UnitAssetPlugin, unit_database::UnitDatabase}; + +pub struct UnitsPlugin; + +impl Plugin for UnitsPlugin { + fn build(&self, app: &mut App) { + app.add_plugins(UnitAssetPlugin); + + app.configure_loading_state(LoadingStateConfig::new(AssetLoadState::Loading).load_collection::()); + + app.add_systems(Update, units_control.in_set(UnitUpdateSet)); + + app.configure_sets( + Update, + UnitUpdateSet.run_if(in_state(GameplayState::Playing).and_then(in_state(GeneratorState::Idle))), + ); + } +} + +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +struct UnitUpdateSet; + +fn units_control(input: Res>, window: Query<&Window, With>) { + let win = window.single(); + + let Some(cursor_pos) = win.cursor_position() else { + return; + }; +} From 1dc632925222118998d85a9e38572a98f611df48 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 1 Sep 2024 22:49:48 -0400 Subject: [PATCH 13/24] generator tweaks data fixes wip biome map visualization --- .../src/generators/packed_mesh_generator.rs | 4 +- engine/world_generation/src/heightmap.rs | 28 +++--- engine/world_generation/src/map/biome_map.rs | 7 +- engine/world_generation/src/map/chunk.rs | 4 +- engine/world_generation/src/map/map.rs | 15 ++- engine/world_generation/src/map/map_utils.rs | 93 +++++++++++++------ game/main/src/map_rendering/map_init.rs | 11 ++- 7 files changed, 114 insertions(+), 48 deletions(-) diff --git a/engine/world_generation/src/generators/packed_mesh_generator.rs b/engine/world_generation/src/generators/packed_mesh_generator.rs index 8660bc9..c32b085 100644 --- a/engine/world_generation/src/generators/packed_mesh_generator.rs +++ b/engine/world_generation/src/generators/packed_mesh_generator.rs @@ -1,4 +1,5 @@ use crate::hex_utils::HexCoord; +use crate::map::biome_map::{self, BiomeChunk, BiomeMap}; use crate::prelude::*; use crate::tile_manager::TileAsset; use crate::tile_mapper::TileMapperAsset; @@ -14,6 +15,7 @@ use bevy::{ pub fn generate_packed_chunk_mesh( chunk: &Chunk, map: &Map, + biome_chunk: &BiomeChunk, painter: &BiomePainterAsset, tiles: &Res>, biomes: &Res>, @@ -27,7 +29,7 @@ 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 data = chunk.biome_data[x + z * Chunk::SIZE]; + let data = biome_chunk.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); diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index 4312ba6..4160dce 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -10,14 +10,15 @@ use crate::biome_painter::BiomePainter; use crate::map::biome_map::{BiomeChunk, BiomeData, BiomeMap}; use crate::prelude::*; -pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> Map { - let biomes = &generate_biomes(cfg, seed, painter); +pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap) { + let biomes = generate_biomes(cfg, seed, painter); + let biomes_borrow = &biomes; // let mut chunks: Vec = Vec::with_capacity(cfg.size.length_squared() as usize); let chunks: Vec = (0..cfg.size.y) .into_par_iter() .flat_map(|z| { (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]; + let biome_chunk = &biomes_borrow.chunks[x as usize + z as usize * cfg.size.x as usize]; return generate_chunk(x, z, cfg, seed, &biome_chunk, painter); }) }) @@ -33,14 +34,18 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain } } - return Map { - chunks, - height: cfg.size.y as usize, - width: cfg.size.x as usize, - sea_level: cfg.sea_level as f32, - min_level: min, - max_level: max, - }; + return ( + Map { + chunks, + height: cfg.size.y as usize, + width: cfg.size.x as usize, + sea_level: cfg.sea_level as f32, + min_level: min, + max_level: max, + biome_count: painter.biomes.len(), + }, + biomes, + ); } pub fn generate_biomes(cfg: &GenerationConfig, seed: u32, biome_painter: &BiomePainter) -> BiomeMap { @@ -186,7 +191,6 @@ pub fn generate_chunk( } return Chunk { heights: result, - biome_data: data, biome_id: biome_ids, chunk_offset: IVec2::new(chunk_x as i32, chunk_z as i32), max_level: max, diff --git a/engine/world_generation/src/map/biome_map.rs b/engine/world_generation/src/map/biome_map.rs index 213ba55..4fda922 100644 --- a/engine/world_generation/src/map/biome_map.rs +++ b/engine/world_generation/src/map/biome_map.rs @@ -1,10 +1,13 @@ -use bevy::math::{UVec2, Vec3}; +use bevy::{ + math::{UVec2, Vec3}, + prelude::Resource, +}; use noise::NoiseFn; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use super::chunk::Chunk; -#[derive(Clone)] +#[derive(Clone, Resource)] pub struct BiomeMap { pub height: usize, pub width: usize, diff --git a/engine/world_generation/src/map/chunk.rs b/engine/world_generation/src/map/chunk.rs index ef37799..f83d185 100644 --- a/engine/world_generation/src/map/chunk.rs +++ b/engine/world_generation/src/map/chunk.rs @@ -7,7 +7,7 @@ use super::biome_map::BiomeData; pub struct Chunk { pub heights: [f32; Chunk::AREA], pub textures: [[u32; 2]; Chunk::AREA], - pub biome_data: [BiomeData; Chunk::AREA], + // pub biome_data: [BiomeData; Chunk::AREA], pub biome_id: [usize; Chunk::AREA], pub chunk_offset: IVec2, pub min_level: f32, @@ -19,7 +19,7 @@ impl Default for Chunk { Self { heights: [0.; Chunk::AREA], textures: [[0; 2]; Chunk::AREA], - biome_data: [BiomeData::default(); Chunk::AREA], + // biome_data: [BiomeData::default(); Chunk::AREA], biome_id: [0; Chunk::AREA], chunk_offset: Default::default(), min_level: 0.0, diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index b344ee5..cf690f5 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -16,6 +16,7 @@ pub struct Map { pub sea_level: f32, pub min_level: f32, pub max_level: f32, + pub biome_count: usize, } impl Map { @@ -72,7 +73,18 @@ impl Map { return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE); } - pub fn get_biome(&self, pos: &HexCoord) -> &BiomeData { + + pub fn get_biome_id(&self, pos: &HexCoord) -> usize { + assert!( + self.is_in_bounds(pos), + "The provided coordinate is not within the map bounds" + ); + + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; + return chunk.biome_id[pos.to_chunk_local_index()]; + } + /* + pub fn get_biome_noise(&self, pos: &HexCoord) -> &BiomeData { assert!( self.is_in_bounds(pos), "The provided coordinate is not within the map bounds" @@ -111,6 +123,7 @@ impl Map { let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.biome_data[pos.to_chunk_local_index()].continentality; } + */ pub fn get_center(&self) -> Vec3 { let w = self.get_world_width(); diff --git a/engine/world_generation/src/map/map_utils.rs b/engine/world_generation/src/map/map_utils.rs index 3dc58e6..de7745d 100644 --- a/engine/world_generation/src/map/map_utils.rs +++ b/engine/world_generation/src/map/map_utils.rs @@ -2,9 +2,13 @@ use bevy::{math::VectorSpace, prelude::*}; use image::ImageBuffer; use rayon::prelude::*; -use crate::hex_utils::HexCoord; +use crate::{biome_painter::BiomePainter, hex_utils::HexCoord}; -use super::{biome_map::BiomeMap, chunk::Chunk, map::Map}; +use super::{ + biome_map::{self, BiomeMap}, + chunk::Chunk, + map::Map, +}; pub fn render_image( size: UVec2, @@ -60,51 +64,58 @@ pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer smooth { - if d > 0.0 { - color.lightness += 0.1; - } else if d < 0.0 { - color.lightness -= 0.1; - } - } else { - if d.abs() <= smooth { - d /= smooth; - if d > 0.0 { - let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into(); - color = LinearRgba::lerp(&color.into(), c2, d).into(); - } else { - let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into(); - color = LinearRgba::lerp(&color.into(), c2, d.abs()).into(); - } - } - } + color = get_height_color_blend(color, height, h2, smooth); } *pixel = to_pixel(&color.into()); }); } -pub fn render_biome_map(map: &Map) -> ImageBuffer, Vec> { +fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f32) -> Hsla { + let mut color = base_color; + let mut d = height2 - height; + if smooth == 0.0 || d.abs() > smooth { + if d > 0.0 { + color.lightness += 0.1; + } else if d < 0.0 { + color.lightness -= 0.1; + } + } else { + if d.abs() <= smooth { + d /= smooth; + if d > 0.0 { + let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into(); + color = LinearRgba::lerp(&color.into(), c2, d).into(); + } else { + let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into(); + color = LinearRgba::lerp(&color.into(), c2, d.abs()).into(); + } + } + } + + return color; +} + +pub fn render_biome_noise_map(map: &BiomeMap) -> ImageBuffer, Vec> { let mut image = ImageBuffer::new( map.width as u32 * Chunk::SIZE as u32, map.height as u32 * Chunk::SIZE as u32, ); - update_biome_map(map, &mut image); + update_biome_noise_map(map, &mut image); return image; } -pub fn update_biome_map(map: &Map, image: &mut ImageBuffer, Vec>) { +pub fn update_biome_noise_map(map: &BiomeMap, image: &mut ImageBuffer, Vec>) { image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { - let coord = HexCoord::from_grid_pos(x as usize, y as usize); - let tile = map.get_biome(&coord); + let tile = map.get_biome_data(x as usize, y as usize); let color = LinearRgba::rgb( tile.temperature / 100.0, @@ -114,3 +125,31 @@ pub fn update_biome_map(map: &Map, image: &mut ImageBuffer, Vec< *pixel = to_pixel(&color); }); } + +pub fn render_biome_map(map: &Map, biome_map: &BiomeMap) -> ImageBuffer, Vec> { + let mut image = ImageBuffer::new( + map.width as u32 * Chunk::SIZE as u32, + map.height as u32 * Chunk::SIZE as u32, + ); + update_biome_map(map, biome_map, &mut image); + return image; +} + +pub fn update_biome_map(map: &Map, biome_map: &BiomeMap, image: &mut ImageBuffer, Vec>) { + let map_biome_count = map.biome_count as f32; + image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { + let coord = HexCoord::from_grid_pos(x as usize, y as usize); + let biome_blend = biome_map.get_biome(x as i32, y as i32); + let right = coord.get_neighbor(1); + let biome_id = map.get_biome_id(&coord) as f32; + let hue = (biome_id / map_biome_count) * 360.0; + let mut color = Hsla::hsl(hue, 0.8, 0.9); + if map.is_in_bounds(&right) { + let h1 = map.sample_height(&coord); + let h2 = map.sample_height(&right); + color = get_height_color_blend(color, h1, h2, 0.5); + } + + *pixel = to_pixel(&color.into()); + }); +} diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 10a8ea8..74a8119 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -16,7 +16,10 @@ use world_generation::{ biome_painter::*, heightmap::generate_heightmap, hex_utils::{offset_to_index, SHORT_DIAGONAL}, - map::map_utils::{render_biome_map, render_map}, + map::{ + biome_map::{self, BiomeMap}, + map_utils::{render_biome_noise_map, render_map}, + }, prelude::*, tile_manager::*, tile_mapper::*, @@ -211,13 +214,14 @@ fn create_heightmap( size: UVec2::splat(16), // size: UVec2::splat(1), }; - let heightmap = generate_heightmap(&config, 42069, &biome_painter); + let (heightmap, biome_map) = generate_heightmap(&config, 42069, &biome_painter); let (mut cam_t, cam_entity) = cam.single_mut(); cam_t.translation = heightmap.get_center(); commands.entity(cam_entity).insert(CameraBounds::from_size(config.size)); commands.insert_resource(heightmap); + commands.insert_resource(biome_map); commands.insert_resource(config); next_state.set(GeneratorState::SpawnMap); } @@ -297,6 +301,7 @@ fn spawn_map( fn despawn_map( mut commands: Commands, mut heightmap: ResMut, + mut biome_map: ResMut, cfg: Res, chunks: Query>, mut next_state: ResMut>, @@ -306,6 +311,6 @@ fn despawn_map( commands.entity(chunk).despawn(); } - *heightmap = generate_heightmap(&cfg, 4, &biome_painter); + (*heightmap, *biome_map) = generate_heightmap(&cfg, 4, &biome_painter); next_state.set(GeneratorState::SpawnMap); } From 343778a883533b7be7d7c7fb6a3c7b782668da13 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 2 Sep 2024 13:06:35 -0400 Subject: [PATCH 14/24] map visualization --- engine/world_generation/src/map/biome_map.rs | 2 +- engine/world_generation/src/map/map_utils.rs | 20 ++++--- game/main/src/map_rendering/map_init.rs | 4 +- game/main/src/utlis/editor_plugin.rs | 59 +++++++++++++++++++- 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/engine/world_generation/src/map/biome_map.rs b/engine/world_generation/src/map/biome_map.rs index 4fda922..6672111 100644 --- a/engine/world_generation/src/map/biome_map.rs +++ b/engine/world_generation/src/map/biome_map.rs @@ -153,7 +153,7 @@ pub struct BiomeChunk { impl BiomeChunk { pub fn get_biome(&self, x: usize, y: usize) -> &Vec { - return &self.tiles[x as usize + y as usize * Chunk::SIZE]; + return &self.tiles[x + y * Chunk::SIZE]; } pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { diff --git a/engine/world_generation/src/map/map_utils.rs b/engine/world_generation/src/map/map_utils.rs index de7745d..f401d7e 100644 --- a/engine/world_generation/src/map/map_utils.rs +++ b/engine/world_generation/src/map/map_utils.rs @@ -1,3 +1,5 @@ +use std::ops::Add; + use bevy::{math::VectorSpace, prelude::*}; use image::ImageBuffer; use rayon::prelude::*; @@ -105,10 +107,7 @@ fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f } pub fn render_biome_noise_map(map: &BiomeMap) -> ImageBuffer, Vec> { - let mut image = ImageBuffer::new( - map.width as u32 * Chunk::SIZE as u32, - map.height as u32 * Chunk::SIZE as u32, - ); + let mut image = ImageBuffer::new(map.width as u32, map.height as u32); update_biome_noise_map(map, &mut image); return image; } @@ -139,15 +138,18 @@ pub fn update_biome_map(map: &Map, biome_map: &BiomeMap, image: &mut ImageBuffer let map_biome_count = map.biome_count as f32; image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { let coord = HexCoord::from_grid_pos(x as usize, y as usize); - let biome_blend = biome_map.get_biome(x as i32, y as i32); + let biome_blend = biome_map.get_biome(x as i32, y as i32).unwrap(); let right = coord.get_neighbor(1); - let biome_id = map.get_biome_id(&coord) as f32; - let hue = (biome_id / map_biome_count) * 360.0; - let mut color = Hsla::hsl(hue, 0.8, 0.9); + let mut color = Oklaba::BLACK; + for i in 0..biome_blend.len() { + let mut c: Oklaba = Hsla::hsl((i as f32 / map_biome_count) * 360.0, 0.8, 0.7).into(); + c *= biome_blend[i]; + color = Oklaba::add(c, color.into()).into(); + } if map.is_in_bounds(&right) { let h1 = map.sample_height(&coord); let h2 = map.sample_height(&right); - color = get_height_color_blend(color, h1, h2, 0.5); + color = get_height_color_blend(color.into(), h1, h2, 0.5).into(); } *pixel = to_pixel(&color.into()); diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 74a8119..65ad04d 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -165,7 +165,7 @@ fn create_heightmap( biome_blend: 16, biome_dither: 16., continent_noise: NoiseConfig { - scale: 500., + scale: 1000., layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, @@ -195,7 +195,7 @@ fn create_heightmap( }], }, temperature_noise: NoiseConfig { - scale: 500., + scale: 900., layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, diff --git a/game/main/src/utlis/editor_plugin.rs b/game/main/src/utlis/editor_plugin.rs index 2460c55..b5b7a21 100644 --- a/game/main/src/utlis/editor_plugin.rs +++ b/game/main/src/utlis/editor_plugin.rs @@ -1,6 +1,9 @@ use bevy::{prelude::*, render::render_asset::RenderAssetUsages}; use bevy_inspector_egui::bevy_egui::EguiContexts; use bevy_inspector_egui::egui::{self}; +use bevy_rapier3d::rapier::crossbeam::deque::Steal; +use world_generation::map::biome_map::{self, BiomeMap}; +use world_generation::map::map_utils::{render_biome_map, render_biome_noise_map, update_map}; use world_generation::{map::map_utils::render_map, prelude::Map, states::GeneratorState}; pub struct EditorPlugin; @@ -10,7 +13,10 @@ impl Plugin for EditorPlugin { app.init_resource::(); app.add_systems(PostUpdate, prepare_image.run_if(in_state(GeneratorState::SpawnMap))); - app.add_systems(Update, (render_map_ui).run_if(in_state(GeneratorState::Idle))); + app.add_systems( + Update, + (render_map_ui, update_map_render).run_if(in_state(GeneratorState::Idle)), + ); } } @@ -27,23 +33,72 @@ pub fn prepare_image(mut images: ResMut>, heightmap: Res, mut #[derive(Resource)] struct UIState { pub is_open: bool, + pub target_map_type: MapDisplayType, + pub cur_map_type: MapDisplayType, } impl Default for UIState { fn default() -> Self { - Self { is_open: true } + Self { + is_open: true, + target_map_type: default(), + cur_map_type: default(), + } } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +enum MapDisplayType { + #[default] + HeightMap, + Biomes, + BiomeNoise, +} + fn render_map_ui(image: Res, mut contexts: EguiContexts, mut state: ResMut) { let id = contexts.add_image(image.0.clone_weak()); + let mut map_type = state.target_map_type; let ctx = contexts.ctx_mut(); egui::Window::new("Map").open(&mut state.is_open).show(ctx, |ui| { ui.label("Map Test"); + egui::ComboBox::from_label("Display Type") + .selected_text(format!("{:?}", map_type)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut map_type, MapDisplayType::HeightMap, "Heightmap"); + ui.selectable_value(&mut map_type, MapDisplayType::Biomes, "Biomes"); + ui.selectable_value(&mut map_type, MapDisplayType::BiomeNoise, "Biome Noise"); + }); + ui.add(egui::widgets::Image::new(egui::load::SizedTexture::new( id, [512.0, 512.0], ))); }); + + state.target_map_type = map_type; +} + +fn update_map_render( + mut state: ResMut, + mut images: ResMut>, + heightmap: Res, + biome_map: Res, + image: Res, +) { + if state.cur_map_type == state.target_map_type { + return; + } + + let result = match state.target_map_type { + MapDisplayType::HeightMap => render_map(&heightmap, 0.1), + MapDisplayType::Biomes => render_biome_map(&heightmap, &biome_map), + MapDisplayType::BiomeNoise => render_biome_noise_map(&biome_map), + }; + images.insert( + image.0.id(), + Image::from_dynamic(result.into(), true, RenderAssetUsages::RENDER_WORLD), + ); + + state.cur_map_type = state.target_map_type; } From f98173d00ae7311dbe745776d59a2b7bc35e6cae Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 2 Sep 2024 18:13:27 -0400 Subject: [PATCH 15/24] Update Assets --- game/main/assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/main/assets b/game/main/assets index 6a48d91..f8f4375 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit 6a48d910aa3826fac04af0b481e741d3b8e2e911 +Subproject commit f8f4375919336356fc1efffe6e6e429cb622a3e9 From 9613e3ae0d7d0b58d1b7526194fd1559ea359239 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 2 Sep 2024 19:53:13 -0400 Subject: [PATCH 16/24] misc fixes --- engine/world_generation/src/heightmap.rs | 35 +++++++++++----- engine/world_generation/src/map/map_utils.rs | 12 +++--- game/main/src/map_rendering/map_init.rs | 8 ++-- game/main/src/utlis/editor_plugin.rs | 43 +++++++++++++++++--- 4 files changed, 71 insertions(+), 27 deletions(-) diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index 4160dce..f2f198d 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -3,12 +3,12 @@ use core::f32; use bevy::math::{IVec2, UVec2}; use bevy::prelude::{FloatExt, Vec2}; use bevy::utils::default; -use noise::{NoiseFn, SuperSimplex}; +use noise::{NoiseFn, Simplex, SuperSimplex}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::biome_painter::BiomePainter; use crate::map::biome_map::{BiomeChunk, BiomeData, BiomeMap}; -use crate::prelude::*; +use crate::{map, prelude::*}; pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap) { let biomes = generate_biomes(cfg, seed, painter); @@ -74,9 +74,9 @@ pub fn generate_biome_chunk( 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); + let noise_m = Simplex::new(seed + 1); + let noise_t = Simplex::new(seed + 2); + let noise_c = Simplex::new(seed + 3); for z in 0..Chunk::SIZE { for x in 0..Chunk::SIZE { @@ -86,7 +86,8 @@ pub fn generate_biome_chunk( &cfg.moisture_noise, &noise_m, cfg.size.as_vec2(), - 0.0, + cfg.border_size, + 100.0, ); let temperature = sample_point( x as f64 + chunk_x as f64 * Chunk::SIZE as f64, @@ -94,7 +95,8 @@ pub fn generate_biome_chunk( &cfg.temperature_noise, &noise_t, cfg.size.as_vec2(), - 0.0, + cfg.border_size, + 50.0, ); let continentality = sample_point( x as f64 + chunk_x as f64 * Chunk::SIZE as f64, @@ -102,6 +104,7 @@ pub fn generate_biome_chunk( &cfg.continent_noise, &noise_c, cfg.size.as_vec2(), + cfg.border_size, 0.0, ); let data = BiomeData { @@ -135,6 +138,7 @@ pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size &noise, size.as_vec2(), border_size, + 0.0, )); } return row; @@ -154,7 +158,7 @@ pub fn generate_chunk( let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::AREA]; let mut data = [BiomeData::default(); Chunk::AREA]; let mut biome_ids = [0; Chunk::AREA]; - let noise = SuperSimplex::new(seed); + let noise = Simplex::new(seed); let mut min = f32::MAX; let mut max = f32::MIN; for z in 0..Chunk::SIZE { @@ -175,6 +179,7 @@ pub fn generate_chunk( &noise, cfg.size.as_vec2(), cfg.border_size, + 0.0, ) * blend; } let idx = x + z * Chunk::SIZE; @@ -199,7 +204,15 @@ pub fn generate_chunk( }; } -fn sample_point(x: f64, z: f64, cfg: &NoiseConfig, noise: &impl NoiseFn, size: Vec2, border_size: f32) -> f32 { +fn sample_point( + x: f64, + z: f64, + cfg: &NoiseConfig, + noise: &impl NoiseFn, + size: Vec2, + border_size: f32, + border_value: f32, +) -> f32 { let x_s = x / cfg.scale; let z_s = z / cfg.scale; @@ -221,7 +234,7 @@ fn sample_point(x: f64, z: f64, cfg: &NoiseConfig, noise: &impl NoiseFn, } else { elevation += value; } - } + } if border_size == 0.0 { return elevation as f32; @@ -235,7 +248,7 @@ fn sample_point(x: f64, z: f64, cfg: &NoiseConfig, noise: &impl NoiseFn, let d2 = od.x.min(od.y); let d = d1.min(d2).min(border_size).remap(0., border_size, 0., 1.); - return (elevation as f32) * d; + return border_value.lerp(elevation as f32, d); } fn mask(mask: f64, value: f64) -> f64 { diff --git a/engine/world_generation/src/map/map_utils.rs b/engine/world_generation/src/map/map_utils.rs index f401d7e..d98d184 100644 --- a/engine/world_generation/src/map/map_utils.rs +++ b/engine/world_generation/src/map/map_utils.rs @@ -106,20 +106,20 @@ fn get_height_color_blend(base_color: Hsla, height: f32, height2: f32, smooth: f return color; } -pub fn render_biome_noise_map(map: &BiomeMap) -> ImageBuffer, Vec> { +pub fn render_biome_noise_map(map: &BiomeMap, multi: Vec3) -> ImageBuffer, Vec> { let mut image = ImageBuffer::new(map.width as u32, map.height as u32); - update_biome_noise_map(map, &mut image); + update_biome_noise_map(map, multi, &mut image); return image; } -pub fn update_biome_noise_map(map: &BiomeMap, image: &mut ImageBuffer, Vec>) { +pub fn update_biome_noise_map(map: &BiomeMap, multi: Vec3, image: &mut ImageBuffer, Vec>) { image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| { let tile = map.get_biome_data(x as usize, y as usize); let color = LinearRgba::rgb( - tile.temperature / 100.0, - tile.continentality / 100.0, - tile.moisture / 100.0, + (tile.temperature / 100.0) * multi.x, + (tile.continentality / 100.0) * multi.y, + (tile.moisture / 100.0) * multi.z, ); *pixel = to_pixel(&color); }); diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 65ad04d..7698342 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -162,10 +162,10 @@ fn create_heightmap( biome_painter: Res, ) { let config = GenerationConfig { - biome_blend: 16, + biome_blend: 32, biome_dither: 16., continent_noise: NoiseConfig { - scale: 1000., + scale: 800., layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, @@ -180,7 +180,7 @@ fn create_heightmap( }], }, moisture_noise: NoiseConfig { - scale: 500., + scale: 900., layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, @@ -195,7 +195,7 @@ fn create_heightmap( }], }, temperature_noise: NoiseConfig { - scale: 900., + scale: 700., layers: vec![GeneratorLayer { base_roughness: 2.14, roughness: 0.87, diff --git a/game/main/src/utlis/editor_plugin.rs b/game/main/src/utlis/editor_plugin.rs index b5b7a21..1a5a242 100644 --- a/game/main/src/utlis/editor_plugin.rs +++ b/game/main/src/utlis/editor_plugin.rs @@ -2,6 +2,7 @@ use bevy::{prelude::*, render::render_asset::RenderAssetUsages}; use bevy_inspector_egui::bevy_egui::EguiContexts; use bevy_inspector_egui::egui::{self}; use bevy_rapier3d::rapier::crossbeam::deque::Steal; +use image::{ImageBuffer, Rgba}; use world_generation::map::biome_map::{self, BiomeMap}; use world_generation::map::map_utils::{render_biome_map, render_biome_noise_map, update_map}; use world_generation::{map::map_utils::render_map, prelude::Map, states::GeneratorState}; @@ -53,9 +54,12 @@ enum MapDisplayType { HeightMap, Biomes, BiomeNoise, + BiomeNoiseTemp, + BiomeNoiseContinent, + BiomeNoiseMoisture, } -fn render_map_ui(image: Res, mut contexts: EguiContexts, mut state: ResMut) { +fn render_map_ui(image: Res, heightmap: Res, biome_map: Res, mut contexts: EguiContexts, mut state: ResMut) { let id = contexts.add_image(image.0.clone_weak()); let mut map_type = state.target_map_type; @@ -68,12 +72,32 @@ fn render_map_ui(image: Res, mut contexts: EguiContexts, mut state: Re ui.selectable_value(&mut map_type, MapDisplayType::HeightMap, "Heightmap"); ui.selectable_value(&mut map_type, MapDisplayType::Biomes, "Biomes"); ui.selectable_value(&mut map_type, MapDisplayType::BiomeNoise, "Biome Noise"); + ui.selectable_value( + &mut map_type, + MapDisplayType::BiomeNoiseTemp, + "Biome Noise: Tempurature", + ); + ui.selectable_value( + &mut map_type, + MapDisplayType::BiomeNoiseContinent, + "Biome Noise: Continent", + ); + ui.selectable_value( + &mut map_type, + MapDisplayType::BiomeNoiseMoisture, + "Biome Noise: Moisture", + ); }); ui.add(egui::widgets::Image::new(egui::load::SizedTexture::new( id, [512.0, 512.0], ))); + + if ui.button("Save Image").clicked() { + let img = get_map_image(&heightmap, &biome_map, map_type); + _ = img.save(format!("{:?}.png", map_type)); + } }); state.target_map_type = map_type; @@ -90,11 +114,7 @@ fn update_map_render( return; } - let result = match state.target_map_type { - MapDisplayType::HeightMap => render_map(&heightmap, 0.1), - MapDisplayType::Biomes => render_biome_map(&heightmap, &biome_map), - MapDisplayType::BiomeNoise => render_biome_noise_map(&biome_map), - }; + let result = get_map_image(&heightmap, &biome_map, state.target_map_type); images.insert( image.0.id(), Image::from_dynamic(result.into(), true, RenderAssetUsages::RENDER_WORLD), @@ -102,3 +122,14 @@ fn update_map_render( state.cur_map_type = state.target_map_type; } + +fn get_map_image(heightmap: &Map, biome_map: &BiomeMap, map_type: MapDisplayType) -> ImageBuffer, Vec> { + return match map_type { + MapDisplayType::HeightMap => render_map(&heightmap, 0.1), + MapDisplayType::Biomes => render_biome_map(&heightmap, &biome_map), + MapDisplayType::BiomeNoise => render_biome_noise_map(&biome_map, Vec3::ONE), + MapDisplayType::BiomeNoiseTemp => render_biome_noise_map(&biome_map, Vec3::X), + MapDisplayType::BiomeNoiseContinent => render_biome_noise_map(&biome_map, Vec3::Y), + MapDisplayType::BiomeNoiseMoisture => render_biome_noise_map(&biome_map, Vec3::Z), + }; +} From 2d1fb78ab87d9053d2092a0b6415b3c77438c51f Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 2 Sep 2024 20:53:11 -0400 Subject: [PATCH 17/24] collision fixes generator changes --- .../src/generators/chunk_colliders.rs | 2 +- engine/world_generation/src/heightmap.rs | 16 ++-------------- engine/world_generation/src/map/biome_map.rs | 19 +++++++++++++++---- engine/world_generation/src/map/config.rs | 1 - engine/world_generation/src/map/map.rs | 6 +++--- engine/world_generation/src/map/mesh_chunk.rs | 3 ++- game/main/Cargo.toml | 6 +++++- game/main/assets | 2 +- game/main/src/map_rendering/map_init.rs | 5 +---- game/main/src/utlis/render_distance_system.rs | 3 +++ 10 files changed, 33 insertions(+), 30 deletions(-) diff --git a/engine/world_generation/src/generators/chunk_colliders.rs b/engine/world_generation/src/generators/chunk_colliders.rs index 78a4baf..9beeb68 100644 --- a/engine/world_generation/src/generators/chunk_colliders.rs +++ b/engine/world_generation/src/generators/chunk_colliders.rs @@ -43,7 +43,7 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec, indices: &mut Vec<[u32 create_tile_wall_collider( idx, Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z), - (i + 1) % 6, + i, verts, indices, ); diff --git a/engine/world_generation/src/heightmap.rs b/engine/world_generation/src/heightmap.rs index f2f198d..62a74dc 100644 --- a/engine/world_generation/src/heightmap.rs +++ b/engine/world_generation/src/heightmap.rs @@ -217,7 +217,6 @@ fn sample_point( let z_s = z / cfg.scale; let mut elevation: f64 = 0.; - let mut first_layer: f64 = 0.; for i in 0..cfg.layers.len() { let value: f64; let layer = &cfg.layers[i]; @@ -226,15 +225,8 @@ fn sample_point( } else { value = sample_simple(x_s, z_s, layer, noise); } - if i == 0 { - first_layer = value; - } - if layer.first_layer_mask { - elevation += mask(first_layer, value); - } else { - elevation += value; - } - } + elevation += value; + } if border_size == 0.0 { return elevation as f32; @@ -251,10 +243,6 @@ fn sample_point( return border_value.lerp(elevation as f32, d); } -fn mask(mask: f64, value: f64) -> f64 { - return value * mask; -} - fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn) -> f64 { let mut freq: f64 = cfg.base_roughness; let mut amp: f64 = 1.; diff --git a/engine/world_generation/src/map/biome_map.rs b/engine/world_generation/src/map/biome_map.rs index 6672111..000a8f7 100644 --- a/engine/world_generation/src/map/biome_map.rs +++ b/engine/world_generation/src/map/biome_map.rs @@ -134,6 +134,15 @@ impl BiomeMap { return chunk.get_biome_id(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE)); } + pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn, scale: f64) -> usize { + let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize; + let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize; + + let chunk = &self.chunks[cx + cy * self.size.x as usize]; + + return chunk.get_biome_id_dithered(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE), noise, scale); + } + pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize; let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize; @@ -175,16 +184,18 @@ impl BiomeChunk { } pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn, scale: f64) -> usize { - let cur_id = self.get_biome_id(x, y); + let mut cur_id = self.get_biome_id(x, y); let b = self.get_biome(x, y); - let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32) * b[cur_id]; + let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32 - 0.5)/ 2.0; + let mut max = b[cur_id] + n; for i in 0..b.len() { let blend = b[i]; if blend == 0. { continue; } - if n < blend { - return i; + if blend > max { + max = blend + n; + cur_id = i; } } diff --git a/engine/world_generation/src/map/config.rs b/engine/world_generation/src/map/config.rs index 84c22b4..2bd3d2a 100644 --- a/engine/world_generation/src/map/config.rs +++ b/engine/world_generation/src/map/config.rs @@ -44,5 +44,4 @@ pub struct GeneratorLayer { pub weight: f64, pub weight_multi: f64, pub layers: usize, - pub first_layer_mask: bool, } diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index cf690f5..8130116 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -26,6 +26,7 @@ impl Map { let chunk = &self.chunks[chunk_index]; return MeshChunkData { + min_height: self.min_level, heights: chunk.heights.clone(), textures: chunk.textures.clone(), }; @@ -73,17 +74,16 @@ impl Map { return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE); } - pub fn get_biome_id(&self, pos: &HexCoord) -> usize { assert!( self.is_in_bounds(pos), "The provided coordinate is not within the map bounds" ); - + let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.biome_id[pos.to_chunk_local_index()]; } - /* + /* pub fn get_biome_noise(&self, pos: &HexCoord) -> &BiomeData { assert!( self.is_in_bounds(pos), diff --git a/engine/world_generation/src/map/mesh_chunk.rs b/engine/world_generation/src/map/mesh_chunk.rs index 3a09423..ac10e5e 100644 --- a/engine/world_generation/src/map/mesh_chunk.rs +++ b/engine/world_generation/src/map/mesh_chunk.rs @@ -5,11 +5,12 @@ use super::chunk::Chunk; pub struct MeshChunkData { pub heights: [f32; Chunk::AREA], pub textures: [[u32; 2]; Chunk::AREA], + pub min_height: f32, } impl MeshChunkData { pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] { - let mut data = [0.; 6]; + let mut data = [self.min_height; 6]; let n_tiles = coord.get_neighbors(); for i in 0..6 { let n = n_tiles[i]; diff --git a/game/main/Cargo.toml b/game/main/Cargo.toml index 943a7ab..15d5ae0 100644 --- a/game/main/Cargo.toml +++ b/game/main/Cargo.toml @@ -12,7 +12,11 @@ bevy-inspector-egui = "0.25.0" iyes_perf_ui = "0.3.0" noise = "0.8.2" world_generation = { path = "../../engine/world_generation" } -bevy_rapier3d = { version = "0.27.0", features = ["simd-stable", "parallel"] } +bevy_rapier3d = { version = "0.27.0", features = [ + "simd-stable", + "parallel", + "debug-render-3d", +] } rayon = "1.10.0" buildings = { path = "../buildings" } units = { path = "../units" } diff --git a/game/main/assets b/game/main/assets index f8f4375..5e5c821 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit f8f4375919336356fc1efffe6e6e429cb622a3e9 +Subproject commit 5e5c821eb152f2f035415a17734d252dba7aed05 diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 7698342..f698ebb 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -163,7 +163,7 @@ fn create_heightmap( ) { let config = GenerationConfig { biome_blend: 32, - biome_dither: 16., + biome_dither: 10., continent_noise: NoiseConfig { scale: 800., layers: vec![GeneratorLayer { @@ -176,7 +176,6 @@ fn create_heightmap( weight: 0., weight_multi: 0., layers: 1, - first_layer_mask: false, }], }, moisture_noise: NoiseConfig { @@ -191,7 +190,6 @@ fn create_heightmap( weight: 0., weight_multi: 0., layers: 1, - first_layer_mask: false, }], }, temperature_noise: NoiseConfig { @@ -206,7 +204,6 @@ fn create_heightmap( weight: 0., weight_multi: 0., layers: 1, - first_layer_mask: false, }], }, sea_level: 8.5, diff --git a/game/main/src/utlis/render_distance_system.rs b/game/main/src/utlis/render_distance_system.rs index 13f6286..efba6f0 100644 --- a/game/main/src/utlis/render_distance_system.rs +++ b/game/main/src/utlis/render_distance_system.rs @@ -9,6 +9,9 @@ impl Plugin for RenderDistancePlugin { app.register_type::(); app.add_systems(PostUpdate, render_distance_system) .insert_resource(RenderDistanceSettings::default()); + + #[cfg(debug_assertions)] + app.insert_resource(RenderDistanceSettings::new(f32::MAX)); } } From d58570f64649bf27272fde87beb0c6e3a34c417b Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Mon, 2 Sep 2024 23:44:35 -0400 Subject: [PATCH 18/24] misc --- .vscode/launch.json | 2 +- game/main/src/main.rs | 5 ++++ game/main/src/map_rendering/map_init.rs | 4 +-- game/main/src/utlis/editor_plugin.rs | 36 +++++++++++++++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2c57237..53fd94b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ "name": "Debug", "program": "${workspaceRoot}/target/debug/phos.exe", "args": [], - "cwd": "${workspaceRoot}/target/debug", + "cwd": "${workspaceRoot}/game/main", "preLaunchTask": "Build", // "environment": [ // { diff --git a/game/main/src/main.rs b/game/main/src/main.rs index 09a015b..ba158be 100644 --- a/game/main/src/main.rs +++ b/game/main/src/main.rs @@ -36,6 +36,11 @@ fn main() { mag_filter: ImageFilterMode::Nearest, ..default() }, + }) + .set(AssetPlugin { + #[cfg(not(debug_assertions))] + watch_for_changes_override: Some(true), + ..Default::default() }), WorldInspectorPlugin::new(), WireframePlugin, diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index f698ebb..dae29fc 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -128,8 +128,8 @@ fn finalize_biome_painter( biome_painter: Res, biomes: Res>, ) { - let biome_painter = biome_painter.build(&biomes); - commands.insert_resource(biome_painter); + let painter = biome_painter.build(&biomes); + commands.insert_resource(painter); next_generator_state.set(GeneratorState::GenerateHeightmap); } diff --git a/game/main/src/utlis/editor_plugin.rs b/game/main/src/utlis/editor_plugin.rs index 1a5a242..b6dc417 100644 --- a/game/main/src/utlis/editor_plugin.rs +++ b/game/main/src/utlis/editor_plugin.rs @@ -1,10 +1,11 @@ use bevy::{prelude::*, render::render_asset::RenderAssetUsages}; use bevy_inspector_egui::bevy_egui::EguiContexts; use bevy_inspector_egui::egui::{self}; -use bevy_rapier3d::rapier::crossbeam::deque::Steal; use image::{ImageBuffer, Rgba}; -use world_generation::map::biome_map::{self, BiomeMap}; -use world_generation::map::map_utils::{render_biome_map, render_biome_noise_map, update_map}; +use world_generation::biome_asset::BiomeAsset; +use world_generation::biome_painter::BiomePainterAsset; +use world_generation::map::biome_map::BiomeMap; +use world_generation::map::map_utils::{render_biome_map, render_biome_noise_map}; use world_generation::{map::map_utils::render_map, prelude::Map, states::GeneratorState}; pub struct EditorPlugin; @@ -16,7 +17,7 @@ impl Plugin for EditorPlugin { app.add_systems(PostUpdate, prepare_image.run_if(in_state(GeneratorState::SpawnMap))); app.add_systems( Update, - (render_map_ui, update_map_render).run_if(in_state(GeneratorState::Idle)), + (render_map_ui, update_map_render, asset_reloaded).run_if(in_state(GeneratorState::Idle)), ); } } @@ -59,7 +60,32 @@ enum MapDisplayType { BiomeNoiseMoisture, } -fn render_map_ui(image: Res, heightmap: Res, biome_map: Res, mut contexts: EguiContexts, mut state: ResMut) { +fn asset_reloaded( + mut asset_events: EventReader>, + mut biomes: ResMut>, + biome_painter: Res, + mut commands: Commands, +) { + let mut rebuild = false; + for event in asset_events.read() { + match event { + AssetEvent::Modified { id } => rebuild = true, + _ => todo!(), + } + } + if rebuild { + let painter = biome_painter.build(&biomes); + commands.insert_resource(painter); + } +} + +fn render_map_ui( + image: Res, + heightmap: Res, + biome_map: Res, + mut contexts: EguiContexts, + mut state: ResMut, +) { let id = contexts.add_image(image.0.clone_weak()); let mut map_type = state.target_map_type; From 0c81742f1132797072a9ef32c41c72027adee9f4 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 8 Sep 2024 01:42:13 -0400 Subject: [PATCH 19/24] improved water shader --- Cargo.lock | 131 +++++++++++++++++- game/main/Cargo.toml | 2 +- game/main/assets | 2 +- game/main/src/map_rendering/map_init.rs | 7 +- .../src/shader_extensions/water_material.rs | 6 +- game/main/src/utlis/editor_plugin.rs | 4 +- 6 files changed, 141 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4f008c..b0a287b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -509,6 +509,7 @@ dependencies = [ "futures-io", "futures-lite", "js-sys", + "notify-debouncer-full", "parking_lot", "ron", "serde", @@ -2101,6 +2102,27 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "file-id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox 0.1.3", + "windows-sys 0.59.0", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -2174,6 +2196,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures-core" version = "0.3.30" @@ -2286,7 +2317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb5e8d912059b33b463831c16b838d15c4772d584ce332e4a80f6dffdae2bc1" dependencies = [ "core-foundation", - "inotify", + "inotify 0.10.2", "io-kit-sys", "js-sys", "libc", @@ -2623,6 +2654,17 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + [[package]] name = "inotify" version = "0.10.2" @@ -2752,6 +2794,26 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "ktx2" version = "0.3.0" @@ -2844,6 +2906,17 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.2", +] + [[package]] name = "libudev-sys" version = "0.1.4" @@ -2996,6 +3069,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "naga" version = "0.20.0" @@ -3187,6 +3272,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify 0.9.6", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +dependencies = [ + "crossbeam-channel", + "file-id", + "log", + "notify", + "parking_lot", + "walkdir", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -3543,7 +3661,7 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", ] [[package]] @@ -5468,6 +5586,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.42.2" diff --git a/game/main/Cargo.toml b/game/main/Cargo.toml index 15d5ae0..a2937fc 100644 --- a/game/main/Cargo.toml +++ b/game/main/Cargo.toml @@ -7,7 +7,7 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bevy = "0.14.0" +bevy = { version = "0.14.0", features = ["file_watcher"] } bevy-inspector-egui = "0.25.0" iyes_perf_ui = "0.3.0" noise = "0.8.2" diff --git a/game/main/assets b/game/main/assets index 5e5c821..d9e7ec8 160000 --- a/game/main/assets +++ b/game/main/assets @@ -1 +1 @@ -Subproject commit 5e5c821eb152f2f035415a17734d252dba7aed05 +Subproject commit d9e7ec829774a4199a07faeb183662d8222c4f21 diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index dae29fc..3de9084 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -105,14 +105,15 @@ fn setup_materials( ) { let water_material = water_materials.add(ExtendedMaterial { base: StandardMaterial { - base_color: Color::srgba(0., 0.5, 1., 0.8), + base_color: Color::srgb(0., 0.878, 1.), alpha_mode: AlphaMode::Blend, ..Default::default() }, extension: WaterMaterial { settings: WaterSettings { - offset: 0.5, - scale: 100., + offset: -4.97, + scale: 1., + deep_color: LinearRgba::rgb(0.0, 0.04, 0.085).into(), ..Default::default() }, ..default() diff --git a/game/main/src/shader_extensions/water_material.rs b/game/main/src/shader_extensions/water_material.rs index 4bcc70e..10db5c0 100644 --- a/game/main/src/shader_extensions/water_material.rs +++ b/game/main/src/shader_extensions/water_material.rs @@ -15,7 +15,8 @@ pub struct WaterMaterial { pub struct WaterSettings { pub offset: f32, pub scale: f32, - pub deep_color: Vec3, + pub f_power: f32, + pub deep_color: LinearRgba, } impl Default for WaterSettings { @@ -23,7 +24,8 @@ impl Default for WaterSettings { Self { offset: 0.0, scale: 1.0, - deep_color: Vec3::ZERO, + f_power: 2.0, + deep_color: default(), } } } diff --git a/game/main/src/utlis/editor_plugin.rs b/game/main/src/utlis/editor_plugin.rs index b6dc417..3b04561 100644 --- a/game/main/src/utlis/editor_plugin.rs +++ b/game/main/src/utlis/editor_plugin.rs @@ -69,8 +69,8 @@ fn asset_reloaded( let mut rebuild = false; for event in asset_events.read() { match event { - AssetEvent::Modified { id } => rebuild = true, - _ => todo!(), + AssetEvent::Modified {..}=> rebuild = true, + _ => (), } } if rebuild { From 805fb3feb67700510f5a46086135eef381add20d Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Tue, 10 Sep 2024 10:00:01 -0400 Subject: [PATCH 20/24] release build fixes --- game/main/src/utlis/debug_plugin.rs | 2 -- game/main/src/utlis/render_distance_system.rs | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index 35be8ef..411a16c 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -4,7 +4,6 @@ use bevy_inspector_egui::egui; use bevy_rapier3d::prelude::*; use shared::states::GameplayState; use shared::tags::MainCamera; -use units::units_debug_plugin::UnitsDebugPlugin; use world_generation::{ consts::HEX_CORNERS, hex_utils::{HexCoord, INNER_RADIUS}, @@ -16,7 +15,6 @@ pub struct DebugPlugin; impl Plugin for DebugPlugin { fn build(&self, app: &mut App) { - app.add_plugins(UnitsDebugPlugin); app.insert_state(DebugState::Base); app.add_systems( diff --git a/game/main/src/utlis/render_distance_system.rs b/game/main/src/utlis/render_distance_system.rs index efba6f0..848e32d 100644 --- a/game/main/src/utlis/render_distance_system.rs +++ b/game/main/src/utlis/render_distance_system.rs @@ -59,8 +59,9 @@ fn render_distance_system( settings: Res, ) { let camera = camera_query.single(); + let cam_pos = Vec3::new(camera.translation.x, 0.0, camera.translation.z); for (t, mut vis, r) in objects.iter_mut() { - let dist = (camera.translation - (t.translation + r.offset)).length(); + let dist = (cam_pos - (t.translation + r.offset)).length(); if settings.render_distance < dist { *vis = Visibility::Hidden; } else { From b156a33a54b1b9d11aecb520dd84b696ffa40bfd Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Tue, 10 Sep 2024 20:41:33 -0400 Subject: [PATCH 21/24] units testing --- game/buildings/src/assets/building_asset.rs | 2 +- game/buildings/src/building_plugin.rs | 33 ++------- game/main/src/map_rendering/map_init.rs | 1 + .../src/map_rendering/terraforming_test.rs | 32 ++------- game/main/src/phos.rs | 36 ++++++++++ game/main/src/utlis/debug_plugin.rs | 67 ++++++------------- game/main/src/utlis/mod.rs | 1 + game/main/src/utlis/tile_selection_plugin.rs | 62 +++++++++++++++++ .../src/{resource.rs => identifiers.rs} | 2 + game/shared/src/lib.rs | 3 +- game/shared/src/resources.rs | 22 ++++++ game/units/src/assets/unit_asset.rs | 11 ++- game/units/src/components.rs | 14 +++- game/units/src/units_debug_plugin.rs | 64 +++++++++++++++--- game/units/src/units_plugin.rs | 35 ++++++---- 15 files changed, 254 insertions(+), 131 deletions(-) create mode 100644 game/main/src/utlis/tile_selection_plugin.rs rename game/shared/src/{resource.rs => identifiers.rs} (66%) create mode 100644 game/shared/src/resources.rs diff --git a/game/buildings/src/assets/building_asset.rs b/game/buildings/src/assets/building_asset.rs index 3911809..5aea700 100644 --- a/game/buildings/src/assets/building_asset.rs +++ b/game/buildings/src/assets/building_asset.rs @@ -1,7 +1,7 @@ use asset_loader::create_asset_loader; use bevy::prelude::*; use serde::{Deserialize, Serialize}; -use shared::resource::ResourceIdentifier; +use shared::identifiers::ResourceIdentifier; use crate::footprint::BuildingFootprint; diff --git a/game/buildings/src/building_plugin.rs b/game/buildings/src/building_plugin.rs index 2115002..5d63463 100644 --- a/game/buildings/src/building_plugin.rs +++ b/game/buildings/src/building_plugin.rs @@ -9,6 +9,7 @@ use bevy_rapier3d::{parry::transformation::utils::transform, pipeline::QueryFilt use shared::{ despawn::Despawn, events::TileModifiedEvent, + resources::TileUnderCursor, states::{AssetLoadState, GameplayState}, tags::MainCamera, }; @@ -78,47 +79,23 @@ fn init(mut commands: Commands, mut meshes: ResMut>, mut materials: } fn hq_placement( - cam_query: Query<(&GlobalTransform, &Camera), With>, mut commands: Commands, - window: Query<&Window, With>, mouse: Res>, - rapier_context: Res, + tile_under_cursor: Res, map: Res, indicator: Res, mut build_queue: ResMut, mut next_state: ResMut>, ) { - let win = window.single(); - let (cam_transform, camera) = cam_query.single(); - let Some(cursor_pos) = win.cursor_position() else { - return; - }; - let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else { - return; - }; - - let collision = rapier_context.cast_ray( - cam_ray.origin, - cam_ray.direction.into(), - 500., - true, - QueryFilter::only_fixed(), - ); - - if let Some((_e, dist)) = collision { - let contact_point = cam_ray.get_point(dist); - let contact_coord = HexCoord::from_world_pos(contact_point); - if !map.is_in_bounds(&contact_coord) { - return; - } - let positions = map.hex_select(&contact_coord, 3, true, |pos, h, _| pos.to_world(h)); + if let Some(contact) = tile_under_cursor.0 { + let positions = map.hex_select(&contact.tile, 3, true, |pos, h, _| pos.to_world(h)); show_indicators(positions, &mut commands, &indicator); if mouse.just_pressed(MouseButton::Left) { build_queue.queue.push(QueueEntry { building: 0.into(), - pos: contact_coord, + pos: contact.tile, }); next_state.set(GameplayState::Playing); diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 3de9084..213eda3 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -107,6 +107,7 @@ fn setup_materials( base: StandardMaterial { base_color: Color::srgb(0., 0.878, 1.), alpha_mode: AlphaMode::Blend, + metallic: 1.0, ..Default::default() }, extension: WaterMaterial { diff --git a/game/main/src/map_rendering/terraforming_test.rs b/game/main/src/map_rendering/terraforming_test.rs index 53edbcd..909fdea 100644 --- a/game/main/src/map_rendering/terraforming_test.rs +++ b/game/main/src/map_rendering/terraforming_test.rs @@ -2,6 +2,7 @@ use bevy::{prelude::*, utils::hashbrown::HashSet, window::PrimaryWindow}; use bevy_rapier3d::{pipeline::QueryFilter, plugin::RapierContext}; use shared::{ events::{ChunkModifiedEvent, TileModifiedEvent}, + resources::TileUnderCursor, states::GameplayState, }; use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState}; @@ -25,13 +26,11 @@ impl Plugin for TerraFormingTestPlugin { } fn deform( - cam_query: Query<(&GlobalTransform, &Camera), With>, mut commands: Commands, - window: Query<&Window, With>, mouse: Res>, - rapier_context: Res, mut heightmap: ResMut, chunks: Res, + tile_under_cursor: Res, mut chunk_modified: EventWriter, mut tile_modified: EventWriter, ) { @@ -46,33 +45,10 @@ fn deform( return; } - let win = window.single(); - let (cam_transform, camera) = cam_query.single(); - let Some(cursor_pos) = win.cursor_position() else { - return; - }; - - let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else { - return; - }; - - let collision = rapier_context.cast_ray( - cam_ray.origin, - cam_ray.direction.into(), - 500., - true, - QueryFilter::only_fixed(), - ); - - if let Some((_, dist)) = collision { + if let Some(contact) = tile_under_cursor.0 { #[cfg(feature = "tracing")] let span = info_span!("Deform Mesh").entered(); - let contact_point = cam_ray.get_point(dist); - let contact_coord = HexCoord::from_world_pos(contact_point); - if !heightmap.is_in_bounds(&contact_coord) { - return; - } - let modified_tiles = heightmap.create_crater(&contact_coord, 5, 5. * multi); + let modified_tiles = heightmap.create_crater(&contact.tile, 5, 5. * multi); let mut chunk_set: HashSet = HashSet::new(); for (tile, height) in modified_tiles { let chunk = tile.to_chunk_index(heightmap.width); diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index 7ed8296..fbbc7eb 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -2,6 +2,7 @@ use crate::camera_system::components::PhosCamera; use crate::map_rendering::map_init::MapInitPlugin; use crate::utlis::editor_plugin::EditorPlugin; use crate::utlis::render_distance_system::RenderDistancePlugin; +use crate::utlis::tile_selection_plugin::TileSelectionPlugin; use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin}; use bevy::{ pbr::{wireframe::WireframeConfig, CascadeShadowConfig}, @@ -13,8 +14,11 @@ use bevy_rapier3d::geometry::Collider; use bevy_rapier3d::plugin::{NoUserData, RapierPhysicsPlugin}; use buildings::BuildingPugin; use iyes_perf_ui::prelude::*; +use shared::sets::GameplaySet; use shared::states::{GameplayState, MenuState}; use shared::{despawn::DespawnPuglin, states::AssetLoadState}; +use units::units_plugin::UnitsPlugin; +use world_generation::states::GeneratorState; pub struct PhosGamePlugin; @@ -33,13 +37,17 @@ impl Plugin for PhosGamePlugin { MapInitPlugin, RenderDistancePlugin, BuildingPugin, + UnitsPlugin, DespawnPuglin, + TileSelectionPlugin, #[cfg(debug_assertions)] EditorPlugin, #[cfg(debug_assertions)] DebugPlugin, )); + configure_gameplay_set(app); + //Systems - Startup app.add_systems(Startup, init_game); @@ -63,6 +71,34 @@ impl Plugin for PhosGamePlugin { } } +fn configure_gameplay_set(app: &mut App) { + app.configure_sets( + Update, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); + app.configure_sets( + PreUpdate, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); + app.configure_sets( + PostUpdate, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); + + app.configure_sets( + FixedUpdate, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); + app.configure_sets( + FixedPreUpdate, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); + app.configure_sets( + FixedPostUpdate, + GameplaySet.run_if(in_state(GeneratorState::Idle).and_then(in_state(MenuState::InGame))), + ); +} + fn init_game(mut commands: Commands, mut materials: ResMut>) { commands.spawn(( PerfUiRoot::default(), diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index 411a16c..2ae5a24 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -2,6 +2,7 @@ use bevy::{prelude::*, window::PrimaryWindow}; use bevy_inspector_egui::bevy_egui::{systems::InputEvents, EguiContexts}; use bevy_inspector_egui::egui; use bevy_rapier3d::prelude::*; +use shared::resources::TileUnderCursor; use shared::states::GameplayState; use shared::tags::MainCamera; use world_generation::{ @@ -66,62 +67,32 @@ fn regenerate_map( } } -fn show_tile_heights( - cam_query: Query<(&GlobalTransform, &Camera), With>, - window: Query<&Window, With>, - rapier_context: Res, - map: Res, - mut gizmos: Gizmos, - shape: Res, -) { - let win = window.single(); - let (cam_transform, camera) = cam_query.single(); - let Some(cursor_pos) = win.cursor_position() else { - return; - }; - - let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else { - return; - }; - - let collision = rapier_context.cast_ray( - cam_ray.origin, - cam_ray.direction.into(), - 500., - true, - QueryFilter::only_fixed(), - ); - - if let Some((_e, dist)) = collision { - let contact_point = cam_ray.get_point(dist); - let contact_coord = HexCoord::from_world_pos(contact_point); - if !map.is_in_bounds(&contact_coord) { - return; - } - let height = map.sample_height(&contact_coord); +fn show_tile_heights(map: Res, mut gizmos: Gizmos, shape: Res, tile_under_cursor: Res) { + if let Some(contact) = tile_under_cursor.0 { + let height = map.sample_height(&contact.tile); gizmos.primitive_3d( &shape.0, - contact_coord.to_world(height + 0.01), + contact.tile.to_world(height + 0.01), Quat::IDENTITY, Color::WHITE, ); - let nbors = map.get_neighbors(&contact_coord); - let contact_tile_pos = contact_coord.to_world(map.sample_height(&contact_coord)); + let nbors = map.get_neighbors(&contact.tile); + let contact_tile_pos = contact.tile.to_world(map.sample_height(&contact.tile)); - for i in 0..6 { - if let Some(s) = nbors[i] { - let coord = contact_coord.get_neighbor(i); - let p = coord.to_world(s); - gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), Color::WHITE); - } + // for i in 0..6 { + // if let Some(s) = nbors[i] { + // let coord = contact.tile.get_neighbor(i); + // let p = coord.to_world(s); + // gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), Color::WHITE); + // } - let p = HEX_CORNERS[i] + contact_tile_pos; - gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), LinearRgba::rgb(1.0, 0.0, 0.5)); - } + // let p = HEX_CORNERS[i] + contact_tile_pos; + // gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), LinearRgba::rgb(1.0, 0.0, 0.5)); + // } - gizmos.line(contact_point, contact_point + Vec3::X, LinearRgba::RED); - gizmos.line(contact_point, contact_point + Vec3::Y, LinearRgba::GREEN); - gizmos.line(contact_point, contact_point + Vec3::Z, LinearRgba::BLUE); + gizmos.line(contact.point, contact.point + Vec3::X, LinearRgba::RED); + gizmos.line(contact.point, contact.point + Vec3::Y, LinearRgba::GREEN); + gizmos.line(contact.point, contact.point + Vec3::Z, LinearRgba::BLUE); //gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, LinearRgba::rgb(1., 0., 0.5)); } } diff --git a/game/main/src/utlis/mod.rs b/game/main/src/utlis/mod.rs index c307916..86dcd04 100644 --- a/game/main/src/utlis/mod.rs +++ b/game/main/src/utlis/mod.rs @@ -2,3 +2,4 @@ pub mod chunk_utils; pub mod render_distance_system; pub mod debug_plugin; pub mod editor_plugin; +pub mod tile_selection_plugin; diff --git a/game/main/src/utlis/tile_selection_plugin.rs b/game/main/src/utlis/tile_selection_plugin.rs new file mode 100644 index 0000000..08216d9 --- /dev/null +++ b/game/main/src/utlis/tile_selection_plugin.rs @@ -0,0 +1,62 @@ +use bevy::{prelude::*, window::PrimaryWindow}; +use bevy_rapier3d::{plugin::RapierContext, prelude::QueryFilter}; +use shared::{ + resources::{TileContact, TileUnderCursor}, + tags::MainCamera, +}; +use world_generation::{hex_utils::HexCoord, prelude::Map, states::GeneratorState}; +pub struct TileSelectionPlugin; + +impl Plugin for TileSelectionPlugin { + fn build(&self, app: &mut App) { + app.init_resource::(); + app.add_systems( + PreUpdate, + update_tile_under_cursor.run_if(in_state(GeneratorState::Idle)), + ); + } +} + +fn update_tile_under_cursor( + cam_query: Query<(&GlobalTransform, &Camera), With>, + window: Query<&Window, With>, + rapier_context: Res, + map: Res, + mut tile_under_cursor: ResMut, +) { + let win = window.single(); + let (cam_transform, camera) = cam_query.single(); + let Some(cursor_pos) = win.cursor_position() else { + return; + }; + + let Some(cam_ray) = camera.viewport_to_world(cam_transform, cursor_pos) else { + return; + }; + + let collision = rapier_context.cast_ray( + cam_ray.origin, + cam_ray.direction.into(), + 500., + true, + QueryFilter::only_fixed(), + ); + + if let Some((_e, dist)) = collision { + let contact_point = cam_ray.get_point(dist); + let contact_coord = HexCoord::from_world_pos(contact_point); + //todo: handle correct tile detection when contacting a tile from the side + if !map.is_in_bounds(&contact_coord) { + tile_under_cursor.0 = None; + return; + } + let surface = map.sample_height(&contact_coord); + tile_under_cursor.0 = Some(TileContact::new( + contact_coord, + contact_point, + contact_coord.to_world(surface), + )); + } else { + tile_under_cursor.0 = None; + } +} diff --git a/game/shared/src/resource.rs b/game/shared/src/identifiers.rs similarity index 66% rename from game/shared/src/resource.rs rename to game/shared/src/identifiers.rs index f9ad934..f1947d9 100644 --- a/game/shared/src/resource.rs +++ b/game/shared/src/identifiers.rs @@ -1,4 +1,6 @@ +use bevy::prelude::Resource; use serde::{Deserialize, Serialize}; +use world_generation::hex_utils::HexCoord; #[derive(Serialize, Deserialize, Debug)] pub struct ResourceIdentifier { diff --git a/game/shared/src/lib.rs b/game/shared/src/lib.rs index 0d6610b..2c2f7cf 100644 --- a/game/shared/src/lib.rs +++ b/game/shared/src/lib.rs @@ -1,7 +1,8 @@ pub mod building; pub mod despawn; -pub mod resource; +pub mod identifiers; pub mod states; pub mod tags; pub mod events; pub mod sets; +pub mod resources; diff --git a/game/shared/src/resources.rs b/game/shared/src/resources.rs new file mode 100644 index 0000000..7a53ef9 --- /dev/null +++ b/game/shared/src/resources.rs @@ -0,0 +1,22 @@ +use bevy::prelude::*; +use world_generation::hex_utils::HexCoord; + +#[derive(Resource, Default)] +pub struct TileUnderCursor(pub Option); + +#[derive(Clone, Copy)] +pub struct TileContact { + pub tile: HexCoord, + pub point: Vec3, + pub surface: Vec3, +} + +impl TileContact { + pub fn new(tile: HexCoord, contact: Vec3, surface: Vec3) -> Self { + return Self { + tile, + point: contact, + surface, + }; + } +} diff --git a/game/units/src/assets/unit_asset.rs b/game/units/src/assets/unit_asset.rs index 94c077d..e49d929 100644 --- a/game/units/src/assets/unit_asset.rs +++ b/game/units/src/assets/unit_asset.rs @@ -2,7 +2,7 @@ use asset_loader::create_asset_loader; use bevy::{ecs::world::CommandQueue, prelude::*}; use serde::{Deserialize, Serialize}; -use crate::components::{Unit, UnitDomain}; +use crate::components::{AirUnit, LandUnit, NavalUnit, Unit, UnitDomain}; #[derive(Asset, TypePath, Debug, Serialize, Deserialize)] pub struct UnitAsset { @@ -26,10 +26,15 @@ impl UnitAsset { ..default() }, Unit, - self.domain.clone(), ); + let domain = self.domain.clone(); commands.push(move |world: &mut World| { - world.spawn(bundle); + let mut e = world.spawn(bundle); + match domain { + UnitDomain::Land => e.insert(LandUnit), + UnitDomain::Air => e.insert(AirUnit), + UnitDomain::Naval => e.insert(NavalUnit), + }; }); todo!(); diff --git a/game/units/src/components.rs b/game/units/src/components.rs index ff9be19..e5276f3 100644 --- a/game/units/src/components.rs +++ b/game/units/src/components.rs @@ -4,9 +4,19 @@ use serde::{Deserialize, Serialize}; #[derive(Component, Debug)] pub struct Unit; -#[derive(Component, Serialize, Deserialize, Debug, Clone, Copy)] +#[derive(Component, Debug)] +pub struct AirUnit; +#[derive(Component, Debug)] +pub struct LandUnit; +#[derive(Component, Debug)] +pub struct NavalUnit; + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum UnitDomain { Land, Air, - Navy, + Naval, } + +#[derive(Component, Debug)] +pub struct Target(pub Vec3); diff --git a/game/units/src/units_debug_plugin.rs b/game/units/src/units_debug_plugin.rs index bca2d0c..e8ad741 100644 --- a/game/units/src/units_debug_plugin.rs +++ b/game/units/src/units_debug_plugin.rs @@ -1,18 +1,66 @@ +use std::f32::consts::E; + use bevy::prelude::*; -use shared::states::GameplayState; -use world_generation::states::GeneratorState; +use shared::{resources::TileUnderCursor, sets::GameplaySet, states::AssetLoadState}; +use world_generation::{heightmap, prelude::Map}; + +use crate::components::{LandUnit, Target, Unit}; pub struct UnitsDebugPlugin; impl Plugin for UnitsDebugPlugin { fn build(&self, app: &mut App) { - app.add_systems( - Update, - spawn_test_unit.run_if(in_state(GeneratorState::Idle).and_then(in_state(GameplayState::Playing))), - ); + app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading))); + + app.add_systems(Update, (spawn_test_unit, set_unit_target).in_set(GameplaySet)); } } -fn spawn_test_unit(mut commands: Commands, input: Res>) { - +#[derive(Resource)] +struct TestUnit(pub Handle); + +fn init(mut meshes: ResMut>, mut commands: Commands) { + let mesh_handle = meshes.add(Cuboid::from_length(1.0)); + commands.insert_resource(TestUnit(mesh_handle)); +} + +fn spawn_test_unit( + mut commands: Commands, + input: Res>, + tile_under_cursor: Res, + unit: Res, +) { + if !input.just_pressed(KeyCode::KeyT) { + return; + } + if let Some(contact) = tile_under_cursor.0 { + info!("Spawning Test Unit"); + commands.spawn(( + PbrBundle { + transform: Transform::from_translation(contact.surface), + mesh: unit.0.clone(), + ..default() + }, + Unit, + LandUnit, + )); + } +} + +fn set_unit_target( + mut commands: Commands, + units: Query>, + input: Res>, + tile_under_cursor: Res, +) { + if !input.just_pressed(MouseButton::Right) { + return; + } + if let Some(contact) = tile_under_cursor.0 { + for e in units.iter() { + info!("Setting Target"); + let mut e = commands.entity(e); + e.insert(Target(contact.surface)); + } + } } diff --git a/game/units/src/units_plugin.rs b/game/units/src/units_plugin.rs index a89e0df..eb1c078 100644 --- a/game/units/src/units_plugin.rs +++ b/game/units/src/units_plugin.rs @@ -3,10 +3,14 @@ use bevy_asset_loader::loading_state::{ config::{ConfigureLoadingState, LoadingStateConfig}, LoadingStateAppExt, }; -use shared::states::{AssetLoadState, GameplayState}; -use world_generation::states::GeneratorState; +use shared::{sets::GameplaySet, states::AssetLoadState}; +use world_generation::{hex_utils::HexCoord, prelude::Map}; -use crate::assets::{unit_asset::UnitAssetPlugin, unit_database::UnitDatabase}; +use crate::{ + assets::{unit_asset::UnitAssetPlugin, unit_database::UnitDatabase}, + components::{Target, Unit}, + units_debug_plugin::UnitsDebugPlugin, +}; pub struct UnitsPlugin; @@ -14,20 +18,16 @@ impl Plugin for UnitsPlugin { fn build(&self, app: &mut App) { app.add_plugins(UnitAssetPlugin); - app.configure_loading_state(LoadingStateConfig::new(AssetLoadState::Loading).load_collection::()); + #[cfg(debug_assertions)] + app.add_plugins(UnitsDebugPlugin); - app.add_systems(Update, units_control.in_set(UnitUpdateSet)); + // app.configure_loading_state(LoadingStateConfig::new(AssetLoadState::Loading).load_collection::()); - app.configure_sets( - Update, - UnitUpdateSet.run_if(in_state(GameplayState::Playing).and_then(in_state(GeneratorState::Idle))), - ); + app.add_systems(Update, units_control.in_set(GameplaySet)); + app.add_systems(Update, move_unit.in_set(GameplaySet)); } } -#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] -struct UnitUpdateSet; - fn units_control(input: Res>, window: Query<&Window, With>) { let win = window.single(); @@ -35,3 +35,14 @@ fn units_control(input: Res>, window: Query<&Window, With

>, time: Res