From 0ae87b6dddf05831d7d36cb79898d7f8db676190 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Wed, 14 Feb 2024 01:59:37 +0100 Subject: [PATCH 01/10] wip client physics --- kubi/src/client_physics.rs | 75 +++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index cacbaf1..17bcb7b 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -1,9 +1,9 @@ //TODO client-side physics //TODO move this to shared -use glam::{Mat4, Vec3}; -use kubi_shared::transform::Transform; +use glam::{vec3, IVec3, Mat4, Vec3}; use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, View, ViewMut}; -use crate::delta_time::DeltaTime; +use kubi_shared::{block::{Block, CollisionType}, transform::Transform}; +use crate::{delta_time::DeltaTime, world::ChunkStorage}; #[derive(Unique)] pub struct GlobalClPhysicsConfig { @@ -12,20 +12,35 @@ pub struct GlobalClPhysicsConfig { #[derive(Component)] pub struct ClPhysicsActor { + pub offset: Vec3, pub forces: Vec3, pub velocity: Vec3, pub terminal_velocity: f32, //TODO: this should be configurable per block pub friction_agains_ground: f32, + on_ground_flag: bool, +} + +impl ClPhysicsActor { + pub fn apply_force(&mut self, force: Vec3) { + self.forces += force; + } + + pub fn on_ground(&self) -> bool { + self.on_ground_flag + } } impl Default for ClPhysicsActor { fn default() -> Self { Self { + //HACK: for player + offset: vec3(0., 1.5, 0.), forces: Vec3::ZERO, velocity: Vec3::ZERO, terminal_velocity: 40., friction_agains_ground: 0.5, + on_ground_flag: false, } } } @@ -34,16 +49,64 @@ pub fn init_client_physics( storages: AllStoragesView, ) { storages.add_unique(GlobalClPhysicsConfig { - gravity: Vec3::new(0., -9.8, 0.), + gravity: Vec3::new(0., -1.0, 0.), }); } pub fn update_client_physics_late( - controllers: View, + mut actors: ViewMut, mut transforms: ViewMut, - dt: UniqueView, phy_conf: UniqueView, + world: UniqueView, + dt: UniqueView, ) { + for (mut actor, mut transform) in (&mut actors, &mut transforms).iter() { + //apply forces + let actor_forces = actor.forces; + actor.velocity += actor_forces + phy_conf.gravity; + actor.forces = Vec3::ZERO; + + let (scale, rotation, mut actor_position) = transform.0.to_scale_rotation_translation(); + actor_position -= actor.offset; + let actor_block_pos = actor_position.floor().as_ivec3(); + let actor_block = world.get_block(actor_block_pos); + let actor_block_below = world.get_block(actor_block_pos + IVec3::NEG_Y); + actor.on_ground_flag = + actor_block_below.map_or_else(|| false, |x| x.descriptor().collision == CollisionType::Solid) || + actor_block.map_or_else(|| false, |x| x.descriptor().collision == CollisionType::Solid); + //push actor back out of the block + if actor_block.is_some() { + //first, compute the normal (assuming actor is a point) + //must be accurate! + let mut normal = Vec3::ZERO; + for i in 0..3 { + let mut offset = Vec3::ZERO; + offset[i] = 0.5; + let block_pos = actor_block_pos + offset.as_ivec3(); + let block = world.get_block(block_pos).unwrap_or(Block::Air); + if block.descriptor().collision == CollisionType::Solid { + normal[i] = 1.; + } + } + //then, based on normal: + //push the actor back + actor_position += normal * 0.5; + //cancel out velocity in the direction of the normal + // let dot = actor.velocity.dot(normal); + // if dot > 0. { + // //actor.velocity -= normal * dot; + // actor.velocity = Vec3::ZERO; + // } + if actor.on_ground_flag { + actor.velocity.y = 0.; + } + } + //Apply velocity + actor_position += actor.velocity * dt.0.as_secs_f32(); + actor_position += actor.offset; + transform.0 = Mat4::from_scale_rotation_translation(scale, rotation, actor_position); + } + // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); // translation.y -= dt.0.as_secs_f32() * 100.; From 93e34b2c88fd3dbc7f6d8509d7a04d33c48ce512 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Wed, 14 Feb 2024 21:56:59 +0100 Subject: [PATCH 02/10] refactor stuff --- kubi/src/client_physics.rs | 86 ++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index 17bcb7b..7bb2aad 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -8,8 +8,21 @@ use crate::{delta_time::DeltaTime, world::ChunkStorage}; #[derive(Unique)] pub struct GlobalClPhysicsConfig { pub gravity: Vec3, + ///XXX: currenly unused: + pub iterations: usize, } +impl Default for GlobalClPhysicsConfig { + fn default() -> Self { + Self { + gravity: Vec3::new(0., -9.8, 0.), + iterations: 10, + } + } +} + +//TODO: actors should be represented by a vertical line, not a point. +//XXX: maybe a capsule? (or configurable hull?) #[derive(Component)] pub struct ClPhysicsActor { pub offset: Vec3, @@ -18,7 +31,8 @@ pub struct ClPhysicsActor { pub terminal_velocity: f32, //TODO: this should be configurable per block pub friction_agains_ground: f32, - on_ground_flag: bool, + flag_ground: bool, + flag_collision: bool, } impl ClPhysicsActor { @@ -27,7 +41,7 @@ impl ClPhysicsActor { } pub fn on_ground(&self) -> bool { - self.on_ground_flag + self.flag_ground } } @@ -40,73 +54,91 @@ impl Default for ClPhysicsActor { velocity: Vec3::ZERO, terminal_velocity: 40., friction_agains_ground: 0.5, - on_ground_flag: false, + flag_ground: false, + flag_collision: false, } } } +trait BlockCollisionExt { + fn collision_type(&self) -> CollisionType; + fn is_solid(&self) -> bool { + self.collision_type() == CollisionType::Solid + } +} + +impl BlockCollisionExt for Option { + fn collision_type(&self) -> CollisionType { + self.unwrap_or(Block::Air).descriptor().collision + } +} + +impl BlockCollisionExt for Block { + fn collision_type(&self) -> CollisionType { + self.descriptor().collision + } +} + pub fn init_client_physics( storages: AllStoragesView, ) { - storages.add_unique(GlobalClPhysicsConfig { - gravity: Vec3::new(0., -1.0, 0.), - }); + storages.add_unique(GlobalClPhysicsConfig::default()); } pub fn update_client_physics_late( mut actors: ViewMut, mut transforms: ViewMut, - phy_conf: UniqueView, + conf: UniqueView, world: UniqueView, dt: UniqueView, ) { for (mut actor, mut transform) in (&mut actors, &mut transforms).iter() { //apply forces let actor_forces = actor.forces; - actor.velocity += actor_forces + phy_conf.gravity; + actor.velocity += (actor_forces + conf.gravity) * dt.0.as_secs_f32(); actor.forces = Vec3::ZERO; + //get position let (scale, rotation, mut actor_position) = transform.0.to_scale_rotation_translation(); actor_position -= actor.offset; + + //get grid-aligned pos and blocks let actor_block_pos = actor_position.floor().as_ivec3(); let actor_block = world.get_block(actor_block_pos); let actor_block_below = world.get_block(actor_block_pos + IVec3::NEG_Y); - actor.on_ground_flag = - actor_block_below.map_or_else(|| false, |x| x.descriptor().collision == CollisionType::Solid) || - actor_block.map_or_else(|| false, |x| x.descriptor().collision == CollisionType::Solid); + + //update flags + actor.flag_collision = actor_block.is_solid(); + actor.flag_ground = actor.flag_collision || actor_block_below.is_solid(); + //push actor back out of the block - if actor_block.is_some() { - //first, compute the normal (assuming actor is a point) - //must be accurate! - let mut normal = Vec3::ZERO; - for i in 0..3 { - let mut offset = Vec3::ZERO; - offset[i] = 0.5; - let block_pos = actor_block_pos + offset.as_ivec3(); - let block = world.get_block(block_pos).unwrap_or(Block::Air); - if block.descriptor().collision == CollisionType::Solid { - normal[i] = 1.; - } - } + if actor.flag_collision { + //first, compute restitution, based on position inside the block + // let block_center = actor_block_pos.as_f32() + Vec3::ONE * 0.5; + // let to_block_center = actor_position - block_center; + //then, based on normal: //push the actor back - actor_position += normal * 0.5; + //actor_position += normal * 0.5; //cancel out velocity in the direction of the normal // let dot = actor.velocity.dot(normal); // if dot > 0. { // //actor.velocity -= normal * dot; // actor.velocity = Vec3::ZERO; // } - if actor.on_ground_flag { + + //HACK: for now, just stop the vertical velocity if on ground altogether, + //as we don't have proper collision velocity resolution yet (we need to compute dot product or sth) + if actor.flag_ground { actor.velocity.y = 0.; } } + //Apply velocity actor_position += actor.velocity * dt.0.as_secs_f32(); actor_position += actor.offset; transform.0 = Mat4::from_scale_rotation_translation(scale, rotation, actor_position); } - // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); // translation.y -= dt.0.as_secs_f32() * 100.; From 1466f62b5a9452257ffe9b17d06f0a7ba2564147 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 15 Feb 2024 01:51:41 +0100 Subject: [PATCH 03/10] pl ctl --- kubi/src/input.rs | 598 +++++++++--------- kubi/src/lib.rs | 6 +- kubi/src/player.rs | 6 +- ...fly_controller.rs => player_controller.rs} | 33 +- 4 files changed, 334 insertions(+), 309 deletions(-) rename kubi/src/{fly_controller.rs => player_controller.rs} (71%) diff --git a/kubi/src/input.rs b/kubi/src/input.rs index 406118b..91f445a 100644 --- a/kubi/src/input.rs +++ b/kubi/src/input.rs @@ -1,297 +1,301 @@ -use gilrs::{Gilrs, GamepadId, Button, Event, Axis}; -use glam::{Vec2, DVec2, vec2, dvec2}; -use winit::{ - keyboard::{KeyCode, PhysicalKey}, - event::{DeviceEvent, DeviceId, ElementState, TouchPhase} -}; -use hashbrown::HashMap; -use tinyset::{SetU32, SetU64}; -use nohash_hasher::BuildNoHashHasher; -use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync}; -use crate::{ - events::{InputDeviceEvent, TouchEvent}, - rendering::WindowSize -}; - -#[derive(Unique, Clone, Copy, Default, Debug)] -pub struct Inputs { - pub movement: Vec2, - pub look: Vec2, - pub action_a: bool, - pub action_b: bool, -} - -#[derive(Unique, Clone, Copy, Default, Debug)] -pub struct PrevInputs(pub Inputs); - -#[derive(Unique, Clone, Default, Debug)] -pub struct RawKbmInputState { - pub keyboard_state: SetU32, - pub button_state: [bool; 32], - pub mouse_delta: DVec2 -} - -#[derive(Clone, Copy, Debug, Default)] -pub enum FingerCheck { - #[default] - Start, - Current, - StartOrCurrent, - StartAndCurrent, - NotMoved, -} - -#[derive(Clone, Copy, Debug)] -pub struct Finger { - pub id: u64, - pub device_id: DeviceId, - pub prev_position: DVec2, - pub start_position: DVec2, - pub current_position: DVec2, - pub has_moved: bool, -} -impl Finger { - pub fn within_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> bool { - let within_area = |pos: DVec2| -> bool { - ((pos - area_pos).min_element() >= 0.) && - ((pos - (area_pos + area_size)).max_element() <= 0.) - }; - let start = within_area(self.start_position); - let current = within_area(self.current_position); - match check { - FingerCheck::Start => start, - FingerCheck::Current => current, - FingerCheck::StartOrCurrent => start || current, - FingerCheck::StartAndCurrent => start && current, - FingerCheck::NotMoved => current && !self.has_moved, - } - } -} - -#[derive(Unique, Clone, Default, Debug)] -pub struct RawTouchState { - //TODO: handle multiple touch devices somehow - pub fingers: HashMap> -} - -impl RawTouchState { - pub fn query_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> impl Iterator + '_ { - self.fingers.iter().filter_map(move |(_, &finger)| { - finger.within_area(area_pos, area_size, check).then_some(finger) - }) - } -} - -#[derive(Unique)] -pub struct GilrsWrapper(Option); - -#[derive(Unique, Default, Clone, Copy)] -pub struct ActiveGamepad(Option); - -//maybe we should manage gamepad state ourselves just like keyboard? -//at least for the sake of consitency - -fn process_events( - device_events: View, - mut input_state: UniqueViewMut, -) { - input_state.mouse_delta = DVec2::ZERO; - for event in device_events.iter() { - match &event.event { - DeviceEvent::MouseMotion { delta } => { - input_state.mouse_delta = DVec2::from(*delta); - }, - DeviceEvent::Key(input) => { - if let PhysicalKey::Code(code) = input.physical_key { - match input.state { - ElementState::Pressed => input_state.keyboard_state.insert(code as u32), - ElementState::Released => input_state.keyboard_state.remove(code as u32), - }; - } - }, - DeviceEvent::Button { button, state } => { - if *button < 32 { - input_state.button_state[*button as usize] = matches!(*state, ElementState::Pressed); - } - }, - _ => () - } - } -} - -fn process_touch_events( - touch_events: View, - mut touch_state: UniqueViewMut, -) { - for (_, finger) in &mut touch_state.fingers { - finger.prev_position = finger.current_position; - } - for event in touch_events.iter() { - let position = dvec2(event.0.location.x, event.0.location.y); - match event.0.phase { - TouchPhase::Started => { - //println!("touch started: finger {}", event.0.id); - touch_state.fingers.insert(event.0.id, Finger { - id: event.0.id, - device_id: event.0.device_id, - start_position: position, - current_position: position, - prev_position: position, - has_moved: false - }); - }, - TouchPhase::Moved => { - if let Some(finger) = touch_state.fingers.get_mut(&event.0.id) { - finger.has_moved = true; - finger.current_position = position; - } - }, - TouchPhase::Ended | TouchPhase::Cancelled => { - //println!("touch ended: finger {}", event.0.id); - touch_state.fingers.remove(&event.0.id); - }, - } - } -} - -fn process_gilrs_events( - mut gilrs: NonSendSync>, - mut active_gamepad: UniqueViewMut -) { - if let Some(gilrs) = &mut gilrs.0 { - while let Some(Event { id, event: _, time: _ }) = gilrs.next_event() { - active_gamepad.0 = Some(id); - } - } -} - -fn input_start( - mut inputs: UniqueViewMut, - mut prev_inputs: UniqueViewMut, -) { - prev_inputs.0 = *inputs; - *inputs = Inputs::default(); -} - -fn update_input_state ( - raw_inputs: UniqueView, - mut inputs: UniqueViewMut, -) { - inputs.movement += Vec2::new( - raw_inputs.keyboard_state.contains(KeyCode::KeyD as u32) as u32 as f32 - - raw_inputs.keyboard_state.contains(KeyCode::KeyA as u32) as u32 as f32, - raw_inputs.keyboard_state.contains(KeyCode::KeyW as u32) as u32 as f32 - - raw_inputs.keyboard_state.contains(KeyCode::KeyS as u32) as u32 as f32 - ); - inputs.look += raw_inputs.mouse_delta.as_vec2(); - inputs.action_a |= raw_inputs.button_state[0]; - inputs.action_b |= raw_inputs.button_state[1]; -} - -fn update_input_state_gamepad ( - gilrs: NonSendSync>, - active_gamepad: UniqueView, - mut inputs: UniqueViewMut, -) { - if let Some(gilrs) = &gilrs.0 { - if let Some(gamepad) = active_gamepad.0.map(|id| gilrs.gamepad(id)) { - let left_stick = vec2(gamepad.value(Axis::LeftStickX), gamepad.value(Axis::LeftStickY)); - let right_stick = vec2(gamepad.value(Axis::RightStickX), -gamepad.value(Axis::RightStickY)); - inputs.movement += left_stick; - inputs.look += right_stick; - inputs.action_a |= gamepad.is_pressed(Button::South); - inputs.action_b |= gamepad.is_pressed(Button::East); - } - } -} - -fn update_input_state_touch ( - touch_state: UniqueView, - win_size: UniqueView, - mut inputs: UniqueViewMut, -) { - let w = win_size.0.as_dvec2(); - - //Movement - if let Some(finger) = touch_state.query_area( - dvec2(0., 0.), - dvec2(w.x / 2., w.y), - FingerCheck::Start - ).next() { - inputs.movement += (((finger.current_position - finger.start_position) / (w.x / 4.)) * dvec2(1., -1.)).as_vec2(); - } - - //Action buttons - let action_button_fingers = { - let mut action_button_fingers = SetU64::new(); - - //Creates iterator of fingers that started within action button area - let action_finger_iter = || touch_state.query_area( - dvec2(w.x * 0.75, w.y * 0.666), - dvec2(w.x * 0.25, w.y * 0.333), - FingerCheck::Start - ); - - //Action button A - inputs.action_a |= action_finger_iter().filter(|finger| finger.within_area( - dvec2(w.x * (0.75 + 0.125), w.y * 0.666), - dvec2(w.x * 0.125, w.y * 0.333), - FingerCheck::StartOrCurrent - )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); - - //Action button B - inputs.action_b |= action_finger_iter().filter(|finger| finger.within_area( - dvec2(w.x * 0.75, w.y * 0.666), - dvec2(w.x * 0.125, w.y * 0.333), - FingerCheck::StartOrCurrent - )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); - - action_button_fingers - }; - - //Camera controls - if let Some(finger) = touch_state.query_area( - dvec2(w.x / 2., 0.), - dvec2(w.x / 2., w.y), - FingerCheck::Start - ).find(|x| !action_button_fingers.contains(x.id)) { - inputs.look += (((finger.current_position - finger.prev_position) / (w.x / 4.)) * 300.).as_vec2(); - } -} - -fn input_end( - mut inputs: UniqueViewMut, -) { - if inputs.movement.length() >= 1. { - inputs.movement = inputs.movement.normalize(); - } -} - -pub fn init_input ( - storages: AllStoragesView -) { - storages.add_unique_non_send_sync(GilrsWrapper( - Gilrs::new().map_err(|x| { - log::error!("Failed to initialize Gilrs"); - x - }).ok() - )); - storages.add_unique(ActiveGamepad::default()); - storages.add_unique(Inputs::default()); - storages.add_unique(PrevInputs::default()); - storages.add_unique(RawKbmInputState::default()); - storages.add_unique(RawTouchState::default()); -} - -pub fn process_inputs() -> Workload { - ( - process_events, - process_touch_events, - process_gilrs_events, - input_start, - update_input_state, - update_input_state_touch, - update_input_state_gamepad, - input_end, - ).into_sequential_workload() -} +use gilrs::{Gilrs, GamepadId, Button, Event, Axis}; +use glam::{Vec2, DVec2, vec2, dvec2}; +use winit::{ + keyboard::{KeyCode, PhysicalKey}, + event::{DeviceEvent, DeviceId, ElementState, TouchPhase} +}; +use hashbrown::HashMap; +use tinyset::{SetU32, SetU64}; +use nohash_hasher::BuildNoHashHasher; +use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync}; +use crate::{ + events::{InputDeviceEvent, TouchEvent}, + rendering::WindowSize +}; + +#[derive(Unique, Clone, Copy, Default, Debug)] +pub struct Inputs { + pub movement: Vec2, + pub look: Vec2, + pub action_a: bool, + pub action_b: bool, + pub jump: bool, +} + +#[derive(Unique, Clone, Copy, Default, Debug)] +pub struct PrevInputs(pub Inputs); + +#[derive(Unique, Clone, Default, Debug)] +pub struct RawKbmInputState { + pub keyboard_state: SetU32, + pub button_state: [bool; 32], + pub mouse_delta: DVec2 +} + +#[derive(Clone, Copy, Debug, Default)] +pub enum FingerCheck { + #[default] + Start, + Current, + StartOrCurrent, + StartAndCurrent, + NotMoved, +} + +#[derive(Clone, Copy, Debug)] +pub struct Finger { + pub id: u64, + pub device_id: DeviceId, + pub prev_position: DVec2, + pub start_position: DVec2, + pub current_position: DVec2, + pub has_moved: bool, +} +impl Finger { + pub fn within_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> bool { + let within_area = |pos: DVec2| -> bool { + ((pos - area_pos).min_element() >= 0.) && + ((pos - (area_pos + area_size)).max_element() <= 0.) + }; + let start = within_area(self.start_position); + let current = within_area(self.current_position); + match check { + FingerCheck::Start => start, + FingerCheck::Current => current, + FingerCheck::StartOrCurrent => start || current, + FingerCheck::StartAndCurrent => start && current, + FingerCheck::NotMoved => current && !self.has_moved, + } + } +} + +#[derive(Unique, Clone, Default, Debug)] +pub struct RawTouchState { + //TODO: handle multiple touch devices somehow + pub fingers: HashMap> +} + +impl RawTouchState { + pub fn query_area(&self, area_pos: DVec2, area_size: DVec2, check: FingerCheck) -> impl Iterator + '_ { + self.fingers.iter().filter_map(move |(_, &finger)| { + finger.within_area(area_pos, area_size, check).then_some(finger) + }) + } +} + +#[derive(Unique)] +pub struct GilrsWrapper(Option); + +#[derive(Unique, Default, Clone, Copy)] +pub struct ActiveGamepad(Option); + +//maybe we should manage gamepad state ourselves just like keyboard? +//at least for the sake of consitency + +fn process_events( + device_events: View, + mut input_state: UniqueViewMut, +) { + input_state.mouse_delta = DVec2::ZERO; + for event in device_events.iter() { + match &event.event { + DeviceEvent::MouseMotion { delta } => { + input_state.mouse_delta = DVec2::from(*delta); + }, + DeviceEvent::Key(input) => { + if let PhysicalKey::Code(code) = input.physical_key { + match input.state { + ElementState::Pressed => input_state.keyboard_state.insert(code as u32), + ElementState::Released => input_state.keyboard_state.remove(code as u32), + }; + } + }, + DeviceEvent::Button { button, state } => { + if *button < 32 { + input_state.button_state[*button as usize] = matches!(*state, ElementState::Pressed); + } + }, + _ => () + } + } +} + +fn process_touch_events( + touch_events: View, + mut touch_state: UniqueViewMut, +) { + for (_, finger) in &mut touch_state.fingers { + finger.prev_position = finger.current_position; + } + for event in touch_events.iter() { + let position = dvec2(event.0.location.x, event.0.location.y); + match event.0.phase { + TouchPhase::Started => { + //println!("touch started: finger {}", event.0.id); + touch_state.fingers.insert(event.0.id, Finger { + id: event.0.id, + device_id: event.0.device_id, + start_position: position, + current_position: position, + prev_position: position, + has_moved: false + }); + }, + TouchPhase::Moved => { + if let Some(finger) = touch_state.fingers.get_mut(&event.0.id) { + finger.has_moved = true; + finger.current_position = position; + } + }, + TouchPhase::Ended | TouchPhase::Cancelled => { + //println!("touch ended: finger {}", event.0.id); + touch_state.fingers.remove(&event.0.id); + }, + } + } +} + +fn process_gilrs_events( + mut gilrs: NonSendSync>, + mut active_gamepad: UniqueViewMut +) { + if let Some(gilrs) = &mut gilrs.0 { + while let Some(Event { id, event: _, time: _ }) = gilrs.next_event() { + active_gamepad.0 = Some(id); + } + } +} + +fn input_start( + mut inputs: UniqueViewMut, + mut prev_inputs: UniqueViewMut, +) { + prev_inputs.0 = *inputs; + *inputs = Inputs::default(); +} + +fn update_input_state ( + raw_inputs: UniqueView, + mut inputs: UniqueViewMut, +) { + inputs.movement += Vec2::new( + raw_inputs.keyboard_state.contains(KeyCode::KeyD as u32) as u32 as f32 - + raw_inputs.keyboard_state.contains(KeyCode::KeyA as u32) as u32 as f32, + raw_inputs.keyboard_state.contains(KeyCode::KeyW as u32) as u32 as f32 - + raw_inputs.keyboard_state.contains(KeyCode::KeyS as u32) as u32 as f32 + ); + inputs.look += raw_inputs.mouse_delta.as_vec2(); + inputs.action_a |= raw_inputs.button_state[0]; + inputs.action_b |= raw_inputs.button_state[1]; + inputs.jump |= raw_inputs.button_state[2]; +} + +fn update_input_state_gamepad ( + gilrs: NonSendSync>, + active_gamepad: UniqueView, + mut inputs: UniqueViewMut, +) { + if let Some(gilrs) = &gilrs.0 { + if let Some(gamepad) = active_gamepad.0.map(|id| gilrs.gamepad(id)) { + let left_stick = vec2(gamepad.value(Axis::LeftStickX), gamepad.value(Axis::LeftStickY)); + let right_stick = vec2(gamepad.value(Axis::RightStickX), -gamepad.value(Axis::RightStickY)); + inputs.movement += left_stick; + //HACK: for now, we multiply look by 2 to make it feel more responsive + inputs.look += right_stick * 2.; + inputs.action_a |= gamepad.is_pressed(Button::West); + inputs.action_b |= gamepad.is_pressed(Button::East); + inputs.jump |= gamepad.is_pressed(Button::South); + } + } +} + +fn update_input_state_touch ( + touch_state: UniqueView, + win_size: UniqueView, + mut inputs: UniqueViewMut, +) { + let w = win_size.0.as_dvec2(); + + //Movement + if let Some(finger) = touch_state.query_area( + dvec2(0., 0.), + dvec2(w.x / 2., w.y), + FingerCheck::Start + ).next() { + inputs.movement += (((finger.current_position - finger.start_position) / (w.x / 4.)) * dvec2(1., -1.)).as_vec2(); + } + + //Action buttons + let action_button_fingers = { + let mut action_button_fingers = SetU64::new(); + + //Creates iterator of fingers that started within action button area + let action_finger_iter = || touch_state.query_area( + dvec2(w.x * 0.75, w.y * 0.666), + dvec2(w.x * 0.25, w.y * 0.333), + FingerCheck::Start + ); + + //Action button A + inputs.action_a |= action_finger_iter().filter(|finger| finger.within_area( + dvec2(w.x * (0.75 + 0.125), w.y * 0.666), + dvec2(w.x * 0.125, w.y * 0.333), + FingerCheck::StartOrCurrent + )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); + + //Action button B + inputs.action_b |= action_finger_iter().filter(|finger| finger.within_area( + dvec2(w.x * 0.75, w.y * 0.666), + dvec2(w.x * 0.125, w.y * 0.333), + FingerCheck::StartOrCurrent + )).map(|x| action_button_fingers.insert(x.id)).next().is_some(); + + action_button_fingers + }; + + //Camera controls + if let Some(finger) = touch_state.query_area( + dvec2(w.x / 2., 0.), + dvec2(w.x / 2., w.y), + FingerCheck::Start + ).find(|x| !action_button_fingers.contains(x.id)) { + inputs.look += (((finger.current_position - finger.prev_position) / (w.x / 4.)) * 300.).as_vec2(); + } +} + +fn input_end( + mut inputs: UniqueViewMut, +) { + if inputs.movement.length() >= 1. { + inputs.movement = inputs.movement.normalize(); + } +} + +pub fn init_input ( + storages: AllStoragesView +) { + storages.add_unique_non_send_sync(GilrsWrapper( + Gilrs::new().map_err(|x| { + log::error!("Failed to initialize Gilrs"); + x + }).ok() + )); + storages.add_unique(ActiveGamepad::default()); + storages.add_unique(Inputs::default()); + storages.add_unique(PrevInputs::default()); + storages.add_unique(RawKbmInputState::default()); + storages.add_unique(RawTouchState::default()); +} + +pub fn process_inputs() -> Workload { + ( + process_events, + process_touch_events, + process_gilrs_events, + input_start, + update_input_state, + update_input_state_touch, + update_input_state_gamepad, + input_end, + ).into_sequential_workload() +} diff --git a/kubi/src/lib.rs b/kubi/src/lib.rs index 0371b3e..9fe125b 100644 --- a/kubi/src/lib.rs +++ b/kubi/src/lib.rs @@ -23,7 +23,7 @@ pub(crate) mod settings; pub(crate) mod camera; pub(crate) mod events; pub(crate) mod input; -pub(crate) mod fly_controller; +pub(crate) mod player_controller; pub(crate) mod block_placement; pub(crate) mod delta_time; pub(crate) mod cursor_lock; @@ -57,7 +57,7 @@ use events::{ player_actions::generate_move_events, }; use input::{init_input, process_inputs}; -use fly_controller::update_controllers; +use player_controller::update_player_controllers; use rendering::{ Renderer, RenderTarget, @@ -133,7 +133,7 @@ fn update() -> Workload { update_loaded_world_around_player, ).into_sequential_workload().run_if(is_ingame_or_loading), ( - update_controllers, + update_player_controllers, update_client_physics_late, generate_move_events, update_raycasts, diff --git a/kubi/src/player.rs b/kubi/src/player.rs index b500e53..7fda47e 100644 --- a/kubi/src/player.rs +++ b/kubi/src/player.rs @@ -12,7 +12,7 @@ use kubi_shared::{ use crate::{ camera::Camera, client_physics::ClPhysicsActor, - fly_controller::FlyController, + player_controller::PlayerController, transform::Transform, world::raycast::LookingAtBlock }; @@ -31,7 +31,7 @@ pub fn spawn_player ( Health::new(PLAYER_HEALTH), Transform::default(), Camera::default(), - FlyController, + PlayerController::DEFAULT_FPS_CTL, LookingAtBlock::default(), PlayerHolding(Some(Block::Cobblestone)), Username("LocalPlayer".into()), @@ -53,7 +53,7 @@ pub fn spawn_local_player_multiplayer ( init.health, Transform(Mat4::from_rotation_translation(init.direction, init.position)), Camera::default(), - FlyController, + PlayerController::DEFAULT_FPS_CTL, LookingAtBlock::default(), PlayerHolding::default(), ),( diff --git a/kubi/src/fly_controller.rs b/kubi/src/player_controller.rs similarity index 71% rename from kubi/src/fly_controller.rs rename to kubi/src/player_controller.rs index c13f760..6b1c949 100644 --- a/kubi/src/fly_controller.rs +++ b/kubi/src/player_controller.rs @@ -3,10 +3,31 @@ use shipyard::{Component, View, ViewMut, IntoIter, UniqueView, Workload, IntoWor use std::f32::consts::PI; use crate::{transform::Transform, input::Inputs, settings::GameSettings, delta_time::DeltaTime}; -#[derive(Component)] -pub struct FlyController; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PlayerControllerType { + FlyCam, + FpsCtl, +} -pub fn update_controllers() -> Workload { +#[derive(Component)] +pub struct PlayerController { + pub control_type: PlayerControllerType, + pub speed: f32, +} + +impl PlayerController { + pub const DEFAULT_FLY_CAM: Self = Self { + control_type: PlayerControllerType::FlyCam, + speed: 30., + }; + + pub const DEFAULT_FPS_CTL: Self = Self { + control_type: PlayerControllerType::FpsCtl, + speed: 10., + }; +} + +pub fn update_player_controllers() -> Workload { ( update_look, update_movement @@ -16,7 +37,7 @@ pub fn update_controllers() -> Workload { const MAX_PITCH: f32 = PI/2. - 0.05; fn update_look( - controllers: View, + controllers: View, mut transforms: ViewMut, inputs: UniqueView, settings: UniqueView, @@ -36,13 +57,13 @@ fn update_look( } fn update_movement( - controllers: View, + controllers: View, mut transforms: ViewMut, inputs: UniqueView, dt: UniqueView, ) { if inputs.movement == Vec2::ZERO { return } - let movement = inputs.movement * 30. * dt.0.as_secs_f32(); + let movement = inputs.movement * dt.0.as_secs_f32(); for (_, mut transform) in (&controllers, &mut transforms).iter() { let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); let rotation_norm = rotation.normalize(); From b34f1a94b180d65e6c7b40c496919ae6b75b1a73 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 15 Feb 2024 01:54:31 +0100 Subject: [PATCH 04/10] fix ctl speed --- kubi/src/player_controller.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kubi/src/player_controller.rs b/kubi/src/player_controller.rs index 6b1c949..402e4ce 100644 --- a/kubi/src/player_controller.rs +++ b/kubi/src/player_controller.rs @@ -64,11 +64,11 @@ fn update_movement( ) { if inputs.movement == Vec2::ZERO { return } let movement = inputs.movement * dt.0.as_secs_f32(); - for (_, mut transform) in (&controllers, &mut transforms).iter() { + for (ctl, mut transform) in (&controllers, &mut transforms).iter() { let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); let rotation_norm = rotation.normalize(); - translation += (rotation_norm * Vec3::NEG_Z).normalize() * movement.y; - translation += (rotation_norm * Vec3::X).normalize() * movement.x; + translation += (rotation_norm * Vec3::NEG_Z).normalize() * movement.y * ctl.speed; + translation += (rotation_norm * Vec3::X).normalize() * movement.x * ctl.speed; transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); } } From 89ccd595ac0e332c4e44dbcf78a1662528d95312 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 15 Feb 2024 14:49:41 +0100 Subject: [PATCH 05/10] fps ctl test impl --- kubi/src/client_physics.rs | 11 ++++++- kubi/src/input.rs | 2 +- kubi/src/lib.rs | 3 +- kubi/src/player_controller.rs | 56 ++++++++++++++++++++++++++++------- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index 7bb2aad..f23ba00 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -25,12 +25,14 @@ impl Default for GlobalClPhysicsConfig { //XXX: maybe a capsule? (or configurable hull?) #[derive(Component)] pub struct ClPhysicsActor { + pub disable: bool, pub offset: Vec3, pub forces: Vec3, pub velocity: Vec3, pub terminal_velocity: f32, //TODO: this should be configurable per block pub friction_agains_ground: f32, + pub gravity_scale: f32, flag_ground: bool, flag_collision: bool, } @@ -49,11 +51,13 @@ impl Default for ClPhysicsActor { fn default() -> Self { Self { //HACK: for player + disable: false, offset: vec3(0., 1.5, 0.), forces: Vec3::ZERO, velocity: Vec3::ZERO, terminal_velocity: 40., friction_agains_ground: 0.5, + gravity_scale: 1., flag_ground: false, flag_collision: false, } @@ -93,6 +97,11 @@ pub fn update_client_physics_late( dt: UniqueView, ) { for (mut actor, mut transform) in (&mut actors, &mut transforms).iter() { + if actor.disable { + actor.forces = Vec3::ZERO; + continue; + } + //apply forces let actor_forces = actor.forces; actor.velocity += (actor_forces + conf.gravity) * dt.0.as_secs_f32(); @@ -137,7 +146,7 @@ pub fn update_client_physics_late( //Apply velocity actor_position += actor.velocity * dt.0.as_secs_f32(); actor_position += actor.offset; - transform.0 = Mat4::from_scale_rotation_translation(scale, rotation, actor_position); + transform.0 = Mat4::from_scale_rotation_translation(scale, rotation.normalize(), actor_position); } // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); diff --git a/kubi/src/input.rs b/kubi/src/input.rs index 91f445a..c24081e 100644 --- a/kubi/src/input.rs +++ b/kubi/src/input.rs @@ -187,7 +187,7 @@ fn update_input_state ( inputs.look += raw_inputs.mouse_delta.as_vec2(); inputs.action_a |= raw_inputs.button_state[0]; inputs.action_b |= raw_inputs.button_state[1]; - inputs.jump |= raw_inputs.button_state[2]; + inputs.jump |= raw_inputs.keyboard_state.contains(KeyCode::Space as u32); } fn update_input_state_gamepad ( diff --git a/kubi/src/lib.rs b/kubi/src/lib.rs index 9fe125b..9fc7b42 100644 --- a/kubi/src/lib.rs +++ b/kubi/src/lib.rs @@ -57,7 +57,7 @@ use events::{ player_actions::generate_move_events, }; use input::{init_input, process_inputs}; -use player_controller::update_player_controllers; +use player_controller::{debug_switch_ctl_type, update_player_controllers}; use rendering::{ Renderer, RenderTarget, @@ -133,6 +133,7 @@ fn update() -> Workload { update_loaded_world_around_player, ).into_sequential_workload().run_if(is_ingame_or_loading), ( + debug_switch_ctl_type, update_player_controllers, update_client_physics_late, generate_move_events, diff --git a/kubi/src/player_controller.rs b/kubi/src/player_controller.rs index 402e4ce..ba2efa9 100644 --- a/kubi/src/player_controller.rs +++ b/kubi/src/player_controller.rs @@ -1,7 +1,8 @@ -use glam::{Vec3, Mat4, Quat, EulerRot, Vec2}; -use shipyard::{Component, View, ViewMut, IntoIter, UniqueView, Workload, IntoWorkload, track}; +use glam::{EulerRot, Mat4, Quat, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles}; +use shipyard::{track, Component, Get, IntoIter, IntoWithId, IntoWorkload, Unique, UniqueView, View, ViewMut, Workload}; +use winit::keyboard::KeyCode; use std::f32::consts::PI; -use crate::{transform::Transform, input::Inputs, settings::GameSettings, delta_time::DeltaTime}; +use crate::{client_physics::ClPhysicsActor, delta_time::DeltaTime, input::{Inputs, RawKbmInputState}, settings::GameSettings, transform::Transform}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PlayerControllerType { @@ -18,7 +19,7 @@ pub struct PlayerController { impl PlayerController { pub const DEFAULT_FLY_CAM: Self = Self { control_type: PlayerControllerType::FlyCam, - speed: 30., + speed: 50., }; pub const DEFAULT_FPS_CTL: Self = Self { @@ -59,16 +60,51 @@ fn update_look( fn update_movement( controllers: View, mut transforms: ViewMut, + mut actors: ViewMut, inputs: UniqueView, dt: UniqueView, ) { - if inputs.movement == Vec2::ZERO { return } - let movement = inputs.movement * dt.0.as_secs_f32(); - for (ctl, mut transform) in (&controllers, &mut transforms).iter() { + if (inputs.movement == Vec2::ZERO) && !inputs.jump { return } + let movement = inputs.movement.extend(inputs.jump as u32 as f32).xzy(); + for (id, (ctl, mut transform)) in (&controllers, &mut transforms).iter().with_id() { let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); let rotation_norm = rotation.normalize(); - translation += (rotation_norm * Vec3::NEG_Z).normalize() * movement.y * ctl.speed; - translation += (rotation_norm * Vec3::X).normalize() * movement.x * ctl.speed; - transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); + match ctl.control_type { + PlayerControllerType::FlyCam => { + translation += (rotation_norm * Vec3::NEG_Z).normalize() * movement.z * ctl.speed * dt.0.as_secs_f32(); + translation += (rotation_norm * Vec3::X).normalize() * movement.x * ctl.speed * dt.0.as_secs_f32(); + translation += Vec3::Y * movement.y * ctl.speed * dt.0.as_secs_f32(); + transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); + }, + PlayerControllerType::FpsCtl => { + let actor = (&mut actors).get(id).unwrap(); + + let euler = rotation_norm.to_euler(EulerRot::YZX); + let right = Vec2::from_angle(-euler.0).extend(0.).xzy(); + let forward = Vec2::from_angle(-(euler.0 + PI/2.)).extend(0.).xzy(); + + translation += forward * movement.z * ctl.speed * dt.0.as_secs_f32(); + translation += right * movement.x * ctl.speed * dt.0.as_secs_f32(); + translation += Vec3::Y * movement.y * ctl.speed * dt.0.as_secs_f32(); + + transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); + } + } + } +} + +pub fn debug_switch_ctl_type( + mut controllers: ViewMut, + mut actors: ViewMut, + kbm_state: UniqueView, +) { + for (mut controller, mut actor) in (&mut controllers, &mut actors).iter() { + if kbm_state.keyboard_state.contains(KeyCode::F4 as u32) { + *controller = PlayerController::DEFAULT_FPS_CTL; + actor.disable = false; + } else if kbm_state.keyboard_state.contains(KeyCode::F5 as u32) { + *controller = PlayerController::DEFAULT_FLY_CAM; + actor.disable = true; + } } } From 778c2b279e47b83a0b37a48c8b0abfc93818b986 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 15 Feb 2024 15:51:06 +0100 Subject: [PATCH 06/10] use force for plr --- kubi-shared/src/worldgen.rs | 676 +++++++++++++++++----------------- kubi/src/player_controller.rs | 18 +- 2 files changed, 354 insertions(+), 340 deletions(-) diff --git a/kubi-shared/src/worldgen.rs b/kubi-shared/src/worldgen.rs index 5d6cc59..7ddb678 100644 --- a/kubi-shared/src/worldgen.rs +++ b/kubi-shared/src/worldgen.rs @@ -1,334 +1,342 @@ -use bracket_noise::prelude::*; -use rand::prelude::*; -use glam::{IVec3, ivec3, Vec3Swizzles, IVec2}; -use rand_xoshiro::Xoshiro256StarStar; -use crate::{ - chunk::{BlockData, CHUNK_SIZE}, - block::Block, - queue::QueuedBlock, -}; - -fn mountain_ramp(mut x: f32) -> f32 { - x *= 2.0; - if x < 0.4 { - 0.5 * x - } else if x < 0.55 { - 4. * (x - 0.4) + 0.2 - } else { - 0.4444 * (x - 0.55) + 0.8 - } -} - -fn local_height(height: i32, chunk_position: IVec3) -> usize { - let offset = chunk_position * CHUNK_SIZE as i32; - (height - offset.y).clamp(0, CHUNK_SIZE as i32) as usize -} - -fn local_y_position(height: i32, chunk_position: IVec3) -> Option { - let offset = chunk_position * CHUNK_SIZE as i32; - let position = height - offset.y; - (0..CHUNK_SIZE as i32).contains(&position).then_some(position as usize) -} - - -pub fn generate_world(chunk_position: IVec3, seed: u64) -> (BlockData, Vec) { - let offset = chunk_position * CHUNK_SIZE as i32; - let mut blocks = Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]); - let mut queue = Vec::with_capacity(0); - - let mut smart_place = |blocks: &mut BlockData, position: IVec3, block: Block| { - if position.to_array().iter().any(|&x| !(0..CHUNK_SIZE).contains(&(x as usize))) { - let event_pos = offset + position; - queue.retain(|block: &QueuedBlock| { - block.position != event_pos - }); - queue.push(QueuedBlock { - position: event_pos, - block_type: block, - soft: true - }); - } else { - blocks[position.x as usize][position.y as usize][position.z as usize] = block; - } - }; - - let mut height_noise = FastNoise::seeded(seed); - height_noise.set_fractal_type(FractalType::FBM); - height_noise.set_fractal_octaves(4); - height_noise.set_frequency(0.003); - - let mut elevation_noise = FastNoise::seeded(seed.rotate_left(1)); - elevation_noise.set_fractal_type(FractalType::FBM); - elevation_noise.set_fractal_octaves(1); - elevation_noise.set_frequency(0.001); - - let mut cave_noise_a = FastNoise::seeded(seed.rotate_left(2)); - cave_noise_a.set_fractal_type(FractalType::FBM); - cave_noise_a.set_fractal_octaves(2); - cave_noise_a.set_frequency(0.01); - - let mut cave_noise_b = FastNoise::seeded(seed.rotate_left(3)); - cave_noise_b.set_fractal_type(FractalType::FBM); - cave_noise_b.set_fractal_octaves(3); - cave_noise_b.set_frequency(0.015); - - let mut cave_noise_holes = FastNoise::seeded(seed.rotate_left(4)); - cave_noise_holes.set_fractal_type(FractalType::FBM); - cave_noise_holes.set_fractal_octaves(2); - cave_noise_holes.set_frequency(0.005); - - let mut ravine_nose_line = FastNoise::seeded(seed.rotate_left(5)); - ravine_nose_line.set_fractal_type(FractalType::Billow); - ravine_nose_line.set_fractal_octaves(2); - ravine_nose_line.set_frequency(0.005); - - let mut ravine_noise_location = FastNoise::seeded(seed.rotate_left(6)); - ravine_noise_location.set_fractal_type(FractalType::FBM); - ravine_noise_location.set_fractal_octaves(1); - ravine_noise_location.set_frequency(0.005); - - let mut river_noise = FastNoise::seeded(seed.rotate_left(7)); - river_noise.set_fractal_type(FractalType::Billow); - river_noise.set_fractal_octaves(2); - river_noise.set_frequency(0.5 * 0.005); - - let mut rng = Xoshiro256StarStar::seed_from_u64( - seed - ^ (chunk_position.x as u32 as u64) - ^ ((chunk_position.z as u32 as u64) << 32) - ); - let rng_map_a: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); - let rng_map_b: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); - - //Generate height map - let mut within_heightmap = false; - let mut deco_heightmap = [[None; CHUNK_SIZE]; CHUNK_SIZE]; - - for x in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - let (noise_x, noise_y) = ((offset.x + x as i32) as f32, (offset.z + z as i32) as f32); - //sample noises (that are needed right now) - let raw_heightmap_value = height_noise.get_noise(noise_x, noise_y); - let raw_elevation_value = elevation_noise.get_noise(noise_x, noise_y); - let raw_ravine_location_value = ravine_noise_location.get_noise(noise_x, noise_y); - //compute height - let mut is_surface = true; - let mut river_fill_height = None; - let height = { - let local_elevation = raw_elevation_value.powi(4).sqrt(); - let mut height = (mountain_ramp(raw_heightmap_value) * local_elevation * 100.) as i32; - //Flatten valleys - if height < 0 { - height /= 2; - } - //Generate rivers - { - let river_width = (height as f32 / -5.).clamp(0.5, 1.); - let river_value = river_noise.get_noise(noise_x, noise_y); - if ((-0.00625 * river_width)..(0.00625 * river_width)).contains(&(river_value.powi(2))) { - is_surface = false; - river_fill_height = Some(height - 1); - //river_fill_height = Some(-3); - height -= (river_width * 15. * ((0.00625 * river_width) - river_value.powi(2)) * (1. / (0.00625 * river_width))).round() as i32; - } - } - //Generate ravines - if height < 0 && raw_ravine_location_value > 0.4 { - let raw_ravine_value = ravine_nose_line.get_noise(noise_x, noise_y); - if (-0.0125..0.0125).contains(&(raw_ravine_value.powi(2))) { - is_surface = false; - height -= (100. * (0.0125 - raw_ravine_value.powi(2)) * (1. / 0.0125)).round() as i32; - } - } - height - }; - //add to heightmap - if is_surface { - deco_heightmap[x][z] = Some(height); - //place dirt - for y in 0..local_height(height, chunk_position) { - blocks[x][y][z] = Block::Dirt; - within_heightmap = true; - } - //place stone - for y in 0..local_height(height - 5 - (raw_heightmap_value * 5.) as i32, chunk_position) { - blocks[x][y][z] = Block::Stone; - within_heightmap = true; - } - //place grass - if let Some(y) = local_y_position(height, chunk_position) { - blocks[x][y][z] = Block::Grass; - within_heightmap = true; - } - } else if let Some(river_fill_height) = river_fill_height { - //Place water - for y in 0..local_height(river_fill_height, chunk_position) { - blocks[x][y][z] = Block::Water; - within_heightmap = true; - } - //Place stone - for y in 0..local_height(height, chunk_position) { - blocks[x][y][z] = Block::Stone; - within_heightmap = true; - } - //Place dirt - if let Some(y) = local_y_position(height, chunk_position) { - blocks[x][y][z] = Block::Dirt; - within_heightmap = true; - } - } else { - //Place stone - for y in 0..local_height(height, chunk_position) { - blocks[x][y][z] = Block::Stone; - within_heightmap = true; - } - } - } - } - - //Carve out caves - if within_heightmap { - for z in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - for x in 0..CHUNK_SIZE { - if blocks[x][y][z] != Block::Stone { continue } - - let cave_size = ((offset.y + y as i32) as f32 / -100.).clamp(0., 1.); - let inv_cave_size = 1. - cave_size; - if cave_size < 0.1 { continue } - - let position = ivec3(x as i32, y as i32, z as i32) + offset; - - let is_cave = || { - let raw_cavemap_value_a = cave_noise_a.get_noise3d(position.x as f32, position.y as f32, position.z as f32); - let raw_cavemap_value_b = cave_noise_b.get_noise3d(position.x as f32, position.y as f32, position.z as f32); - ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_a) && - ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_b) - }; - let is_hole_cave = || { - let raw_cavemap_value_holes = cave_noise_holes.get_noise3d(position.x as f32, position.y as f32, position.z as f32); - ((0.9 + (0.1 * inv_cave_size))..=1.0).contains(&raw_cavemap_value_holes.abs()) - }; - - if is_cave() || is_hole_cave() { - blocks[x][y][z] = Block::Air; - if deco_heightmap[x][z] == Some(y as i32 + offset.y) { - deco_heightmap[x][z] = None - } - } - } - } - } - } - - //Add decorations - for x in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - //get height - let Some(height) = deco_heightmap[x][z] else { continue }; - //check for air - // if blocks[x][local_y][z] == Block::Air { - // continue - // } - //place tall grass - if rng_map_a[x][z] < 0.03 { - if let Some(y) = local_y_position(height + 1, chunk_position) { - blocks[x][y][z] = Block::TallGrass; - } - } - //place trees! - if rng_map_a[x][z] < 0.001 { - //Replace grass with dirt under the tree - if let Some(y) = local_y_position(height, chunk_position) { - blocks[x][y][z] = Block::Dirt; - } - - //Place wood (no smart_place needed here!) - let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; - for tree_y in 0..tree_height { - if let Some(y) = local_y_position(height + 1 + tree_y, chunk_position) { - blocks[x][y][z] = Block::Wood; - } - } - - let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; - - //Place leaf blocks - if let Some(y) = local_y_position(height + 1, chunk_position) { - let tree_pos = ivec3(x as i32, y as i32, z as i32); - // Place wood (smart_place) - // for tree_y in 0..tree_height { - // smart_place(&mut blocks, tree_pos + tree_y * IVec3::Y, Block::Wood); - // } - // Part that wraps around the tree - { - let tree_leaf_height = tree_height - 3; - let leaf_width = 2; - for tree_y in tree_leaf_height..tree_height { - for tree_x in (-leaf_width)..=leaf_width { - for tree_z in (-leaf_width)..=leaf_width { - let tree_offset = ivec3(tree_x, tree_y, tree_z); - if tree_offset.xz() == IVec2::ZERO { continue } - smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); - } - } - } - } - //part above the tree - { - let leaf_above_height = 2; - let leaf_width = 1; - for tree_y in tree_height..(tree_height + leaf_above_height) { - for tree_x in (-leaf_width)..=leaf_width { - for tree_z in (-leaf_width)..=leaf_width { - let tree_offset = ivec3(tree_x, tree_y, tree_z); - smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); - } - } - } - } - } - } - } - } - - (blocks, queue) - - // let mut cave_noise = FastNoise::seeded(seed); - // cave_noise.set_fractal_type(FractalType::FBM); - // cave_noise.set_frequency(0.1); - - // let mut dirt_noise = FastNoise::seeded(seed.rotate_left(1)); - // dirt_noise.set_fractal_type(FractalType::FBM); - // dirt_noise.set_frequency(0.1); - - // - - // if chunk_position.y >= 0 { - // if chunk_position.y == 0 { - // for x in 0..CHUNK_SIZE { - // for z in 0..CHUNK_SIZE { - // blocks[x][0][z] = Block::Dirt; - // blocks[x][1][z] = Block::Grass; - // } - // } - // } - // } else { - // for x in 0..CHUNK_SIZE { - // for y in 0..CHUNK_SIZE { - // for z in 0..CHUNK_SIZE { - // let position = ivec3(x as i32, y as i32, z as i32) + offset; - // let v_cave_noise = cave_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32 - 10.0).clamp(0., 1.); - // let v_dirt_noise = dirt_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32).clamp(0., 1.); - // if v_cave_noise > 0.5 { - // blocks[x][y][z] = Block::Stone; - // } else if v_dirt_noise > 0.5 { - // blocks[x][y][z] = Block::Dirt; - // } - // } - // } - // } - // } - // blocks - -} +use bracket_noise::prelude::*; +use rand::prelude::*; +use glam::{IVec3, ivec3, Vec3Swizzles, IVec2}; +use rand_xoshiro::Xoshiro256StarStar; +use crate::{ + chunk::{BlockData, CHUNK_SIZE}, + block::Block, + queue::QueuedBlock, +}; + +fn mountain_ramp(mut x: f32) -> f32 { + x *= 2.0; + if x < 0.4 { + 0.5 * x + } else if x < 0.55 { + 4. * (x - 0.4) + 0.2 + } else { + 0.4444 * (x - 0.55) + 0.8 + } +} + +fn local_height(height: i32, chunk_position: IVec3) -> usize { + let offset = chunk_position * CHUNK_SIZE as i32; + (height - offset.y).clamp(0, CHUNK_SIZE as i32) as usize +} + +fn local_y_position(height: i32, chunk_position: IVec3) -> Option { + let offset = chunk_position * CHUNK_SIZE as i32; + let position = height - offset.y; + (0..CHUNK_SIZE as i32).contains(&position).then_some(position as usize) +} + + +pub fn generate_world(chunk_position: IVec3, seed: u64) -> (BlockData, Vec) { + let offset = chunk_position * CHUNK_SIZE as i32; + let mut blocks = Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]); + let mut queue = Vec::with_capacity(0); + + let mut smart_place = |blocks: &mut BlockData, position: IVec3, block: Block| { + if position.to_array().iter().any(|&x| !(0..CHUNK_SIZE).contains(&(x as usize))) { + let event_pos = offset + position; + queue.retain(|block: &QueuedBlock| { + block.position != event_pos + }); + queue.push(QueuedBlock { + position: event_pos, + block_type: block, + soft: true + }); + } else { + blocks[position.x as usize][position.y as usize][position.z as usize] = block; + } + }; + + //STICK + if chunk_position.x == 0 && chunk_position.y == 5 { + for z in 0..CHUNK_SIZE { + blocks[0][0][z] = Block::Stone; + } + } + // + + let mut height_noise = FastNoise::seeded(seed); + height_noise.set_fractal_type(FractalType::FBM); + height_noise.set_fractal_octaves(4); + height_noise.set_frequency(0.003); + + let mut elevation_noise = FastNoise::seeded(seed.rotate_left(1)); + elevation_noise.set_fractal_type(FractalType::FBM); + elevation_noise.set_fractal_octaves(1); + elevation_noise.set_frequency(0.001); + + let mut cave_noise_a = FastNoise::seeded(seed.rotate_left(2)); + cave_noise_a.set_fractal_type(FractalType::FBM); + cave_noise_a.set_fractal_octaves(2); + cave_noise_a.set_frequency(0.01); + + let mut cave_noise_b = FastNoise::seeded(seed.rotate_left(3)); + cave_noise_b.set_fractal_type(FractalType::FBM); + cave_noise_b.set_fractal_octaves(3); + cave_noise_b.set_frequency(0.015); + + let mut cave_noise_holes = FastNoise::seeded(seed.rotate_left(4)); + cave_noise_holes.set_fractal_type(FractalType::FBM); + cave_noise_holes.set_fractal_octaves(2); + cave_noise_holes.set_frequency(0.005); + + let mut ravine_nose_line = FastNoise::seeded(seed.rotate_left(5)); + ravine_nose_line.set_fractal_type(FractalType::Billow); + ravine_nose_line.set_fractal_octaves(2); + ravine_nose_line.set_frequency(0.005); + + let mut ravine_noise_location = FastNoise::seeded(seed.rotate_left(6)); + ravine_noise_location.set_fractal_type(FractalType::FBM); + ravine_noise_location.set_fractal_octaves(1); + ravine_noise_location.set_frequency(0.005); + + let mut river_noise = FastNoise::seeded(seed.rotate_left(7)); + river_noise.set_fractal_type(FractalType::Billow); + river_noise.set_fractal_octaves(2); + river_noise.set_frequency(0.5 * 0.005); + + let mut rng = Xoshiro256StarStar::seed_from_u64( + seed + ^ (chunk_position.x as u32 as u64) + ^ ((chunk_position.z as u32 as u64) << 32) + ); + let rng_map_a: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + let rng_map_b: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + + //Generate height map + let mut within_heightmap = false; + let mut deco_heightmap = [[None; CHUNK_SIZE]; CHUNK_SIZE]; + + for x in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + let (noise_x, noise_y) = ((offset.x + x as i32) as f32, (offset.z + z as i32) as f32); + //sample noises (that are needed right now) + let raw_heightmap_value = height_noise.get_noise(noise_x, noise_y); + let raw_elevation_value = elevation_noise.get_noise(noise_x, noise_y); + let raw_ravine_location_value = ravine_noise_location.get_noise(noise_x, noise_y); + //compute height + let mut is_surface = true; + let mut river_fill_height = None; + let height = { + let local_elevation = raw_elevation_value.powi(4).sqrt(); + let mut height = (mountain_ramp(raw_heightmap_value) * local_elevation * 100.) as i32; + //Flatten valleys + if height < 0 { + height /= 2; + } + //Generate rivers + { + let river_width = (height as f32 / -5.).clamp(0.5, 1.); + let river_value = river_noise.get_noise(noise_x, noise_y); + if ((-0.00625 * river_width)..(0.00625 * river_width)).contains(&(river_value.powi(2))) { + is_surface = false; + river_fill_height = Some(height - 1); + //river_fill_height = Some(-3); + height -= (river_width * 15. * ((0.00625 * river_width) - river_value.powi(2)) * (1. / (0.00625 * river_width))).round() as i32; + } + } + //Generate ravines + if height < 0 && raw_ravine_location_value > 0.4 { + let raw_ravine_value = ravine_nose_line.get_noise(noise_x, noise_y); + if (-0.0125..0.0125).contains(&(raw_ravine_value.powi(2))) { + is_surface = false; + height -= (100. * (0.0125 - raw_ravine_value.powi(2)) * (1. / 0.0125)).round() as i32; + } + } + height + }; + //add to heightmap + if is_surface { + deco_heightmap[x][z] = Some(height); + //place dirt + for y in 0..local_height(height, chunk_position) { + blocks[x][y][z] = Block::Dirt; + within_heightmap = true; + } + //place stone + for y in 0..local_height(height - 5 - (raw_heightmap_value * 5.) as i32, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + //place grass + if let Some(y) = local_y_position(height, chunk_position) { + blocks[x][y][z] = Block::Grass; + within_heightmap = true; + } + } else if let Some(river_fill_height) = river_fill_height { + //Place water + for y in 0..local_height(river_fill_height, chunk_position) { + blocks[x][y][z] = Block::Water; + within_heightmap = true; + } + //Place stone + for y in 0..local_height(height, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + //Place dirt + if let Some(y) = local_y_position(height, chunk_position) { + blocks[x][y][z] = Block::Dirt; + within_heightmap = true; + } + } else { + //Place stone + for y in 0..local_height(height, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + } + } + } + + //Carve out caves + if within_heightmap { + for z in 0..CHUNK_SIZE { + for y in 0..CHUNK_SIZE { + for x in 0..CHUNK_SIZE { + if blocks[x][y][z] != Block::Stone { continue } + + let cave_size = ((offset.y + y as i32) as f32 / -100.).clamp(0., 1.); + let inv_cave_size = 1. - cave_size; + if cave_size < 0.1 { continue } + + let position = ivec3(x as i32, y as i32, z as i32) + offset; + + let is_cave = || { + let raw_cavemap_value_a = cave_noise_a.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + let raw_cavemap_value_b = cave_noise_b.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_a) && + ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_b) + }; + let is_hole_cave = || { + let raw_cavemap_value_holes = cave_noise_holes.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + ((0.9 + (0.1 * inv_cave_size))..=1.0).contains(&raw_cavemap_value_holes.abs()) + }; + + if is_cave() || is_hole_cave() { + blocks[x][y][z] = Block::Air; + if deco_heightmap[x][z] == Some(y as i32 + offset.y) { + deco_heightmap[x][z] = None + } + } + } + } + } + } + + //Add decorations + for x in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + //get height + let Some(height) = deco_heightmap[x][z] else { continue }; + //check for air + // if blocks[x][local_y][z] == Block::Air { + // continue + // } + //place tall grass + if rng_map_a[x][z] < 0.03 { + if let Some(y) = local_y_position(height + 1, chunk_position) { + blocks[x][y][z] = Block::TallGrass; + } + } + //place trees! + if rng_map_a[x][z] < 0.001 { + //Replace grass with dirt under the tree + if let Some(y) = local_y_position(height, chunk_position) { + blocks[x][y][z] = Block::Dirt; + } + + //Place wood (no smart_place needed here!) + let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; + for tree_y in 0..tree_height { + if let Some(y) = local_y_position(height + 1 + tree_y, chunk_position) { + blocks[x][y][z] = Block::Wood; + } + } + + let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; + + //Place leaf blocks + if let Some(y) = local_y_position(height + 1, chunk_position) { + let tree_pos = ivec3(x as i32, y as i32, z as i32); + // Place wood (smart_place) + // for tree_y in 0..tree_height { + // smart_place(&mut blocks, tree_pos + tree_y * IVec3::Y, Block::Wood); + // } + // Part that wraps around the tree + { + let tree_leaf_height = tree_height - 3; + let leaf_width = 2; + for tree_y in tree_leaf_height..tree_height { + for tree_x in (-leaf_width)..=leaf_width { + for tree_z in (-leaf_width)..=leaf_width { + let tree_offset = ivec3(tree_x, tree_y, tree_z); + if tree_offset.xz() == IVec2::ZERO { continue } + smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); + } + } + } + } + //part above the tree + { + let leaf_above_height = 2; + let leaf_width = 1; + for tree_y in tree_height..(tree_height + leaf_above_height) { + for tree_x in (-leaf_width)..=leaf_width { + for tree_z in (-leaf_width)..=leaf_width { + let tree_offset = ivec3(tree_x, tree_y, tree_z); + smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); + } + } + } + } + } + } + } + } + + (blocks, queue) + + // let mut cave_noise = FastNoise::seeded(seed); + // cave_noise.set_fractal_type(FractalType::FBM); + // cave_noise.set_frequency(0.1); + + // let mut dirt_noise = FastNoise::seeded(seed.rotate_left(1)); + // dirt_noise.set_fractal_type(FractalType::FBM); + // dirt_noise.set_frequency(0.1); + + // + + // if chunk_position.y >= 0 { + // if chunk_position.y == 0 { + // for x in 0..CHUNK_SIZE { + // for z in 0..CHUNK_SIZE { + // blocks[x][0][z] = Block::Dirt; + // blocks[x][1][z] = Block::Grass; + // } + // } + // } + // } else { + // for x in 0..CHUNK_SIZE { + // for y in 0..CHUNK_SIZE { + // for z in 0..CHUNK_SIZE { + // let position = ivec3(x as i32, y as i32, z as i32) + offset; + // let v_cave_noise = cave_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32 - 10.0).clamp(0., 1.); + // let v_dirt_noise = dirt_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32).clamp(0., 1.); + // if v_cave_noise > 0.5 { + // blocks[x][y][z] = Block::Stone; + // } else if v_dirt_noise > 0.5 { + // blocks[x][y][z] = Block::Dirt; + // } + // } + // } + // } + // } + // blocks + +} diff --git a/kubi/src/player_controller.rs b/kubi/src/player_controller.rs index ba2efa9..3bd1ab8 100644 --- a/kubi/src/player_controller.rs +++ b/kubi/src/player_controller.rs @@ -1,4 +1,4 @@ -use glam::{EulerRot, Mat4, Quat, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles}; +use glam::{vec3, EulerRot, Mat4, Quat, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles}; use shipyard::{track, Component, Get, IntoIter, IntoWithId, IntoWorkload, Unique, UniqueView, View, ViewMut, Workload}; use winit::keyboard::KeyCode; use std::f32::consts::PI; @@ -77,17 +77,23 @@ fn update_movement( transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); }, PlayerControllerType::FpsCtl => { - let actor = (&mut actors).get(id).unwrap(); + let mut actor = (&mut actors).get(id).unwrap(); let euler = rotation_norm.to_euler(EulerRot::YZX); let right = Vec2::from_angle(-euler.0).extend(0.).xzy(); let forward = Vec2::from_angle(-(euler.0 + PI/2.)).extend(0.).xzy(); - translation += forward * movement.z * ctl.speed * dt.0.as_secs_f32(); - translation += right * movement.x * ctl.speed * dt.0.as_secs_f32(); - translation += Vec3::Y * movement.y * ctl.speed * dt.0.as_secs_f32(); + actor.apply_force(ctl.speed * ( + (forward * movement.z) + + (right * movement.x) + + (Vec3::Y * movement.y) + )); - transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); + // translation += forward * movement.z * ctl.speed * dt.0.as_secs_f32(); + // translation += right * movement.x * ctl.speed * dt.0.as_secs_f32(); + // translation += Vec3::Y * movement.y * ctl.speed * dt.0.as_secs_f32(); + + // transform.0 = Mat4::from_scale_rotation_translation(scale, rotation_norm, translation); } } } From 6a96d6c3d3ef68d2b956b553d4909c7c23514ed4 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 15 Feb 2024 19:39:09 +0100 Subject: [PATCH 07/10] fun --- kubi/src/client_physics.rs | 16 ++++++++++++---- kubi/src/player_controller.rs | 12 ++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index f23ba00..f2d9416 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -31,7 +31,7 @@ pub struct ClPhysicsActor { pub velocity: Vec3, pub terminal_velocity: f32, //TODO: this should be configurable per block - pub friction_agains_ground: f32, + pub ground_friction: f32, pub gravity_scale: f32, flag_ground: bool, flag_collision: bool, @@ -56,7 +56,7 @@ impl Default for ClPhysicsActor { forces: Vec3::ZERO, velocity: Vec3::ZERO, terminal_velocity: 40., - friction_agains_ground: 0.5, + ground_friction: 10., gravity_scale: 1., flag_ground: false, flag_collision: false, @@ -114,7 +114,8 @@ pub fn update_client_physics_late( //get grid-aligned pos and blocks let actor_block_pos = actor_position.floor().as_ivec3(); let actor_block = world.get_block(actor_block_pos); - let actor_block_below = world.get_block(actor_block_pos + IVec3::NEG_Y); + let actor_block_pos_slightly_below = (actor_position + Vec3::NEG_Y * 0.01).floor().as_ivec3(); + let actor_block_below = world.get_block(actor_block_pos_slightly_below); //update flags actor.flag_collision = actor_block.is_solid(); @@ -139,7 +140,7 @@ pub fn update_client_physics_late( //HACK: for now, just stop the vertical velocity if on ground altogether, //as we don't have proper collision velocity resolution yet (we need to compute dot product or sth) if actor.flag_ground { - actor.velocity.y = 0.; + actor.velocity.y = actor.velocity.y.max(0.); } } @@ -147,6 +148,13 @@ pub fn update_client_physics_late( actor_position += actor.velocity * dt.0.as_secs_f32(); actor_position += actor.offset; transform.0 = Mat4::from_scale_rotation_translation(scale, rotation.normalize(), actor_position); + + //Apply friction + // if actor.flag_ground { + // let actor_velocity = actor.velocity; + // let actor_friction = actor.ground_friction; + // actor.velocity -= (actor_velocity * actor_friction * dt.0.as_secs_f32()) * vec3(1., 0., 1.); + // } } // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); diff --git a/kubi/src/player_controller.rs b/kubi/src/player_controller.rs index 3bd1ab8..aee4a49 100644 --- a/kubi/src/player_controller.rs +++ b/kubi/src/player_controller.rs @@ -2,7 +2,7 @@ use glam::{vec3, EulerRot, Mat4, Quat, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles}; use shipyard::{track, Component, Get, IntoIter, IntoWithId, IntoWorkload, Unique, UniqueView, View, ViewMut, Workload}; use winit::keyboard::KeyCode; use std::f32::consts::PI; -use crate::{client_physics::ClPhysicsActor, delta_time::DeltaTime, input::{Inputs, RawKbmInputState}, settings::GameSettings, transform::Transform}; +use crate::{client_physics::ClPhysicsActor, delta_time::DeltaTime, input::{Inputs, PrevInputs, RawKbmInputState}, settings::GameSettings, transform::Transform}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PlayerControllerType { @@ -62,10 +62,12 @@ fn update_movement( mut transforms: ViewMut, mut actors: ViewMut, inputs: UniqueView, + prev_inputs: UniqueView, dt: UniqueView, ) { - if (inputs.movement == Vec2::ZERO) && !inputs.jump { return } - let movement = inputs.movement.extend(inputs.jump as u32 as f32).xzy(); + let jump = inputs.jump && !prev_inputs.0.jump; + if (inputs.movement == Vec2::ZERO) && !jump { return } + let movement = inputs.movement.extend(jump as u32 as f32).xzy(); for (id, (ctl, mut transform)) in (&controllers, &mut transforms).iter().with_id() { let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); let rotation_norm = rotation.normalize(); @@ -78,6 +80,7 @@ fn update_movement( }, PlayerControllerType::FpsCtl => { let mut actor = (&mut actors).get(id).unwrap(); + let actor_on_ground = actor.on_ground(); let euler = rotation_norm.to_euler(EulerRot::YZX); let right = Vec2::from_angle(-euler.0).extend(0.).xzy(); @@ -86,7 +89,8 @@ fn update_movement( actor.apply_force(ctl.speed * ( (forward * movement.z) + (right * movement.x) + - (Vec3::Y * movement.y) + //TODO: remove hardcoded jump force + (Vec3::Y * movement.y * 125. * (actor_on_ground as u8 as f32)) )); // translation += forward * movement.z * ctl.speed * dt.0.as_secs_f32(); From f97b922943711520be8770ed3a78cab0bed7575d Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Fri, 16 Feb 2024 00:43:11 +0100 Subject: [PATCH 08/10] uwu --- kubi/src/client_physics.rs | 27 +++++++++++++++++---------- kubi/src/player_controller.rs | 22 ++++++++++++++++------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index f2d9416..0edaa52 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -23,15 +23,17 @@ impl Default for GlobalClPhysicsConfig { //TODO: actors should be represented by a vertical line, not a point. //XXX: maybe a capsule? (or configurable hull?) +//TODO: per block friction + #[derive(Component)] pub struct ClPhysicsActor { pub disable: bool, pub offset: Vec3, pub forces: Vec3, + pub constant_forces: Vec3, pub velocity: Vec3, pub terminal_velocity: f32, - //TODO: this should be configurable per block - pub ground_friction: f32, + pub decel: Vec3, pub gravity_scale: f32, flag_ground: bool, flag_collision: bool, @@ -42,6 +44,10 @@ impl ClPhysicsActor { self.forces += force; } + pub fn apply_constant_force(&mut self, force: Vec3) { + self.constant_forces += force; + } + pub fn on_ground(&self) -> bool { self.flag_ground } @@ -54,9 +60,11 @@ impl Default for ClPhysicsActor { disable: false, offset: vec3(0., 1.5, 0.), forces: Vec3::ZERO, + constant_forces: Vec3::ZERO, velocity: Vec3::ZERO, terminal_velocity: 40., - ground_friction: 10., + //constant deceleration, in ratio per second. e.g. value of 1 should stop the actor in 1 second. + decel: vec3(0., 0., 0.), gravity_scale: 1., flag_ground: false, flag_collision: false, @@ -145,16 +153,15 @@ pub fn update_client_physics_late( } //Apply velocity - actor_position += actor.velocity * dt.0.as_secs_f32(); + actor_position += (actor.velocity + actor.constant_forces) * dt.0.as_secs_f32(); + actor.constant_forces = Vec3::ZERO; actor_position += actor.offset; transform.0 = Mat4::from_scale_rotation_translation(scale, rotation.normalize(), actor_position); - //Apply friction - // if actor.flag_ground { - // let actor_velocity = actor.velocity; - // let actor_friction = actor.ground_friction; - // actor.velocity -= (actor_velocity * actor_friction * dt.0.as_secs_f32()) * vec3(1., 0., 1.); - // } + //Apply "friction" + // let actor_velocity = actor.velocity; + // let actor_decel = actor.decel; + // actor.velocity -= actor_velocity * actor_decel * dt.0.as_secs_f32(); } // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); diff --git a/kubi/src/player_controller.rs b/kubi/src/player_controller.rs index aee4a49..bd0fbc7 100644 --- a/kubi/src/player_controller.rs +++ b/kubi/src/player_controller.rs @@ -86,12 +86,22 @@ fn update_movement( let right = Vec2::from_angle(-euler.0).extend(0.).xzy(); let forward = Vec2::from_angle(-(euler.0 + PI/2.)).extend(0.).xzy(); - actor.apply_force(ctl.speed * ( - (forward * movement.z) + - (right * movement.x) + - //TODO: remove hardcoded jump force - (Vec3::Y * movement.y * 125. * (actor_on_ground as u8 as f32)) - )); + //TODO: remove hardcoded jump force + // actor.apply_constant_force(ctl.speed * ( + // (forward * movement.z) + + // (right * movement.x) + // )); + actor.apply_force( + ctl.speed * ( + (forward * movement.z) + + (right * movement.x) + ) + + Vec3::Y * movement.y * 1250. * (actor_on_ground as u8 as f32) + ); + + // actor.decel = + // (right * (1. - inputs.movement.x.abs()) * 10.) + + // (forward * (1. - inputs.movement.y.abs()) * 10.); // translation += forward * movement.z * ctl.speed * dt.0.as_secs_f32(); // translation += right * movement.x * ctl.speed * dt.0.as_secs_f32(); From f3a844bf553df98677dd2accf79ac0a0737e2967 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Fri, 16 Feb 2024 15:13:37 +0100 Subject: [PATCH 09/10] clamp? --- kubi/src/client_physics.rs | 43 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/kubi/src/client_physics.rs b/kubi/src/client_physics.rs index 0edaa52..ac79175 100644 --- a/kubi/src/client_physics.rs +++ b/kubi/src/client_physics.rs @@ -1,6 +1,6 @@ //TODO client-side physics //TODO move this to shared -use glam::{vec3, IVec3, Mat4, Vec3}; +use glam::{vec3, IVec3, Mat4, Vec3, Vec3Swizzles}; use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, View, ViewMut}; use kubi_shared::{block::{Block, CollisionType}, transform::Transform}; use crate::{delta_time::DeltaTime, world::ChunkStorage}; @@ -30,11 +30,12 @@ pub struct ClPhysicsActor { pub disable: bool, pub offset: Vec3, pub forces: Vec3, - pub constant_forces: Vec3, + pub frame_velocity: Vec3, pub velocity: Vec3, - pub terminal_velocity: f32, pub decel: Vec3, pub gravity_scale: f32, + pub max_velocity: (Option, Option, Option), + pub hack_xz_circular: bool, flag_ground: bool, flag_collision: bool, } @@ -44,8 +45,8 @@ impl ClPhysicsActor { self.forces += force; } - pub fn apply_constant_force(&mut self, force: Vec3) { - self.constant_forces += force; + pub fn add_frame_velocity(&mut self, force: Vec3) { + self.frame_velocity += force; } pub fn on_ground(&self) -> bool { @@ -60,12 +61,13 @@ impl Default for ClPhysicsActor { disable: false, offset: vec3(0., 1.5, 0.), forces: Vec3::ZERO, - constant_forces: Vec3::ZERO, + frame_velocity: Vec3::ZERO, velocity: Vec3::ZERO, - terminal_velocity: 40., //constant deceleration, in ratio per second. e.g. value of 1 should stop the actor in 1 second. - decel: vec3(0., 0., 0.), + decel: vec3(1., 0., 1.), gravity_scale: 1., + max_velocity: (Some(20.), None, Some(20.)), + hack_xz_circular: true, flag_ground: false, flag_collision: false, } @@ -152,16 +154,31 @@ pub fn update_client_physics_late( } } + //clamp velocity + let max_velocity = actor.max_velocity; + if actor.hack_xz_circular && actor.max_velocity.0.is_some() && (actor.max_velocity.0 == actor.max_velocity.2) { + actor.velocity.y = actor.velocity.y.clamp(-max_velocity.1.unwrap_or(f32::MAX), max_velocity.1.unwrap_or(f32::MAX)); + let clamped = actor.velocity.xz().clamp_length_max(actor.max_velocity.0.unwrap_or(f32::MAX)); + actor.velocity.x = clamped.x; + actor.velocity.z = clamped.y; + } else { + actor.velocity = vec3( + actor.velocity.x.clamp(-max_velocity.0.unwrap_or(f32::MAX), max_velocity.0.unwrap_or(f32::MAX)), + actor.velocity.y.clamp(-max_velocity.1.unwrap_or(f32::MAX), max_velocity.1.unwrap_or(f32::MAX)), + actor.velocity.z.clamp(-max_velocity.2.unwrap_or(f32::MAX), max_velocity.2.unwrap_or(f32::MAX)), + ); + } + //Apply velocity - actor_position += (actor.velocity + actor.constant_forces) * dt.0.as_secs_f32(); - actor.constant_forces = Vec3::ZERO; + actor_position += (actor.velocity + actor.frame_velocity) * dt.0.as_secs_f32(); + actor.frame_velocity = Vec3::ZERO; actor_position += actor.offset; transform.0 = Mat4::from_scale_rotation_translation(scale, rotation.normalize(), actor_position); //Apply "friction" - // let actor_velocity = actor.velocity; - // let actor_decel = actor.decel; - // actor.velocity -= actor_velocity * actor_decel * dt.0.as_secs_f32(); + let actor_velocity = actor.velocity; + let actor_decel = actor.decel; + actor.velocity -= actor_velocity * actor_decel * dt.0.as_secs_f32(); } // for (_, mut transform) in (&controllers, &mut transforms).iter() { // let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); From 14e9cb32f7331a795ad36c17b1c90b2a924c253d Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Sat, 17 Feb 2024 14:45:30 +0100 Subject: [PATCH 10/10] pass velocity to server move evt --- kubi/src/events/player_actions.rs | 74 +++++------ kubi/src/networking/player.rs | 202 +++++++++++++++--------------- 2 files changed, 140 insertions(+), 136 deletions(-) diff --git a/kubi/src/events/player_actions.rs b/kubi/src/events/player_actions.rs index 564b6aa..6946962 100644 --- a/kubi/src/events/player_actions.rs +++ b/kubi/src/events/player_actions.rs @@ -1,35 +1,39 @@ -use shipyard::{Component, View, ViewMut, EntitiesViewMut, IntoIter, track}; -use glam::{IVec3, Quat, Vec3}; -use kubi_shared::block::Block; -use crate::{ - player::MainPlayer, - transform::Transform -}; -use super::EventComponent; - -#[derive(Component, Clone, Copy, Debug)] -pub enum PlayerActionEvent { - PositionChanged { - position: Vec3, - direction: Quat - }, - UpdatedBlock { - position: IVec3, - block: Block, - }, -} - -pub fn generate_move_events( - transforms: View, - player: View, - mut entities: EntitiesViewMut, - mut events: ViewMut, - mut actions: ViewMut, -) { - let Some((_, transform)) = (&player, transforms.inserted_or_modified()).iter().next() else { return }; - let (_, direction, position) = transform.0.to_scale_rotation_translation(); - entities.add_entity( - (&mut events, &mut actions), - (EventComponent, PlayerActionEvent::PositionChanged { position, direction }) - ); -} +use shipyard::{Component, View, ViewMut, EntitiesViewMut, IntoIter, track}; +use glam::{IVec3, Quat, Vec3}; +use kubi_shared::block::Block; +use crate::{ + client_physics::ClPhysicsActor, player::MainPlayer, transform::Transform +}; +use super::EventComponent; + +#[derive(Component, Clone, Copy, Debug)] +pub enum PlayerActionEvent { + PositionChanged { + position: Vec3, + //XXX: should this even be here? + velocity: Vec3, + direction: Quat + }, + UpdatedBlock { + position: IVec3, + block: Block, + }, +} + +pub fn generate_move_events( + transforms: View, + player: View, + actors: View, + mut entities: EntitiesViewMut, + mut events: ViewMut, + mut actions: ViewMut, +) { + let Some((_, transform, actor)) = (&player, transforms.inserted_or_modified(), &actors).iter().next() else { return }; + let (_, direction, position) = transform.0.to_scale_rotation_translation(); + //HACK: if the actor is disabled, the velocity is irrelevant, so we just set it to zero. + let velocity = if actor.disable { Vec3::ZERO } else { actor.velocity }; + entities.add_entity( + (&mut events, &mut actions), + (EventComponent, PlayerActionEvent::PositionChanged { position, velocity, direction }) + ); +} diff --git a/kubi/src/networking/player.rs b/kubi/src/networking/player.rs index 8b0b470..a70f254 100644 --- a/kubi/src/networking/player.rs +++ b/kubi/src/networking/player.rs @@ -1,101 +1,101 @@ -use glam::{Vec3, Mat4}; -use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get}; -use uflow::{SendMode, client::Event as ClientEvent}; -use kubi_shared::{ - transform::Transform, - networking::{ - messages::{ClientToServerMessage, ServerToClientMessage, ServerToClientMessageType}, - channels::Channel, - client::ClientIdMap, - }, -}; -use crate::{ - events::player_actions::PlayerActionEvent, - player::spawn_remote_player_multiplayer, -}; -use super::{UdpClient, NetworkEvent}; - -pub fn init_client_map( - storages: AllStoragesView, -) { - storages.add_unique(ClientIdMap::new()); -} - -pub fn send_player_movement_events( - actions: View, - mut client: UniqueViewMut, -) { - for event in actions.iter() { - let PlayerActionEvent::PositionChanged { position, direction } = event else { - continue - }; - client.0.send( - postcard::to_allocvec(&ClientToServerMessage::PositionChanged { - position: *position, - velocity: Vec3::ZERO, - direction: *direction - }).unwrap().into_boxed_slice(), - Channel::Move as usize, - SendMode::TimeSensitive - ); - } -} - -pub fn receive_player_movement_events( - mut transforms: ViewMut, - network_events: View, - id_map: UniqueView -) { - for event in network_events.iter() { - let ClientEvent::Receive(data) = &event.0 else { - continue - }; - - if !event.is_message_of_type::<{ServerToClientMessageType::PlayerPositionChanged as u8}>() { - continue - } - - let Ok(parsed_message) = postcard::from_bytes(data) else { - log::error!("Malformed message"); - continue - }; - - let ServerToClientMessage::PlayerPositionChanged { - client_id, position, direction - } = parsed_message else { unreachable!() }; - - let Some(&ent_id) = id_map.0.get(&client_id) else { - log::error!("Not in client-id map"); - continue - }; - - let mut transform = (&mut transforms).get(ent_id) - .expect("invalid player entity id"); - - transform.0 = Mat4::from_rotation_translation(direction, position); - } -} - -pub fn receive_player_connect_events( - mut storages: AllStoragesViewMut, -) { - let messages: Vec = storages.borrow::>().unwrap().iter().filter_map(|event| { - let ClientEvent::Receive(data) = &event.0 else { - return None - }; - if !event.is_message_of_type::<{ServerToClientMessageType::PlayerConnected as u8}>() { - return None - }; - let Ok(parsed_message) = postcard::from_bytes(data) else { - log::error!("Malformed message"); - return None - }; - Some(parsed_message) - }).collect(); - - for message in messages { - let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() }; - log::info!("player connected: {} (id {})", init.username, init.client_id); - spawn_remote_player_multiplayer(&mut storages, init); - } -} +use glam::{Vec3, Mat4}; +use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get}; +use uflow::{SendMode, client::Event as ClientEvent}; +use kubi_shared::{ + transform::Transform, + networking::{ + messages::{ClientToServerMessage, ServerToClientMessage, ServerToClientMessageType}, + channels::Channel, + client::ClientIdMap, + }, +}; +use crate::{ + events::player_actions::PlayerActionEvent, + player::spawn_remote_player_multiplayer, +}; +use super::{UdpClient, NetworkEvent}; + +pub fn init_client_map( + storages: AllStoragesView, +) { + storages.add_unique(ClientIdMap::new()); +} + +pub fn send_player_movement_events( + actions: View, + mut client: UniqueViewMut, +) { + for event in actions.iter() { + let PlayerActionEvent::PositionChanged { position, velocity, direction } = event else { + continue + }; + client.0.send( + postcard::to_allocvec(&ClientToServerMessage::PositionChanged { + position: *position, + velocity: *velocity, + direction: *direction + }).unwrap().into_boxed_slice(), + Channel::Move as usize, + SendMode::TimeSensitive + ); + } +} + +pub fn receive_player_movement_events( + mut transforms: ViewMut, + network_events: View, + id_map: UniqueView +) { + for event in network_events.iter() { + let ClientEvent::Receive(data) = &event.0 else { + continue + }; + + if !event.is_message_of_type::<{ServerToClientMessageType::PlayerPositionChanged as u8}>() { + continue + } + + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + continue + }; + + let ServerToClientMessage::PlayerPositionChanged { + client_id, position, direction + } = parsed_message else { unreachable!() }; + + let Some(&ent_id) = id_map.0.get(&client_id) else { + log::error!("Not in client-id map"); + continue + }; + + let mut transform = (&mut transforms).get(ent_id) + .expect("invalid player entity id"); + + transform.0 = Mat4::from_rotation_translation(direction, position); + } +} + +pub fn receive_player_connect_events( + mut storages: AllStoragesViewMut, +) { + let messages: Vec = storages.borrow::>().unwrap().iter().filter_map(|event| { + let ClientEvent::Receive(data) = &event.0 else { + return None + }; + if !event.is_message_of_type::<{ServerToClientMessageType::PlayerConnected as u8}>() { + return None + }; + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + return None + }; + Some(parsed_message) + }).collect(); + + for message in messages { + let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() }; + log::info!("player connected: {} (id {})", init.username, init.client_id); + spawn_remote_player_multiplayer(&mut storages, init); + } +}