fixes to camera orbit

This commit is contained in:
2024-10-29 20:52:53 -04:00
parent 9120c70b80
commit 3e12fcadbc
6 changed files with 194 additions and 90 deletions

View File

@@ -141,6 +141,14 @@ impl Map {
return Vec3::new(w / 2., self.sealevel, h / 2.); return Vec3::new(w / 2., self.sealevel, h / 2.);
} }
pub fn get_center_with_height(&self) -> Vec3 {
let w = self.get_world_width();
let h = self.get_world_height();
let mut pos = Vec3::new(w / 2., self.sealevel, h / 2.);
pos.y = self.sample_height(&HexCoord::from_world_pos(pos));
return pos;
}
pub fn get_world_width(&self) -> f32 { pub fn get_world_width(&self) -> f32 {
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL; return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
} }

View File

@@ -1,3 +1,7 @@
use std::collections::VecDeque;
use bevy::math::IVec2;
use crate::hex_utils::HexCoord; use crate::hex_utils::HexCoord;
use super::chunk::Chunk; use super::chunk::Chunk;
@@ -42,4 +46,40 @@ impl MeshChunkData {
} }
return (data, has_land); return (data, has_land);
} }
pub fn caluclate_water_distances(data: &mut Vec<MeshChunkData>, height: usize, width: usize, range: usize) {
let mut open: VecDeque<(HexCoord, f32, usize)> = VecDeque::new();
let mut closed: Vec<(HexCoord, f32)> = Vec::new();
for z in 0..height {
for x in 0..width {
let chunk = &mut data[z * height + x];
chunk.prepare_chunk_open(x * Chunk::SIZE, z * Chunk::SIZE, &mut open);
}
}
}
fn prepare_chunk_open(&mut self, offset_x: usize, offset_z: usize, open: &mut VecDeque<(HexCoord, f32, usize)>) {
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
let coord = HexCoord::from_grid_pos(x + offset_x, z + offset_z);
let idx = coord.to_chunk_local_index();
let h = self.heights[idx];
self.distance_to_land[idx] = if h > self.sealevel { 0.0 } else { 4.0 };
if h > self.sealevel {
open.push_back((coord, h, 0));
}
}
}
}
fn fill_chunk_borders(
&mut self,
chunks: &Vec<MeshChunkData>,
offset: IVec2,
open: &mut VecDeque<(HexCoord, f32, usize)>,
closed: &mut Vec<(HexCoord, f32)>,
) {
self.prepare_chunk_open(offset.x as usize * Chunk::SIZE, offset.y as usize * Chunk::SIZE, open);
todo!("Fill closed list with bordering tiles")
}
} }

View File

@@ -2,11 +2,11 @@ use bevy::core_pipeline::experimental::taa::{TemporalAntiAliasBundle, TemporalAn
use bevy::core_pipeline::prepass::DepthPrepass; use bevy::core_pipeline::prepass::DepthPrepass;
use bevy::input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel}; use bevy::input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::window::CursorGrabMode; use shared::sets::GameplaySet;
use shared::states::MenuState;
use shared::tags::MainCamera; use shared::tags::MainCamera;
use world_generation::hex_utils::HexCoord; use world_generation::hex_utils::HexCoord;
use world_generation::prelude::Map; use world_generation::prelude::Map;
use world_generation::states::GeneratorState;
use super::components::*; use super::components::*;
@@ -15,11 +15,15 @@ pub struct PhosCameraPlugin;
impl Plugin for PhosCameraPlugin { impl Plugin for PhosCameraPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_type::<PhosCamera>(); app.register_type::<PhosCamera>();
app.register_type::<PhosOrbitCamera>();
app.add_systems(PreStartup, setup); app.add_systems(PreStartup, setup);
app.add_systems(Update, rts_camera_system.run_if(in_state(MenuState::InGame))); // app.add_systems(Update, rts_camera_system.in_set(GameplaySet));
app.add_systems(PostUpdate, limit_camera_bounds.run_if(in_state(MenuState::InGame))); // app.add_systems(PostUpdate, limit_camera_bounds.in_set(GameplaySet));
app.add_systems(Update, orbit_camera_upate.in_set(GameplaySet));
app.add_systems(Update, init_bounds.run_if(in_state(GeneratorState::SpawnMap)));
//Free Cam //Free Cam
//app.add_systems(Update, (grab_mouse, (update_camera, update_camera_mouse).chain())); //app.add_systems(Update, (grab_mouse, (update_camera, update_camera_mouse).chain()));
@@ -27,6 +31,22 @@ impl Plugin for PhosCameraPlugin {
} }
} }
fn init_bounds(
mut commands: Commands,
mut cam: Query<(&mut Transform, Entity), With<PhosCamera>>,
heightmap: Res<Map>,
) {
let (mut cam_t, cam_entity) = cam.single_mut();
cam_t.translation = heightmap.get_center();
commands
.entity(cam_entity)
.insert(CameraBounds::from_size(heightmap.get_world_size()))
.insert(PhosOrbitCamera {
target: heightmap.get_center_with_height(),
..Default::default()
});
}
fn setup(mut commands: Commands, mut msaa: ResMut<Msaa>) { fn setup(mut commands: Commands, mut msaa: ResMut<Msaa>) {
commands commands
.spawn(( .spawn((
@@ -37,93 +57,108 @@ fn setup(mut commands: Commands, mut msaa: ResMut<Msaa>) {
PhosCamera::default(), PhosCamera::default(),
MainCamera, MainCamera,
DepthPrepass, DepthPrepass,
PhosCameraTargets::default(), PhosOrbitCamera::default(),
)) ))
.insert(TemporalAntiAliasBundle::default()); .insert(TemporalAntiAliasBundle::default());
*msaa = Msaa::Off; *msaa = Msaa::Off;
} }
fn update_camera(
mut cam_query: Query<(&PhosCamera, &mut Transform)>, fn orbit_camera_upate(
keyboard_input: Res<ButtonInput<KeyCode>>, mut cam_query: Query<(&mut Transform, &PhosCamera, &mut PhosOrbitCamera, &CameraBounds)>,
mut wheel: EventReader<MouseWheel>,
mut mouse_motion: EventReader<MouseMotion>,
mouse: Res<ButtonInput<MouseButton>>,
key: Res<ButtonInput<KeyCode>>,
time: Res<Time>, time: Res<Time>,
windows: Query<&Window>, map: Res<Map>,
#[cfg(debug_assertions)] mut gizmos: Gizmos,
) { ) {
let window = windows.single(); let (mut transform, config, mut orbit, bounds) = cam_query.single_mut();
if window.cursor.grab_mode != CursorGrabMode::Locked {
return;
}
let (cam, mut transform) = cam_query.single_mut();
let mut move_vec = Vec3::ZERO; let target = orbit.target;
if keyboard_input.pressed(KeyCode::KeyA) { let mut cam_pos = target;
move_vec += Vec3::NEG_X;
//Apply Camera Dist
cam_pos -= orbit.forward * orbit.distance;
if mouse.pressed(MouseButton::Middle) {
let mut orbit_move = Vec2::ZERO;
for e in mouse_motion.read() {
orbit_move += e.delta;
} }
if keyboard_input.pressed(KeyCode::KeyD) { orbit_move *= f32::to_radians(config.speed) * time.delta_seconds();
move_vec += Vec3::X; let rot_y = Quat::from_axis_angle(Vec3::Y, orbit_move.x);
let right = orbit.forward.cross(Vec3::Y).normalize();
let rot_x = Quat::from_axis_angle(right, orbit_move.y);
orbit.forward = rot_x * rot_y * orbit.forward;
orbit.forward.y = orbit.forward.y.clamp(-1.0, 0.0);
orbit.forward = orbit.forward.normalize();
} }
if keyboard_input.pressed(KeyCode::KeyW) { if key.pressed(KeyCode::KeyE) {
move_vec += Vec3::NEG_Z; let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(config.speed) * time.delta_seconds());
} orbit.forward = rot * orbit.forward;
if keyboard_input.pressed(KeyCode::KeyS) { } else if key.pressed(KeyCode::KeyQ) {
move_vec += Vec3::Z; let rot = Quat::from_axis_angle(Vec3::Y, f32::to_radians(-config.speed) * time.delta_seconds());
orbit.forward = rot * orbit.forward;
} }
let rot = transform.rotation; let mut cam_move = Vec3::ZERO;
move_vec = (rot * move_vec.normalize_or_zero()) * cam.speed * time.delta_seconds();
if keyboard_input.pressed(KeyCode::ShiftLeft) { if key.pressed(KeyCode::KeyA) {
move_vec += Vec3::from(transform.down()); cam_move.x = 1.;
} } else if key.pressed(KeyCode::KeyD) {
if keyboard_input.pressed(KeyCode::Space) { cam_move.x = -1.;
move_vec += Vec3::from(transform.up());
} }
transform.translation += move_vec.normalize_or_zero() * cam.speed * time.delta_seconds(); if key.pressed(KeyCode::KeyW) {
cam_move.z = 1.;
} else if key.pressed(KeyCode::KeyS) {
cam_move.z = -1.;
} }
fn update_camera_mouse( if key.pressed(KeyCode::ShiftLeft) {
mut cam_query: Query<&mut Transform, With<PhosCamera>>, cam_move *= 2.0;
mut mouse_move: EventReader<MouseMotion>,
time: Res<Time>,
windows: Query<&Window>,
) {
let window = windows.single();
if window.cursor.grab_mode != CursorGrabMode::Locked {
return;
} }
let mut transform = cam_query.single_mut();
for ev in mouse_move.read() { if cam_move != Vec3::ZERO {
let (mut yaw, mut pitch, _) = transform.rotation.to_euler(EulerRot::YXZ); cam_move = cam_move.normalize();
match window.cursor.grab_mode { let move_fwd = Vec3::new(orbit.forward.x, 0., orbit.forward.z).normalize();
CursorGrabMode::None => (), let move_rot = Quat::from_rotation_arc(Vec3::NEG_Z, move_fwd);
_ => { #[cfg(debug_assertions)]
// Using smallest of height or width ensures equal vertical and horizontal sensitivity {
pitch -= ev.delta.y.to_radians() * time.delta_seconds() * 5.; gizmos.arrow(orbit.target, orbit.target + move_fwd, LinearRgba::WHITE.with_alpha(0.5));
yaw -= ev.delta.x.to_radians() * time.delta_seconds() * 5.; gizmos.arrow(orbit.target, orbit.target - (move_rot * cam_move), LinearRgba::BLUE);
}
orbit.target -= (move_rot * cam_move) * config.speed * time.delta_seconds();
orbit.target.y = sample_ground(orbit.target, &map);
orbit.target.x = orbit.target.x.clamp(bounds.min.x, bounds.max.x);
orbit.target.z = orbit.target.z.clamp(bounds.min.y, bounds.max.y);
}
let mut scroll = 0.0;
for e in wheel.read() {
match e.unit {
MouseScrollUnit::Line => scroll += e.y * 5.,
MouseScrollUnit::Pixel => scroll += e.y,
} }
} }
pitch = pitch.clamp(-1.54, 1.54); orbit.distance -= scroll * time.delta_seconds() * config.zoom_speed;
orbit.distance = orbit.distance.clamp(config.min_height, config.max_height);
// Order is important to prevent unintended roll // let ground_below_cam = sample_ground(cam_pos, &map) + config.min_height;
transform.rotation = Quat::from_axis_angle(Vec3::Y, yaw) * Quat::from_axis_angle(Vec3::X, pitch); // if cam_pos.y <= ground_below_cam {
} // cam_pos.y = ground_below_cam;
} // }
fn grab_mouse(mut windows: Query<&mut Window>, mouse: Res<ButtonInput<MouseButton>>, key: Res<ButtonInput<KeyCode>>) { // if cam_pos.y < target.y {
let mut window = windows.single_mut(); // cam_pos.y = target.y;
// }
if mouse.just_pressed(MouseButton::Middle) { transform.translation = cam_pos;
window.cursor.visible = false; transform.look_at(target, Vec3::Y);
window.cursor.grab_mode = CursorGrabMode::Locked;
}
if key.just_pressed(KeyCode::Escape) {
window.cursor.visible = true;
window.cursor.grab_mode = CursorGrabMode::None;
}
} }
fn rts_camera_system( fn rts_camera_system(

View File

@@ -1,4 +1,5 @@
use bevy::prelude::*; use bevy::{math::Direction3d, prelude::*};
use rayon::str;
use world_generation::{hex_utils::SHORT_DIAGONAL, prelude::Chunk}; use world_generation::{hex_utils::SHORT_DIAGONAL, prelude::Chunk};
#[derive(Component, Reflect)] #[derive(Component, Reflect)]
@@ -18,12 +19,28 @@ impl Default for PhosCamera {
min_height: 10., min_height: 10.,
max_height: 420., max_height: 420.,
speed: 100., speed: 100.,
zoom_speed: 0.3, zoom_speed: 20.,
min_angle: (20. as f32).to_radians(), min_angle: (20. as f32).to_radians(),
max_angle: 1., max_angle: 1.,
} }
} }
} }
#[derive(Component, Reflect)]
pub struct PhosOrbitCamera {
pub target: Vec3,
pub distance: f32,
pub forward: Vec3,
}
impl Default for PhosOrbitCamera {
fn default() -> Self {
Self {
target: Default::default(),
distance: 40.0,
forward: Vec3::new(0.0, -0.5, 0.5).normalize(),
}
}
}
#[derive(Component)] #[derive(Component)]
pub struct PhosCameraTargets { pub struct PhosCameraTargets {
@@ -53,14 +70,11 @@ pub struct CameraBounds {
} }
impl CameraBounds { impl CameraBounds {
pub fn from_size(size: UVec2) -> Self { pub fn from_size(world_size: Vec2) -> Self {
let padding = Chunk::WORLD_SIZE; let padding = Chunk::WORLD_SIZE;
return Self { return Self {
min: Vec2::ZERO - padding, min: Vec2::ZERO - padding,
max: Vec2::new( max: world_size + padding,
(size.x as usize * Chunk::SIZE) as f32 * SHORT_DIAGONAL,
(size.y * Chunk::SIZE as u32) as f32 * 1.5,
) + padding,
}; };
} }
} }

View File

@@ -21,7 +21,6 @@ use world_generation::{
}; };
use crate::{ use crate::{
camera_system::components::*,
prelude::{PhosAssets, PhosChunk, PhosChunkRegistry}, prelude::{PhosAssets, PhosChunk, PhosChunkRegistry},
shader_extensions::{ shader_extensions::{
chunk_material::ChunkMaterial, chunk_material::ChunkMaterial,
@@ -144,7 +143,6 @@ fn finalize_texture(
fn create_heightmap( fn create_heightmap(
mut commands: Commands, mut commands: Commands,
mut cam: Query<(&mut Transform, Entity), With<PhosCamera>>,
mut next_state: ResMut<NextState<GeneratorState>>, mut next_state: ResMut<NextState<GeneratorState>>,
biome_painter: Res<BiomePainter>, biome_painter: Res<BiomePainter>,
) { ) {
@@ -200,10 +198,6 @@ fn create_heightmap(
}; };
let (heightmap, biome_map) = 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(heightmap);
commands.insert_resource(biome_map); commands.insert_resource(biome_map);
commands.insert_resource(config); commands.insert_resource(config);

View File

@@ -1,12 +1,14 @@
use bevy::{gizmos::gizmos, prelude::*}; use bevy::{gizmos::gizmos, prelude::*};
use shared::resources::TileUnderCursor;
use shared::states::GameplayState; use shared::states::GameplayState;
use shared::{resources::TileUnderCursor, sets::GameplaySet};
use world_generation::{ use world_generation::{
consts::{HEX_CORNERS, WATER_HEX_CORNERS}, consts::{HEX_CORNERS, WATER_HEX_CORNERS},
prelude::Map, prelude::Map,
states::GeneratorState, states::GeneratorState,
}; };
use crate::camera_system::components::{PhosCamera, PhosOrbitCamera};
pub struct DebugPlugin; pub struct DebugPlugin;
impl Plugin for DebugPlugin { impl Plugin for DebugPlugin {
@@ -27,6 +29,7 @@ impl Plugin for DebugPlugin {
.run_if(in_state(DebugState::Verbose)), .run_if(in_state(DebugState::Verbose)),
); );
app.add_systems(Update, camera_debug.in_set(GameplaySet));
app.add_systems(Update, regenerate_map.run_if(in_state(GeneratorState::Idle))); app.add_systems(Update, regenerate_map.run_if(in_state(GeneratorState::Idle)));
app.insert_resource(Shape(Polyline3d::new([ app.insert_resource(Shape(Polyline3d::new([
@@ -89,4 +92,14 @@ fn show_water_corners(pos: Vec3, gizmos: &mut Gizmos) {
} }
} }
fn camera_debug(mut cam_query: Query<(&PhosCamera, &PhosOrbitCamera)>, mut gizmos: Gizmos) {
let (config, orbit) = cam_query.single();
gizmos.sphere(orbit.target, Quat::IDENTITY, 0.3, LinearRgba::RED);
let cam_proxy = orbit.target - (orbit.forward * 10.0);
gizmos.ray(orbit.target, orbit.forward * 10.0, LinearRgba::rgb(1.0, 0.0, 1.0));
gizmos.circle(cam_proxy, Dir3::Y, 0.3, LinearRgba::rgb(1.0, 1.0, 0.0));
}
fn verbose_data() {} fn verbose_data() {}