diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 19e620a01c..34df1eded3 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -2,7 +2,7 @@ use specs::{Component, FlaggedStorage, HashMapStorage}; //use specs_idvs::IDVStorage; use std::time::Duration; -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] pub enum MovementState { Stand, Run, @@ -22,7 +22,7 @@ impl MovementState { } } -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] pub enum ActionState { Idle, Wield { time_left: Duration }, @@ -57,7 +57,7 @@ impl ActionState { } } -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] pub struct CharacterState { pub movement: MovementState, pub action: ActionState, diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index 87bbcdb02d..cdc6471d5e 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -164,6 +164,8 @@ impl Scene { Body::Humanoid(body), Some(equipment), client.get_tick(), + CameraMode::default(), + None, ) .0; diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index 4293b33001..e922bd090f 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -7,16 +7,23 @@ use vek::*; const NEAR_PLANE: f32 = 0.01; const FAR_PLANE: f32 = 10000.0; -const INTERP_TIME: f32 = 0.1; +const FIRST_PERSON_INTERP_TIME: f32 = 0.05; +const THIRD_PERSON_INTERP_TIME: f32 = 0.1; pub const MIN_ZOOM: f32 = 0.1; // Possible TODO: Add more modes -#[derive(PartialEq, Clone, Copy)] +#[derive(PartialEq, Clone, Copy, Eq, Hash)] pub enum CameraMode { FirstPerson, ThirdPerson, } +impl Default for CameraMode { + fn default() -> Self { + Self::ThirdPerson + } +} + pub struct Camera { tgt_focus: Vec3, focus: Vec3, @@ -166,11 +173,26 @@ impl Camera { // This is horribly frame time dependent, but so is most of the game let delta = self.last_time.replace(time).map_or(0.0, |t| time - t); if (self.dist - self.tgt_dist).abs() > 0.01 { - self.dist = f32::lerp(self.dist, self.tgt_dist, (delta as f32) / INTERP_TIME); + self.dist = f32::lerp( + self.dist, + self.tgt_dist, + (delta as f32) / self.interp_time(), + ); } if (self.focus - self.tgt_focus).magnitude() > 0.01 { - self.focus = Vec3::lerp(self.focus, self.tgt_focus, (delta as f32) / INTERP_TIME); + self.focus = Vec3::lerp( + self.focus, + self.tgt_focus, + (delta as f32) / self.interp_time(), + ); + } + } + + pub fn interp_time(&self) -> f32 { + match self.mode { + CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME, + CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME, } } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index b3d0cf24f4..967a218910 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -2,17 +2,18 @@ use super::load::*; use crate::{ anim::SkeletonAttr, render::{FigurePipeline, Mesh, Model, Renderer}, + scene::camera::CameraMode, }; use common::{ assets::watch::ReloadIndicator, - comp::{Body, Equipment}, + comp::{Body, CharacterState, Equipment}, }; use hashbrown::HashMap; #[derive(PartialEq, Eq, Hash, Clone)] enum FigureKey { Simple(Body), - Complex(Body, Option), + Complex(Body, Option, CameraMode, Option), } pub struct FigureModelCache { @@ -34,9 +35,16 @@ impl FigureModelCache { body: Body, equipment: Option<&Equipment>, tick: u64, + camera_mode: CameraMode, + character_state: Option<&CharacterState>, ) -> &(Model, SkeletonAttr) { let key = if equipment.is_some() { - FigureKey::Complex(body, equipment.cloned()) + FigureKey::Complex( + body, + equipment.cloned(), + camera_mode, + character_state.cloned(), + ) } else { FigureKey::Simple(body) }; @@ -54,27 +62,84 @@ impl FigureModelCache { HumHeadSpec::load_watched(&mut self.manifest_indicator); let bone_meshes = match body { Body::Humanoid(body) => [ - Some(humanoid_head_spec.mesh_head( - body.race, - body.body_type, - body.hair_color, - body.hair_style, - body.beard, - body.eye_color, - body.skin, - body.eyebrows, - body.accessory, - )), - Some(mesh_chest(body.chest)), - Some(mesh_belt(body.belt)), - Some(mesh_pants(body.pants)), - Some(mesh_left_hand(body.hand)), - Some(mesh_right_hand(body.hand)), - Some(mesh_left_foot(body.foot)), - Some(mesh_right_foot(body.foot)), - Some(mesh_main(equipment.and_then(|e| e.main.as_ref()))), - Some(mesh_left_shoulder(body.shoulder)), - Some(mesh_right_shoulder(body.shoulder)), + match camera_mode { + CameraMode::ThirdPerson => { + Some(humanoid_head_spec.mesh_head( + body.race, + body.body_type, + body.hair_color, + body.hair_style, + body.beard, + body.eye_color, + body.skin, + body.eyebrows, + body.accessory, + )) + } + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(mesh_chest(body.chest)), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(mesh_belt(body.belt)), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(mesh_pants(body.pants)), + CameraMode::FirstPerson => None, + }, + if camera_mode == CameraMode::FirstPerson + && character_state + .map(|cs| cs.movement.is_roll()) + .unwrap_or_default() + { + None + } else { + Some(mesh_left_hand(body.hand)) + }, + if character_state + .map(|cs| cs.movement.is_roll()) + .unwrap_or_default() + { + None + } else { + Some(mesh_right_hand(body.hand)) + }, + match camera_mode { + CameraMode::ThirdPerson => Some(mesh_left_foot(body.foot)), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(mesh_right_foot(body.foot)), + CameraMode::FirstPerson => None, + }, + if camera_mode != CameraMode::FirstPerson + || character_state + .map(|cs| { + cs.action.is_attack() + || cs.action.is_block() + || cs.action.is_wield() + }) + .unwrap_or_default() + { + Some(mesh_main(equipment.and_then(|e| e.main.as_ref()))) + } else { + None + }, + match camera_mode { + CameraMode::ThirdPerson => { + Some(mesh_left_shoulder(body.shoulder)) + } + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => { + Some(mesh_right_shoulder(body.shoulder)) + } + CameraMode::FirstPerson => None, + }, Some(mesh_draw()), None, None, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index d8b11c4b39..e6bc82d680 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -116,7 +116,14 @@ impl FigureMgr { let skeleton_attr = &self .model_cache - .get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick) + .get_or_create_model( + renderer, + *body, + stats.map(|s| &s.equipment), + tick, + CameraMode::default(), + None, + ) .1; match body { @@ -341,6 +348,11 @@ impl FigureMgr { let frustum = camera.frustum(client); + let character_state_storage = client + .state() + .read_storage::(); + let character_state = character_state_storage.get(client.entity()); + for (entity, _, _, _, body, stats, _) in ( &ecs.entities(), &ecs.read_storage::(), @@ -381,23 +393,26 @@ impl FigureMgr { .get(&entity) .map(|state| (state.locals(), state.bone_consts())), } { + let is_player = entity == client.entity(); + + let player_camera_mode = if is_player { + camera.get_mode() + } else { + CameraMode::default() + }; + let model = &self .model_cache - .get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick) + .get_or_create_model( + renderer, + *body, + stats.map(|s| &s.equipment), + tick, + player_camera_mode, + if is_player { character_state } else { None }, + ) .0; - // Don't render the player's body while in first person mode - if camera.get_mode() == CameraMode::FirstPerson - && client - .state() - .read_storage::() - .get(client.entity()) - .is_some() - && entity == client.entity() - { - continue; - } - renderer.render_figure(model, globals, locals, bone_consts, lights); } else { debug!("Body has no saved figure"); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 424345d204..d402af31b7 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -134,14 +134,28 @@ impl Scene { .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); + let player_rolling = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(false, |cs| cs.movement.is_roll()); + // Alter camera position to match player. let tilt = self.camera.get_orientation().y; let dist = self.camera.get_distance(); - let up = if self.camera.get_mode() == CameraMode::FirstPerson { - 1.5 - } else { - 1.2 + + let up = match self.camera.get_mode() { + CameraMode::FirstPerson => { + if player_rolling { + 0.75 + } else { + 1.5 + } + } + CameraMode::ThirdPerson => 1.2, }; + self.camera.set_focus_pos( player_pos + Vec3::unit_z() * (up + dist * 0.15 - tilt.min(0.0) * dist * 0.75), );