Merge branch 'fexii/freefly-camera' into 'master'

Add freefly camera mode and cycle camera mode keybind

See merge request veloren/veloren!1188
This commit is contained in:
Marcel 2020-07-11 22:45:02 +00:00
commit 930e0028bc
7 changed files with 152 additions and 43 deletions

View File

@ -1083,6 +1083,18 @@ impl Client {
.collect() .collect()
} }
/// Return true if this client is an admin on the server
pub fn is_admin(&self) -> bool {
let client_uid = self
.state
.read_component_cloned::<Uid>(self.entity)
.expect("Client doesn't have a Uid!!!");
self.player_list
.get(&client_uid)
.map_or(false, |info| info.is_admin)
}
/// Clean client ECS state /// Clean client ECS state
fn clean_state(&mut self) { fn clean_state(&mut self) {
let client_uid = self let client_uid = self

View File

@ -8,6 +8,7 @@ const FAR_PLANE: f32 = 100000.0;
const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const FIRST_PERSON_INTERP_TIME: f32 = 0.1;
const THIRD_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
const FREEFLY_INTERP_TIME: f32 = 0.0;
const LERP_ORI_RATE: f32 = 15.0; const LERP_ORI_RATE: f32 = 15.0;
pub const MIN_ZOOM: f32 = 0.1; pub const MIN_ZOOM: f32 = 0.1;
@ -16,6 +17,7 @@ pub const MIN_ZOOM: f32 = 0.1;
pub enum CameraMode { pub enum CameraMode {
FirstPerson = 0, FirstPerson = 0,
ThirdPerson = 1, ThirdPerson = 1,
Freefly = 2,
} }
impl Default for CameraMode { impl Default for CameraMode {
@ -73,15 +75,7 @@ impl Camera {
/// and position of the camera. /// and position of the camera.
pub fn compute_dependents(&mut self, terrain: &impl ReadVol) { pub fn compute_dependents(&mut self, terrain: &impl ReadVol) {
let dist = { let dist = {
let (start, end) = ( let (start, end) = (self.focus - self.forward() * self.dist, self.focus);
self.focus
+ (Vec3::new(
-f32::sin(self.ori.x) * f32::cos(self.ori.y),
-f32::cos(self.ori.x) * f32::cos(self.ori.y),
f32::sin(self.ori.y),
) * self.dist),
self.focus,
);
match terrain match terrain
.ray(start, end) .ray(start, end)
@ -155,13 +149,10 @@ impl Camera {
/// Zoom the camera by the given delta, limiting the input accordingly. /// Zoom the camera by the given delta, limiting the input accordingly.
pub fn zoom_by(&mut self, delta: f32) { pub fn zoom_by(&mut self, delta: f32) {
match self.mode { if self.mode == CameraMode::ThirdPerson {
CameraMode::ThirdPerson => { // Clamp camera dist to the 2 <= x <= infinity range
// Clamp camera dist to the 2 <= x <= infinity range self.tgt_dist = (self.tgt_dist + delta).max(2.0);
self.tgt_dist = (self.tgt_dist + delta).max(2.0); }
},
CameraMode::FirstPerson => {},
};
} }
/// Zoom with the ability to switch between first and third-person mode. /// Zoom with the ability to switch between first and third-person mode.
@ -181,6 +172,7 @@ impl Camera {
self.set_mode(CameraMode::ThirdPerson); self.set_mode(CameraMode::ThirdPerson);
self.tgt_dist = MIN_THIRD_PERSON; self.tgt_dist = MIN_THIRD_PERSON;
}, },
_ => {},
} }
} }
} }
@ -244,6 +236,7 @@ impl Camera {
match self.mode { match self.mode {
CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME, CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME,
CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME, CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME,
CameraMode::Freefly => FREEFLY_INTERP_TIME,
} }
} }
@ -287,6 +280,9 @@ impl Camera {
CameraMode::FirstPerson => { CameraMode::FirstPerson => {
self.set_distance(MIN_ZOOM); self.set_distance(MIN_ZOOM);
}, },
CameraMode::Freefly => {
self.zoom_by(0.0);
},
} }
} }
} }
@ -301,4 +297,45 @@ impl Camera {
mode => mode, mode => mode,
} }
} }
/// Cycle the camera to its next valid mode. If is_admin is false then only
/// modes which are accessible without admin access will be cycled to.
pub fn next_mode(&mut self, is_admin: bool) {
self.set_mode(match self.mode {
CameraMode::ThirdPerson => CameraMode::FirstPerson,
CameraMode::FirstPerson => {
if is_admin {
CameraMode::Freefly
} else {
CameraMode::ThirdPerson
}
},
CameraMode::Freefly => CameraMode::ThirdPerson,
});
}
/// Return a unit vector in the forward direction for the current camera
/// orientation
pub fn forward(&self) -> Vec3<f32> {
Vec3::new(
f32::sin(self.ori.x) * f32::cos(self.ori.y),
f32::cos(self.ori.x) * f32::cos(self.ori.y),
-f32::sin(self.ori.y),
)
}
/// Return a unit vector in the right direction for the current camera
/// orientation
pub fn right(&self) -> Vec3<f32> {
const UP: Vec3<f32> = Vec3::new(0.0, 0.0, 1.0);
self.forward().cross(UP).normalized()
}
/// Return a unit vector in the forward direction on the XY plane for
/// the current camera orientation
pub fn forward_xy(&self) -> Vec2<f32> { Vec2::new(f32::sin(self.ori.x), f32::cos(self.ori.x)) }
/// Return a unit vector in the right direction on the XY plane for
/// the current camera orientation
pub fn right_xy(&self) -> Vec2<f32> { Vec2::new(f32::cos(self.ori.x), -f32::sin(self.ori.x)) }
} }

View File

@ -120,37 +120,33 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
[ [
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => { CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_head_spec.mesh_head(&body, generate_mesh)) Some(humanoid_head_spec.mesh_head(&body, generate_mesh))
}, },
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => Some(humanoid_armor_chest_spec.mesh_chest( CameraMode::ThirdPerson | CameraMode::Freefly => Some(
&body, humanoid_armor_chest_spec.mesh_chest(&body, loadout, generate_mesh),
loadout, ),
generate_mesh,
)),
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => { CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh)) Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh))
}, },
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => { CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh)) Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh))
}, },
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => Some(humanoid_armor_pants_spec.mesh_pants( CameraMode::ThirdPerson | CameraMode::Freefly => Some(
&body, humanoid_armor_pants_spec.mesh_pants(&body, loadout, generate_mesh),
loadout, ),
generate_mesh,
)),
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)), Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)),
@ -158,7 +154,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
Some(humanoid_armor_foot_spec.mesh_left_foot(&body, loadout, generate_mesh)), Some(humanoid_armor_foot_spec.mesh_left_foot(&body, loadout, generate_mesh)),
Some(humanoid_armor_foot_spec.mesh_right_foot(&body, loadout, generate_mesh)), Some(humanoid_armor_foot_spec.mesh_right_foot(&body, loadout, generate_mesh)),
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => { CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_shoulder_spec.mesh_left_shoulder( Some(humanoid_armor_shoulder_spec.mesh_left_shoulder(
&body, &body,
loadout, loadout,
@ -168,7 +164,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
CameraMode::FirstPerson => None, CameraMode::FirstPerson => None,
}, },
match camera_mode { match camera_mode {
CameraMode::ThirdPerson => { CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_shoulder_spec.mesh_right_shoulder( Some(humanoid_armor_shoulder_spec.mesh_right_shoulder(
&body, &body,
loadout, loadout,

View File

@ -239,10 +239,17 @@ impl Scene {
}, },
CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1, CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1,
CameraMode::ThirdPerson => player_scale * 1.65, CameraMode::ThirdPerson => player_scale * 1.65,
CameraMode::Freefly => 0.0,
}; };
self.camera match self.camera.get_mode() {
.set_focus_pos(player_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6)); CameraMode::FirstPerson | CameraMode::ThirdPerson => {
self.camera.set_focus_pos(
player_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6),
);
},
CameraMode::Freefly => {},
};
// Tick camera for interpolation. // Tick camera for interpolation.
self.camera.update( self.camera.update(

View File

@ -173,7 +173,9 @@ impl PlayState for SessionState {
) )
.unwrap(); .unwrap();
let mut ori = self.scene.camera().get_orientation(); let mut walk_forward_dir = self.scene.camera().forward_xy();
let mut walk_right_dir = self.scene.camera().right_xy();
let mut freefly_vel = Vec3::zero();
let mut free_look = false; let mut free_look = false;
let mut auto_walk = false; let mut auto_walk = false;
@ -517,6 +519,14 @@ impl PlayState for SessionState {
_ => {}, _ => {},
} }
}, },
Event::InputUpdate(GameInput::CycleCamera, true) => {
// Prevent accessing camera modes which aren't available in multiplayer
// unless you are an admin. This is an easily bypassed clientside check.
// The server should do its own filtering of which entities are sent to
// clients to prevent abuse.
let camera = self.scene.camera_mut();
camera.next_mode(self.client.borrow().is_admin());
},
Event::AnalogGameInput(input) => match input { Event::AnalogGameInput(input) => match input {
AnalogGameInput::MovementX(v) => { AnalogGameInput::MovementX(v) => {
self.key_state.analog_matrix.x = v; self.key_state.analog_matrix.x = v;
@ -543,17 +553,60 @@ impl PlayState for SessionState {
} }
if !free_look { if !free_look {
ori = self.scene.camera().get_orientation(); walk_forward_dir = self.scene.camera().forward_xy();
walk_right_dir = self.scene.camera().right_xy();
self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap(); self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
} }
// Calculate the movement input vector of the player from the current key
// presses and the camera direction. // Get the current state of movement related inputs
let unit_vecs = ( let input_vec = self.key_state.dir_vec();
Vec2::new(ori[0].cos(), -ori[0].sin()), let (axis_right, axis_up) = (input_vec[0], input_vec[1]);
Vec2::new(ori[0].sin(), ori[0].cos()),
); match self.scene.camera().get_mode() {
let dir_vec = self.key_state.dir_vec(); camera::CameraMode::FirstPerson | camera::CameraMode::ThirdPerson => {
self.inputs.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1]; // Move the player character based on their walking direction.
// This could be different from the camera direction if free look is enabled.
self.inputs.move_dir = walk_right_dir * axis_right + walk_forward_dir * axis_up;
freefly_vel = Vec3::zero();
},
camera::CameraMode::Freefly => {
// Move the camera freely in 3d space. Apply acceleration so that
// the movement feels more natural and controlled.
const FREEFLY_ACCEL: f32 = 120.0;
const FREEFLY_DAMPING: f32 = 80.0;
const FREEFLY_MAX_SPEED: f32 = 50.0;
let forward = self.scene.camera().forward();
let right = self.scene.camera().right();
let dir = right * axis_right + forward * axis_up;
let dt = clock.get_last_delta().as_secs_f32();
if freefly_vel.magnitude_squared() > 0.01 {
let new_vel =
freefly_vel - freefly_vel.normalized() * (FREEFLY_DAMPING * dt);
if freefly_vel.dot(new_vel) > 0.0 {
freefly_vel = new_vel;
} else {
freefly_vel = Vec3::zero();
}
}
if dir.magnitude_squared() > 0.01 {
freefly_vel += dir * (FREEFLY_ACCEL * dt);
if freefly_vel.magnitude() > FREEFLY_MAX_SPEED {
freefly_vel = freefly_vel.normalized() * FREEFLY_MAX_SPEED;
}
}
let pos = self.scene.camera().get_focus_pos();
self.scene
.camera_mut()
.set_focus_pos(pos + freefly_vel * dt);
// Do not apply any movement to the player character
self.inputs.move_dir = Vec2::zero();
},
};
self.inputs.climb = self.key_state.climb(); self.inputs.climb = self.key_state.climb();

View File

@ -142,6 +142,7 @@ impl ControlSettings {
//GameInput::Charge => KeyMouse::Key(VirtualKeyCode::Key1), //GameInput::Charge => KeyMouse::Key(VirtualKeyCode::Key1),
GameInput::FreeLook => KeyMouse::Key(VirtualKeyCode::L), GameInput::FreeLook => KeyMouse::Key(VirtualKeyCode::L),
GameInput::AutoWalk => KeyMouse::Key(VirtualKeyCode::Period), GameInput::AutoWalk => KeyMouse::Key(VirtualKeyCode::Period),
GameInput::CycleCamera => KeyMouse::Key(VirtualKeyCode::Key0),
GameInput::Slot1 => KeyMouse::Key(VirtualKeyCode::Key1), GameInput::Slot1 => KeyMouse::Key(VirtualKeyCode::Key1),
GameInput::Slot2 => KeyMouse::Key(VirtualKeyCode::Key2), GameInput::Slot2 => KeyMouse::Key(VirtualKeyCode::Key2),
GameInput::Slot3 => KeyMouse::Key(VirtualKeyCode::Key3), GameInput::Slot3 => KeyMouse::Key(VirtualKeyCode::Key3),
@ -204,6 +205,7 @@ impl Default for ControlSettings {
//GameInput::Charge, //GameInput::Charge,
GameInput::FreeLook, GameInput::FreeLook,
GameInput::AutoWalk, GameInput::AutoWalk,
GameInput::CycleCamera,
GameInput::Slot1, GameInput::Slot1,
GameInput::Slot2, GameInput::Slot2,
GameInput::Slot3, GameInput::Slot3,

View File

@ -65,6 +65,7 @@ pub enum GameInput {
SwapLoadout, SwapLoadout,
FreeLook, FreeLook,
AutoWalk, AutoWalk,
CycleCamera,
} }
impl GameInput { impl GameInput {
@ -89,6 +90,7 @@ impl GameInput {
GameInput::Mount => "gameinput.mount", GameInput::Mount => "gameinput.mount",
GameInput::Enter => "gameinput.enter", GameInput::Enter => "gameinput.enter",
GameInput::Command => "gameinput.command", GameInput::Command => "gameinput.command",
GameInput::CycleCamera => "gameinput.cyclecamera",
GameInput::Escape => "gameinput.escape", GameInput::Escape => "gameinput.escape",
GameInput::Map => "gameinput.map", GameInput::Map => "gameinput.map",
GameInput::Bag => "gameinput.bag", GameInput::Bag => "gameinput.bag",