diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index e43eb8a..41c6be5 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -2,7 +2,10 @@ use crate::prelude::Chunk; use bevy::prelude::*; pub const OUTER_RADIUS: f32 = 1.; -pub const INNER_RADIUS: f32 = OUTER_RADIUS * 0.866025404; +pub const INNER_RADIUS: f32 = OUTER_RADIUS * (SQRT_3 / 2.); +pub const SHORT_DIAGONAL: f32 = 1. * SQRT_3; +pub const LONG_DIAGONAL: f32 = 2. * OUTER_RADIUS; +const SQRT_3: f32 = 1.7320508076; pub fn offset3d_to_world(offset: Vec3) -> Vec3 { let x = (offset.x + (offset.z * 0.5) - (offset.z / 2.).floor()) * (INNER_RADIUS * 2.); @@ -41,11 +44,11 @@ pub fn world_to_offset_pos(world_pos: Vec3) -> IVec2 { return IVec2::new(ox, oz); } -pub fn tile_to_world_distance(dist: i32) -> f32 { +pub fn tile_to_world_distance(dist: u32) -> f32 { return dist as f32 * (2. * INNER_RADIUS); } -pub fn get_tile_count(radius: i32) -> i32 { +pub fn get_tile_count(radius: u32) -> u32 { return 1 + 3 * (radius + 1) * radius; } diff --git a/engine/world_generation/src/lib.rs b/engine/world_generation/src/lib.rs index 925f8f0..892f95d 100644 --- a/engine/world_generation/src/lib.rs +++ b/engine/world_generation/src/lib.rs @@ -8,7 +8,7 @@ pub mod tile_manager; pub mod tile_mapper; pub mod prelude { - use crate::hex_utils::{tile_to_world_distance, HexCoord, INNER_RADIUS, OUTER_RADIUS}; + use crate::hex_utils::{tile_to_world_distance, HexCoord, INNER_RADIUS, OUTER_RADIUS, SHORT_DIAGONAL}; use bevy::math::{IVec2, UVec2, Vec2, Vec3}; use bevy::prelude::Resource; use bevy::prelude::*; @@ -77,6 +77,9 @@ pub mod prelude { impl Chunk { pub const SIZE: usize = 64; + pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL; + pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5; + pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT); } #[derive(Resource)] @@ -111,6 +114,10 @@ pub mod prelude { return chunk.heights[pos.to_chunk_local_index()]; } + pub fn is_in_bounds(&self, pos: &HexCoord) -> bool { + return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE); + } + pub fn get_moisture(&self, pos: &HexCoord) -> f32 { let chunk = &self.chunks[pos.to_chunk_index(self.width)]; return chunk.moisture[pos.to_chunk_local_index()]; @@ -122,20 +129,20 @@ pub mod prelude { } pub fn get_center(&self) -> Vec3 { - let w = self.width * Chunk::SIZE; - let h = self.height * Chunk::SIZE; - return Vec3::new( - tile_to_world_distance(w as i32 / 2), - self.sea_level, - tile_to_world_distance(h as i32 / 2), - ); + let w = self.get_world_width(); + let h = self.get_world_height(); + return Vec3::new(w / 2., self.sea_level, h / 2.); } pub fn get_world_width(&self) -> f32 { - return tile_to_world_distance((self.width * Chunk::SIZE) as i32); + return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL; } pub fn get_world_height(&self) -> f32 { - return tile_to_world_distance((self.height * Chunk::SIZE) as i32); + return (self.height * Chunk::SIZE) as f32 * 1.5; + } + + pub fn get_world_size(&self) -> Vec2 { + return Vec2::new(self.get_world_width(), self.get_world_height()); } } pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute = diff --git a/game/main/src/camera_system/camera_plugin.rs b/game/main/src/camera_system/camera_plugin.rs index be33adb..7c8ecb6 100644 --- a/game/main/src/camera_system/camera_plugin.rs +++ b/game/main/src/camera_system/camera_plugin.rs @@ -36,7 +36,6 @@ fn setup(mut commands: Commands, mut msaa: ResMut) { PhosCamera::default(), PhosCameraTargets::default(), )) - .insert(ScreenSpaceAmbientOcclusionBundle::default()) .insert(TemporalAntiAliasBundle::default()); *msaa = Msaa::Off; @@ -147,12 +146,13 @@ fn rts_camera_system( cam_move.z = -1.; } - if key.pressed(KeyCode::ShiftLeft) { - cam_move = cam_move.normalize_or_zero() * cam_cfg.speed * time.delta_seconds() * 2.; + let move_speed = if key.pressed(KeyCode::ShiftLeft) { + cam_cfg.speed * 2. } else { - cam_move = cam_move.normalize_or_zero() * cam_cfg.speed * time.delta_seconds(); - } + cam_cfg.speed + }; + cam_move = cam_move.normalize_or_zero() * move_speed * time.delta_seconds(); cam_pos -= cam_move; let mut scroll = 0.0; @@ -163,22 +163,13 @@ fn rts_camera_system( } } + let ground_height = sample_ground(cam.translation, &heightmap); + cam_targets.height -= scroll; if cam_targets.height > cam_cfg.max_height { cam_targets.height = cam_cfg.max_height; } - let tile_under = HexCoord::from_world_pos(cam.translation); - let neighbors = heightmap.get_neighbors(&tile_under); - let mut ground_height = heightmap.sample_height(&tile_under); - for n in neighbors { - if let Some(h) = n { - if h > ground_height { - ground_height = h; - } - } - } - let min_height = ground_height + cam_cfg.min_height; if min_height != cam_targets.last_height { @@ -218,4 +209,35 @@ fn rts_camera_system( cam.translation = cam_pos; } -fn limit_camera_bounds(mut cam_query: Query<(&mut Transform, &CameraBounds)>) {} +fn sample_ground(pos: Vec3, heightmap: &Map) -> f32 { + let tile_under = HexCoord::from_world_pos(pos); + let neighbors = heightmap.get_neighbors(&tile_under); + let mut ground_height = if heightmap.is_in_bounds(&tile_under) { + heightmap.sample_height(&tile_under) + } else { + heightmap.sea_level + }; + + for n in neighbors { + if let Some(h) = n { + if h > ground_height { + ground_height = h; + } + } + } + if ground_height < heightmap.sea_level { + ground_height = heightmap.sea_level; + } + return ground_height; +} + +fn limit_camera_bounds(mut cam_query: Query<(&mut Transform, &CameraBounds)>) { + let (mut tranform, bounds) = cam_query.single_mut(); + + let mut pos = tranform.translation; + + pos.x = pos.x.clamp(bounds.min.x, bounds.max.x); + pos.z = pos.z.clamp(bounds.min.y, bounds.max.y); + + tranform.translation = pos; +} diff --git a/game/main/src/camera_system/components.rs b/game/main/src/camera_system/components.rs index 792d7c5..f1f40ff 100644 --- a/game/main/src/camera_system/components.rs +++ b/game/main/src/camera_system/components.rs @@ -1,4 +1,8 @@ use bevy::prelude::*; +use world_generation::{ + hex_utils::{tile_to_world_distance, SHORT_DIAGONAL}, + prelude::Chunk, +}; #[derive(Component, Reflect)] #[reflect(Component)] @@ -15,8 +19,8 @@ impl Default for PhosCamera { fn default() -> Self { Self { min_height: 10., - max_height: 100., - speed: 20., + max_height: 120., + speed: 30., zoom_speed: 0.3, min_angle: (20. as f32).to_radians(), max_angle: 1., @@ -24,14 +28,27 @@ impl Default for PhosCamera { } } -#[derive(Component, Default)] +#[derive(Component)] pub struct PhosCameraTargets { pub height: f32, + pub forward: Vec3, pub last_height: f32, pub anim_time: f32, pub rotate_time: f32, } +impl Default for PhosCameraTargets { + fn default() -> Self { + Self { + height: Default::default(), + forward: Vec3::Z, + last_height: Default::default(), + anim_time: Default::default(), + rotate_time: Default::default(), + } + } +} + #[derive(Component, Default)] pub struct CameraBounds { pub min: Vec2, @@ -40,9 +57,13 @@ pub struct CameraBounds { impl CameraBounds { pub fn from_size(size: UVec2) -> Self { + let padding = Chunk::WORLD_SIZE; return Self { - min: Vec2::ZERO, - max: size.as_vec2(), + min: Vec2::ZERO - padding, + max: Vec2::new( + (size.x as usize * Chunk::SIZE) as f32 * SHORT_DIAGONAL, + (size.y * Chunk::SIZE as u32) as f32 * 1.5, + ) + padding, }; } } diff --git a/game/main/src/map_init.rs b/game/main/src/map_init.rs index 9f4787c..b01ca33 100644 --- a/game/main/src/map_init.rs +++ b/game/main/src/map_init.rs @@ -6,7 +6,7 @@ use world_generation::{ biome_painter::*, chunk_colliders::generate_chunk_collider, heightmap::generate_heightmap, - hex_utils::{offset_to_world, tile_to_world_distance}, + hex_utils::{offset_to_world, SHORT_DIAGONAL}, mesh_generator::generate_chunk_mesh, prelude::*, tile_manager::*, @@ -39,11 +39,23 @@ impl Plugin for MapInitPlugin { } } -fn load_textures(mut commands: Commands, asset_server: Res) { +fn load_textures( + mut commands: Commands, + asset_server: Res, + mut standard_materials: ResMut>, +) { let main_tex = asset_server.load("textures/world/stack.png"); + + let water_material = standard_materials.add(StandardMaterial { + base_color: Color::AQUAMARINE.with_a(0.5), + alpha_mode: AlphaMode::Blend, + ..default() + }); commands.insert_resource(ChunkAtlas { handle: main_tex.clone(), is_loaded: false, + chunk_material_handle: Handle::default(), + water_material: water_material, }); } #[derive(Resource)] @@ -62,6 +74,7 @@ fn finalize_texture( painter: Res, painter_load: Res, tile_load: Res, + mut chunk_materials: ResMut>>, mapper_load: Res, ) { if atlas.is_loaded { @@ -84,6 +97,13 @@ fn finalize_texture( image.reinterpret_stacked_2d_as_array(array_layers); atlas.is_loaded = true; + let chunk_material = chunk_materials.add(ExtendedMaterial { + base: StandardMaterial::default(), + extension: ChunkMaterial { + array_texture: atlas.handle.clone(), + }, + }); + atlas.chunk_material_handle = chunk_material; map.ready = true; map.regenerate = true; } @@ -160,25 +180,18 @@ fn create_map(mut commands: Commands, mut cam: Query<(&mut Transform, Entity), W }; let heightmap = generate_heightmap(&config, 4); - commands.insert_resource(heightmap); - let (mut cam_t, cam_entity) = cam.single_mut(); - cam_t.translation = Vec3::new( - tile_to_world_distance((config.size.x as i32 * Chunk::SIZE as i32) / 2), - cam_t.translation.y, - tile_to_world_distance((config.size.y as i32 * Chunk::SIZE as i32) / 2), - ); + cam_t.translation = heightmap.get_center(); commands.entity(cam_entity).insert(CameraBounds::from_size(config.size)); + commands.insert_resource(heightmap); commands.insert_resource(config); } fn spawn_map( heightmap: Res, mut commands: Commands, - mut chunk_materials: ResMut>>, - mut standard_materials: ResMut>, mut meshes: ResMut>, atlas: Res, mut map: ResMut, @@ -192,12 +205,6 @@ fn spawn_map( } let b_painter = biome_painters.get(painter.0.clone()); map.regenerate = false; - let chunk_material = chunk_materials.add(ExtendedMaterial { - base: StandardMaterial::default(), - extension: ChunkMaterial { - array_texture: atlas.handle.clone(), - }, - }); let cur_painter = b_painter.unwrap(); @@ -219,15 +226,15 @@ fn spawn_map( commands.spawn(( MaterialMeshBundle { mesh: meshes.add(mesh), - material: chunk_material.clone(), + material: atlas.chunk_material_handle.clone(), transform: Transform::from_translation(pos), ..default() }, PhosChunk, RenderDistanceVisibility::default().with_offset(Vec3::new( - tile_to_world_distance(Chunk::SIZE as i32 / 2), + (Chunk::SIZE / 2) as f32 * SHORT_DIAGONAL, 0., - tile_to_world_distance(Chunk::SIZE as i32 / 2), + (Chunk::SIZE / 2) as f32 * 1.5, )), Collider::trimesh_with_flags(col_verts, col_indicies, TriMeshFlags::MERGE_DUPLICATE_VERTICES), )); @@ -240,11 +247,7 @@ fn spawn_map( .mesh() .size(heightmap.get_world_width(), heightmap.get_world_height()), ), - material: standard_materials.add(StandardMaterial { - base_color: Color::AQUAMARINE.with_a(0.5), - alpha_mode: AlphaMode::Blend, - ..default() - }), + material: atlas.water_material.clone(), ..default() },)); } diff --git a/game/main/src/prelude.rs b/game/main/src/prelude.rs index f222b45..d68400e 100644 --- a/game/main/src/prelude.rs +++ b/game/main/src/prelude.rs @@ -1,11 +1,16 @@ use bevy::asset::Handle; +use bevy::pbr::ExtendedMaterial; use bevy::prelude::*; use bevy::prelude::{Component, Image, Resource}; use bevy::reflect::Reflect; +use crate::shader_extensions::chunk_material::ChunkMaterial; + #[derive(Resource)] pub struct ChunkAtlas { pub handle: Handle, + pub chunk_material_handle: Handle>, + pub water_material: Handle, pub is_loaded: bool, }